123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- /*
- 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;
|