123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const { ConcatSource } = require("webpack-sources");
- const { RuntimeGlobals } = require("..");
- const HotUpdateChunk = require("../HotUpdateChunk");
- const Template = require("../Template");
- const { getAllChunks } = require("../javascript/ChunkHelpers");
- const {
- chunkHasJs,
- getCompilationHooks,
- getChunkFilenameTemplate
- } = require("../javascript/JavascriptModulesPlugin");
- const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
- const { getUndoPath } = require("../util/identifier");
- /** @typedef {import("../Chunk")} Chunk */
- /** @typedef {import("../Compiler")} Compiler */
- /** @typedef {import("../Entrypoint")} Entrypoint */
- class ModuleChunkFormatPlugin {
- /**
- * Apply the plugin
- * @param {Compiler} compiler the compiler instance
- * @returns {void}
- */
- apply(compiler) {
- compiler.hooks.thisCompilation.tap(
- "ModuleChunkFormatPlugin",
- compilation => {
- compilation.hooks.additionalChunkRuntimeRequirements.tap(
- "ModuleChunkFormatPlugin",
- (chunk, set) => {
- if (chunk.hasRuntime()) return;
- if (compilation.chunkGraph.getNumberOfEntryModules(chunk) > 0) {
- set.add(RuntimeGlobals.require);
- set.add(RuntimeGlobals.startupEntrypoint);
- set.add(RuntimeGlobals.externalInstallChunk);
- }
- }
- );
- const hooks = getCompilationHooks(compilation);
- hooks.renderChunk.tap(
- "ModuleChunkFormatPlugin",
- (modules, renderContext) => {
- const { chunk, chunkGraph, runtimeTemplate } = renderContext;
- const hotUpdateChunk =
- chunk instanceof HotUpdateChunk ? chunk : null;
- const source = new ConcatSource();
- if (hotUpdateChunk) {
- throw new Error(
- "HMR is not implemented for module chunk format yet"
- );
- } else {
- source.add(`export const id = ${JSON.stringify(chunk.id)};\n`);
- source.add(`export const ids = ${JSON.stringify(chunk.ids)};\n`);
- source.add(`export const modules = `);
- source.add(modules);
- source.add(`;\n`);
- const runtimeModules =
- chunkGraph.getChunkRuntimeModulesInOrder(chunk);
- if (runtimeModules.length > 0) {
- source.add("export const runtime =\n");
- source.add(
- Template.renderChunkRuntimeModules(
- runtimeModules,
- renderContext
- )
- );
- }
- const entries = Array.from(
- chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
- );
- if (entries.length > 0) {
- const runtimeChunk =
- /** @type {Entrypoint[][]} */
- (entries)[0][1].getRuntimeChunk();
- const currentOutputName = compilation
- .getPath(
- getChunkFilenameTemplate(chunk, compilation.outputOptions),
- {
- chunk,
- contentHashType: "javascript"
- }
- )
- .replace(/^\/+/g, "")
- .split("/");
- /**
- * @param {Chunk} chunk the chunk
- * @returns {string} the relative path
- */
- const getRelativePath = chunk => {
- const baseOutputName = currentOutputName.slice();
- const chunkOutputName = compilation
- .getPath(
- getChunkFilenameTemplate(
- chunk,
- compilation.outputOptions
- ),
- {
- chunk: 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
- );
- };
- const entrySource = new ConcatSource();
- entrySource.add(source);
- entrySource.add(";\n\n// load runtime\n");
- entrySource.add(
- `import ${RuntimeGlobals.require} from ${JSON.stringify(
- getRelativePath(/** @type {Chunk} */ (runtimeChunk))
- )};\n`
- );
- const startupSource = new ConcatSource();
- startupSource.add(
- `var __webpack_exec__ = ${runtimeTemplate.returningFunction(
- `${RuntimeGlobals.require}(${RuntimeGlobals.entryModuleId} = moduleId)`,
- "moduleId"
- )}\n`
- );
- const loadedChunks = new Set();
- let index = 0;
- for (let i = 0; i < entries.length; i++) {
- const [module, entrypoint] = entries[i];
- const final = i + 1 === entries.length;
- const moduleId = chunkGraph.getModuleId(module);
- const chunks = getAllChunks(
- /** @type {Entrypoint} */ (entrypoint),
- /** @type {Chunk} */ (runtimeChunk),
- undefined
- );
- for (const chunk of chunks) {
- if (
- loadedChunks.has(chunk) ||
- !chunkHasJs(chunk, chunkGraph)
- )
- continue;
- loadedChunks.add(chunk);
- startupSource.add(
- `import * as __webpack_chunk_${index}__ from ${JSON.stringify(
- getRelativePath(chunk)
- )};\n`
- );
- startupSource.add(
- `${RuntimeGlobals.externalInstallChunk}(__webpack_chunk_${index}__);\n`
- );
- index++;
- }
- 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(
- "ModuleChunkFormatPlugin",
- (chunk, hash, { chunkGraph, runtimeTemplate }) => {
- if (chunk.hasRuntime()) return;
- hash.update("ModuleChunkFormatPlugin");
- hash.update("1");
- const entries = Array.from(
- chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
- );
- updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
- }
- );
- }
- );
- }
- }
- module.exports = ModuleChunkFormatPlugin;
|