| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- 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 { JAVASCRIPT_TYPE } = require("../ModuleSourceTypeConstants");
- const Template = require("../Template");
- const {
- createChunkHashHandler,
- getChunkInfo
- } = require("../javascript/ChunkFormatHelpers");
- const { getAllChunks } = require("../javascript/ChunkHelpers");
- const {
- chunkHasJs,
- getChunkFilenameTemplate,
- getCompilationHooks
- } = require("../javascript/JavascriptModulesPlugin");
- const { entryModuleIdExpression } = require("../javascript/StartupHelpers");
- const { getUndoPath } = require("../util/identifier");
- /** @import { Source } from "webpack-sources" */
- /** @import Chunk from "../Chunk" */
- /** @import ChunkGraph from "../ChunkGraph" */
- /** @import Compilation from "../Compilation" */
- /** @import Compiler from "../Compiler" */
- /** @import Entrypoint from "../Entrypoint" */
- /**
- * @import {
- * ChunkRenderContext
- * } from "../javascript/JavascriptModulesPlugin"
- */
- /**
- * Gets relative path.
- * @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;
- };
- /**
- * Renders chunk import.
- * @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`;
- }
- /**
- * Gets chunk named import.
- * @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 {
- /**
- * Applies the plugin by registering its hooks on the compiler.
- * @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);
- /**
- * With dependent chunks.
- * @param {Iterable<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 (
- chunkGraph.getNumberOfEntryModules(chunk) > 0 &&
- chunk.hasRuntime()
- ) {
- const entryDependentChunks =
- chunkGraph.getChunkEntryDependentChunksIterable(chunk);
- const sourceWithDependentChunks = withDependentChunks(
- 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;
- }
- return modules;
- }
- );
- hooks.renderChunk.tap(PLUGIN_NAME, (modules, renderContext) => {
- const { chunk, chunkGraph, runtimeTemplate } = renderContext;
- const hotUpdateChunk = chunk instanceof HotUpdateChunk ? chunk : null;
- const source = new ConcatSource();
- const cst = runtimeTemplate.renderConst();
- source.add(
- `export ${cst} ${RuntimeGlobals.esmIds} = ${JSON.stringify(
- chunk.ids
- )};\n`
- );
- source.add(`export ${cst} ${RuntimeGlobals.esmModules} = `);
- source.add(modules);
- source.add(";\n");
- const runtimeModules = chunkGraph.getChunkRuntimeModulesInOrder(chunk);
- if (runtimeModules.length > 0) {
- source.add(`export ${cst} ${RuntimeGlobals.esmRuntime} =\n`);
- source.add(
- Template.renderChunkRuntimeModules(runtimeModules, renderContext)
- );
- }
- if (hotUpdateChunk) {
- return source;
- }
- const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
- if (runtimeChunk) {
- const { inlinedEntryModule, inlinedEntrySource } =
- /** @type {ChunkRenderContext} */ (renderContext);
- const entrySource = new ConcatSource();
- entrySource.add(source);
- entrySource.add(";\n\n// load runtime\n");
- entrySource.add(
- renderChunkImport(compilation, runtimeChunk, "", chunk)
- );
- const startupSource = new ConcatSource();
- // The `__webpack_exec__` helper is only needed for entries that
- // are executed through the registry, not for inlined entries.
- const needExec = entries.some(
- ([module]) =>
- module !== inlinedEntryModule &&
- chunkGraph.getModuleSourceTypes(module).has(JAVASCRIPT_TYPE)
- );
- if (needExec) {
- startupSource.add(
- `var __webpack_exec__ = ${runtimeTemplate.returningFunction(
- `${RuntimeGlobals.require}(${entryModuleIdExpression(
- chunkGraph,
- chunk,
- "moduleId"
- )})`,
- "moduleId"
- )}\n`
- );
- }
- /** @type {Set<Chunk>} */
- const loadedChunks = new Set();
- let ownInstalled = false;
- for (let i = 0; i < entries.length; i++) {
- const [module, entrypoint] = entries[i];
- if (!chunkGraph.getModuleSourceTypes(module).has(JAVASCRIPT_TYPE)) {
- continue;
- }
- const final = i + 1 === entries.length;
- const moduleId = chunkGraph.getModuleId(module);
- const chunks = getAllChunks(
- /** @type {Entrypoint} */ (entrypoint),
- chunk,
- /** @type {Chunk} */ (runtimeChunk)
- );
- /** @type {Set<Chunk>} */
- const processChunks = new Set();
- for (const chunk of chunks) {
- if (loadedChunks.has(chunk)) {
- continue;
- }
- loadedChunks.add(chunk);
- processChunks.add(chunk);
- }
- const sourceWithDependentChunks = withDependentChunks(
- processChunks,
- chunkGraph,
- chunk
- );
- if (sourceWithDependentChunks) {
- startupSource.add("\n");
- startupSource.add(sourceWithDependentChunks);
- }
- if (!ownInstalled) {
- ownInstalled = true;
- // Own chunk data comes from the `export const` bindings above.
- startupSource.add(
- `${RuntimeGlobals.externalInstallChunk}({ ${
- RuntimeGlobals.esmIds
- }, ${RuntimeGlobals.esmModules}${
- runtimeModules.length > 0
- ? `, ${RuntimeGlobals.esmRuntime}`
- : ""
- } });\n`
- );
- }
- if (module === inlinedEntryModule && inlinedEntrySource) {
- // Inline the entry at the top level so its declarations stay
- // live ESM bindings (exports appended by `renderStartup`).
- startupSource.add(
- `${runtimeTemplate.renderLet()} ${
- RuntimeGlobals.exports
- } = {};\n`
- );
- startupSource.add(inlinedEntrySource);
- startupSource.add("\n");
- } else {
- startupSource.add(
- `${
- final ? `var ${RuntimeGlobals.exports} = ` : ""
- }__webpack_exec__(${JSON.stringify(moduleId)});\n`
- );
- }
- }
- entrySource.add(
- hooks.renderStartup.call(
- startupSource,
- entries[entries.length - 1][0],
- inlinedEntryModule
- ? { ...renderContext, inlined: true, inlinedInIIFE: false }
- : renderContext
- )
- );
- return entrySource;
- }
- return source;
- });
- hooks.chunkHash.tap(PLUGIN_NAME, createChunkHashHandler(PLUGIN_NAME));
- });
- }
- }
- module.exports = ModuleChunkFormatPlugin;
|