工作端小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

307 lines
9.3 KiB

/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { ConcatSource } = require("webpack-sources");
const { HotUpdateChunk, RuntimeGlobals } = require("..");
const Template = require("../Template");
const { getAllChunks } = require("../javascript/ChunkHelpers");
const {
chunkHasJs,
getChunkFilenameTemplate,
getCompilationHooks
} = require("../javascript/JavascriptModulesPlugin");
const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
const { getUndoPath } = require("../util/identifier");
/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../Chunk")} Chunk */
/** @typedef {import("../ChunkGraph")} ChunkGraph */
/** @typedef {import("../ChunkGroup")} ChunkGroup */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../Compiler")} Compiler */
/** @typedef {import("../Entrypoint")} Entrypoint */
/** @typedef {import("../Module")} Module */
/** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */
/**
* Gets information about a chunk including its entries and runtime chunk
* @param {Chunk} chunk The chunk to get information for
* @param {ChunkGraph} chunkGraph The chunk graph containing the chunk
* @returns {{entries: Array<[Module, Entrypoint | undefined]>, runtimeChunk: Chunk|null}} Object containing chunk entries and runtime chunk
*/
function getChunkInfo(chunk, chunkGraph) {
const entries = [
...chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
];
const runtimeChunk =
entries.length > 0
? /** @type {Entrypoint[][]} */
(entries)[0][1].getRuntimeChunk()
: null;
return {
entries,
runtimeChunk
};
}
/**
* @param {Compilation} compilation the compilation instance
* @param {Chunk} chunk the chunk
* @param {Chunk} runtimeChunk the runtime chunk
* @returns {string} the relative path
*/
const getRelativePath = (compilation, chunk, runtimeChunk) => {
const currentOutputName = compilation
.getPath(
getChunkFilenameTemplate(runtimeChunk, compilation.outputOptions),
{
chunk: runtimeChunk,
contentHashType: "javascript"
}
)
.replace(/^\/+/g, "")
.split("/");
const baseOutputName = [...currentOutputName];
const chunkOutputName = compilation
.getPath(getChunkFilenameTemplate(chunk, compilation.outputOptions), {
chunk,
contentHashType: "javascript"
})
.replace(/^\/+/g, "")
.split("/");
// remove common parts except filename
while (
baseOutputName.length > 1 &&
chunkOutputName.length > 1 &&
baseOutputName[0] === chunkOutputName[0]
) {
baseOutputName.shift();
chunkOutputName.shift();
}
const last = chunkOutputName.join("/");
// create final path
return getUndoPath(baseOutputName.join("/"), last, true) + last;
};
/**
* @param {Compilation} compilation the compilation instance
* @param {Chunk} chunk the chunk to render the import for
* @param {string=} namedImport the named import to use for the import
* @param {Chunk=} runtimeChunk the runtime chunk
* @returns {string} the import source
*/
function renderChunkImport(compilation, chunk, namedImport, runtimeChunk) {
return `import ${namedImport ? `* as ${namedImport}` : RuntimeGlobals.require} from ${JSON.stringify(
getRelativePath(compilation, chunk, runtimeChunk || chunk)
)};\n`;
}
/**
* @param {number} index the index of the chunk
* @returns {string} the named import to use for the import
*/
function getChunkNamedImport(index) {
return `__webpack_chunk_${index}__`;
}
const PLUGIN_NAME = "ModuleChunkFormatPlugin";
class ModuleChunkFormatPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, compilation => {
compilation.hooks.additionalChunkRuntimeRequirements.tap(
PLUGIN_NAME,
(chunk, set) => {
if (chunk.hasRuntime()) return;
if (compilation.chunkGraph.getNumberOfEntryModules(chunk) > 0) {
set.add(RuntimeGlobals.require);
set.add(RuntimeGlobals.externalInstallChunk);
}
}
);
const hooks = getCompilationHooks(compilation);
/**
* @param {Set<Chunk>} chunks the chunks to render
* @param {ChunkGraph} chunkGraph the chunk graph
* @param {Chunk=} runtimeChunk the runtime chunk
* @returns {Source|undefined} the source
*/
const withDependentChunks = (chunks, chunkGraph, runtimeChunk) => {
if (/** @type {Set<Chunk>} */ (chunks).size > 0) {
const source = new ConcatSource();
let index = 0;
for (const chunk of chunks) {
index++;
if (!chunkHasJs(chunk, chunkGraph)) {
continue;
}
const namedImport = getChunkNamedImport(index);
source.add(
renderChunkImport(
compilation,
chunk,
namedImport,
runtimeChunk || chunk
)
);
source.add(
`${RuntimeGlobals.externalInstallChunk}(${namedImport});\n`
);
}
return source;
}
};
hooks.renderStartup.tap(
PLUGIN_NAME,
(modules, _lastModule, renderContext) => {
const { chunk, chunkGraph } = renderContext;
if (!chunk.hasRuntime()) {
return modules;
}
const entryDependentChunks =
chunkGraph.getChunkEntryDependentChunksIterable(chunk);
const sourceWithDependentChunks = withDependentChunks(
/** @type {Set<Chunk>} */ (entryDependentChunks),
chunkGraph,
chunk
);
if (!sourceWithDependentChunks) {
return modules;
}
if (modules.size() === 0) {
return sourceWithDependentChunks;
}
const source = new ConcatSource();
source.add(sourceWithDependentChunks);
source.add("\n");
source.add(modules);
return source;
}
);
hooks.renderChunk.tap(PLUGIN_NAME, (modules, renderContext) => {
const { chunk, chunkGraph, runtimeTemplate } = renderContext;
const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
const source = new ConcatSource();
source.add(
`export const __webpack_id__ = ${JSON.stringify(chunk.id)};\n`
);
source.add(
`export const __webpack_ids__ = ${JSON.stringify(chunk.ids)};\n`
);
source.add("export const __webpack_modules__ = ");
source.add(modules);
source.add(";\n");
const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
if (runtimeModules.length > 0) {
source.add("export const __webpack_runtime__ =\n");
source.add(
Template.renderChunkRuntimeModules(runtimeModules, renderContext)
);
}
if (hotUpdateChunk) {
return source;
}
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
if (runtimeChunk) {
const entrySource = new ConcatSource();
entrySource.add(source);
entrySource.add(";\n\n// load runtime\n");
entrySource.add(
renderChunkImport(compilation, runtimeChunk, "", chunk)
);
const startupSource = new ConcatSource();
startupSource.add(
`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
`${RuntimeGlobals.require}(${RuntimeGlobals.entryModuleId} = moduleId)`,
"moduleId"
)}\n`
);
const loadedChunks = new Set();
for (let i = 0; i < entries.length; i++) {
const [module, entrypoint] = entries[i];
if (!chunkGraph.getModuleSourceTypes(module).has("javascript")) {
continue;
}
const final = i + 1 === entries.length;
const moduleId = chunkGraph.getModuleId(module);
const entryDependentChunks = /** @type {Set<Chunk>} */ (
chunkGraph.getChunkEntryDependentChunksIterable(chunk)
);
const chunks = getAllChunks(
/** @type {Entrypoint} */ (entrypoint),
/** @type {Chunk} */ (runtimeChunk),
undefined
);
const processChunks = new Set();
for (const _chunk of chunks) {
if (
loadedChunks.has(_chunk) ||
entryDependentChunks.has(_chunk)
) {
continue;
}
loadedChunks.add(_chunk);
processChunks.add(_chunk);
}
const sourceWithDependentChunks = withDependentChunks(
processChunks,
chunkGraph,
chunk
);
if (sourceWithDependentChunks) {
startupSource.add("\n");
startupSource.add(sourceWithDependentChunks);
}
startupSource.add(
`${
final ? `var ${RuntimeGlobals.exports} = ` : ""
}__webpack_exec__(${JSON.stringify(moduleId)});\n`
);
}
entrySource.add(
hooks.renderStartup.call(
startupSource,
entries[entries.length - 1][0],
{
...renderContext,
inlined: false
}
)
);
return entrySource;
}
return source;
});
hooks.chunkHash.tap(PLUGIN_NAME, (chunk, hash, { chunkGraph }) => {
if (chunk.hasRuntime()) return;
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
hash.update(PLUGIN_NAME);
hash.update("1");
if (runtimeChunk && runtimeChunk.hash) {
// Any change to runtimeChunk should trigger a hash update,
// we shouldn't depend on or inspect its internal implementation.
// import __webpack_require__ from "./runtime-main.e9400aee33633a3973bd.js";
hash.update(runtimeChunk.hash);
}
updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
});
});
}
}
module.exports = ModuleChunkFormatPlugin;