Skip to content

Commit 4f9fafc

Browse files
authored
Merge pull request #14829 from webpack/bugfix/split-chunks-esm
fix outputModule with initial splitChunks
2 parents 093eadf + c69e37c commit 4f9fafc

10 files changed

Lines changed: 207 additions & 120 deletions

File tree

lib/esm/ModuleChunkFormatPlugin.js

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,16 @@
55

66
"use strict";
77

8-
const { ConcatSource, RawSource } = require("webpack-sources");
8+
const { ConcatSource } = require("webpack-sources");
99
const { RuntimeGlobals } = require("..");
1010
const HotUpdateChunk = require("../HotUpdateChunk");
1111
const Template = require("../Template");
12+
const { getAllChunks } = require("../javascript/ChunkHelpers");
1213
const {
1314
getCompilationHooks,
1415
getChunkFilenameTemplate
1516
} = require("../javascript/JavascriptModulesPlugin");
16-
const {
17-
generateEntryStartup,
18-
updateHashForEntryStartup
19-
} = require("../javascript/StartupHelpers");
17+
const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
2018

2119
/** @typedef {import("../Compiler")} Compiler */
2220

@@ -84,63 +82,90 @@ class ModuleChunkFormatPlugin {
8482
}
8583
)
8684
.split("/");
87-
const runtimeOutputName = compilation
88-
.getPath(
89-
getChunkFilenameTemplate(
90-
runtimeChunk,
91-
compilation.outputOptions
92-
),
93-
{
94-
chunk: runtimeChunk,
95-
contentHashType: "javascript"
96-
}
97-
)
98-
.split("/");
9985

10086
// remove filename, we only need the directory
101-
const outputFilename = currentOutputName.pop();
87+
currentOutputName.pop();
10288

103-
// remove common parts
104-
while (
105-
currentOutputName.length > 0 &&
106-
runtimeOutputName.length > 0 &&
107-
currentOutputName[0] === runtimeOutputName[0]
108-
) {
109-
currentOutputName.shift();
110-
runtimeOutputName.shift();
111-
}
89+
const getRelativePath = chunk => {
90+
const baseOutputName = currentOutputName.slice();
91+
const chunkOutputName = compilation
92+
.getPath(
93+
getChunkFilenameTemplate(
94+
chunk,
95+
compilation.outputOptions
96+
),
97+
{
98+
chunk: chunk,
99+
contentHashType: "javascript"
100+
}
101+
)
102+
.split("/");
112103

113-
// create final path
114-
const runtimePath =
115-
(currentOutputName.length > 0
116-
? "../".repeat(currentOutputName.length)
117-
: "./") + runtimeOutputName.join("/");
104+
// remove common parts
105+
while (
106+
baseOutputName.length > 0 &&
107+
chunkOutputName.length > 0 &&
108+
baseOutputName[0] === chunkOutputName[0]
109+
) {
110+
baseOutputName.shift();
111+
chunkOutputName.shift();
112+
}
113+
// create final path
114+
return (
115+
(baseOutputName.length > 0
116+
? "../".repeat(baseOutputName.length)
117+
: "./") + chunkOutputName.join("/")
118+
);
119+
};
118120

119121
const entrySource = new ConcatSource();
120122
entrySource.add(source);
121123
entrySource.add(";\n\n// load runtime\n");
122124
entrySource.add(
123125
`import __webpack_require__ from ${JSON.stringify(
124-
runtimePath
125-
)};\n`
126-
);
127-
entrySource.add(
128-
`import * as __webpack_self_exports__ from ${JSON.stringify(
129-
"./" + outputFilename
126+
getRelativePath(runtimeChunk)
130127
)};\n`
131128
);
132-
entrySource.add(
133-
`${RuntimeGlobals.externalInstallChunk}(__webpack_self_exports__);\n`
134-
);
135-
const startupSource = new RawSource(
136-
generateEntryStartup(
137-
chunkGraph,
138-
runtimeTemplate,
139-
entries,
140-
chunk,
141-
false
142-
)
129+
130+
const startupSource = new ConcatSource();
131+
startupSource.add(
132+
`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
133+
`__webpack_require__(${RuntimeGlobals.entryModuleId} = moduleId)`,
134+
"moduleId"
135+
)}\n`
143136
);
137+
138+
const loadedChunks = new Set();
139+
let index = 0;
140+
for (let i = 0; i < entries.length; i++) {
141+
const [module, entrypoint] = entries[i];
142+
const final = i + 1 === entries.length;
143+
const moduleId = chunkGraph.getModuleId(module);
144+
const chunks = getAllChunks(
145+
entrypoint,
146+
runtimeChunk,
147+
undefined
148+
);
149+
for (const chunk of chunks) {
150+
if (loadedChunks.has(chunk)) continue;
151+
loadedChunks.add(chunk);
152+
startupSource.add(
153+
`import * as __webpack_chunk_${index}__ from ${JSON.stringify(
154+
getRelativePath(chunk)
155+
)};\n`
156+
);
157+
startupSource.add(
158+
`${RuntimeGlobals.externalInstallChunk}(__webpack_chunk_${index}__);\n`
159+
);
160+
index++;
161+
}
162+
startupSource.add(
163+
`${
164+
final ? "var __webpack_exports__ = " : ""
165+
}__webpack_exec__(${JSON.stringify(moduleId)});\n`
166+
);
167+
}
168+
144169
entrySource.add(
145170
hooks.renderStartup.call(
146171
startupSource,

lib/javascript/ChunkHelpers.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
6+
"use strict";
7+
8+
const Entrypoint = require("../Entrypoint");
9+
10+
/** @typedef {import("../Chunk")} Chunk */
11+
12+
/**
13+
* @param {Entrypoint} entrypoint a chunk group
14+
* @param {Chunk} excludedChunk1 current chunk which is excluded
15+
* @param {Chunk} excludedChunk2 runtime chunk which is excluded
16+
* @returns {Set<Chunk>} chunks
17+
*/
18+
const getAllChunks = (entrypoint, excludedChunk1, excludedChunk2) => {
19+
const queue = new Set([entrypoint]);
20+
const chunks = new Set();
21+
for (const entrypoint of queue) {
22+
for (const chunk of entrypoint.chunks) {
23+
if (chunk === excludedChunk1) continue;
24+
if (chunk === excludedChunk2) continue;
25+
chunks.add(chunk);
26+
}
27+
for (const parent of entrypoint.parentsIterable) {
28+
if (parent instanceof Entrypoint) queue.add(parent);
29+
}
30+
}
31+
return chunks;
32+
};
33+
exports.getAllChunks = getAllChunks;

lib/javascript/StartupHelpers.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
"use strict";
77

8-
const Entrypoint = require("../Entrypoint");
98
const RuntimeGlobals = require("../RuntimeGlobals");
109
const Template = require("../Template");
1110
const { isSubset } = require("../util/SetHelpers");
11+
const { getAllChunks } = require("./ChunkHelpers");
1212
const { chunkHasJs } = require("./JavascriptModulesPlugin");
1313

1414
/** @typedef {import("../util/Hash")} Hash */
@@ -19,30 +19,6 @@ const { chunkHasJs } = require("./JavascriptModulesPlugin");
1919
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
2020
/** @typedef {(string|number)[]} EntryItem */
2121

22-
// TODO move to this file to ../javascript/ChunkHelpers.js
23-
24-
/**
25-
* @param {Entrypoint} entrypoint a chunk group
26-
* @param {Chunk} excludedChunk1 current chunk which is excluded
27-
* @param {Chunk} excludedChunk2 runtime chunk which is excluded
28-
* @returns {Set<Chunk>} chunks
29-
*/
30-
const getAllChunks = (entrypoint, excludedChunk1, excludedChunk2) => {
31-
const queue = new Set([entrypoint]);
32-
const chunks = new Set();
33-
for (const entrypoint of queue) {
34-
for (const chunk of entrypoint.chunks) {
35-
if (chunk === excludedChunk1) continue;
36-
if (chunk === excludedChunk2) continue;
37-
chunks.add(chunk);
38-
}
39-
for (const parent of entrypoint.parentsIterable) {
40-
if (parent instanceof Entrypoint) queue.add(parent);
41-
}
42-
}
43-
return chunks;
44-
};
45-
4622
const EXPORT_PREFIX = "var __webpack_exports__ = ";
4723

4824
/**

test/ConfigTestCases.template.js

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ const describeCases = config => {
305305
if (testConfig.beforeExecute) testConfig.beforeExecute();
306306
const results = [];
307307
for (let i = 0; i < optionsArr.length; i++) {
308+
const options = optionsArr[i];
308309
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
309310
if (bundlePath) {
310311
filesCount++;
@@ -327,6 +328,43 @@ const describeCases = config => {
327328
const requireCache = Object.create(null);
328329
const esmCache = new Map();
329330
const esmIdentifier = `${category.name}-${testName}-${i}`;
331+
const baseModuleScope = {
332+
console: console,
333+
it: _it,
334+
beforeEach: _beforeEach,
335+
afterEach: _afterEach,
336+
expect,
337+
jest,
338+
__STATS__: jsonStats,
339+
nsObj: m => {
340+
Object.defineProperty(m, Symbol.toStringTag, {
341+
value: "Module"
342+
});
343+
return m;
344+
}
345+
};
346+
347+
let runInNewContext = false;
348+
if (
349+
options.target === "web" ||
350+
options.target === "webworker"
351+
) {
352+
baseModuleScope.window = globalContext;
353+
baseModuleScope.self = globalContext;
354+
baseModuleScope.URL = URL;
355+
baseModuleScope.Worker =
356+
require("./helpers/createFakeWorker")({
357+
outputDirectory
358+
});
359+
runInNewContext = true;
360+
}
361+
if (testConfig.moduleScope) {
362+
testConfig.moduleScope(baseModuleScope);
363+
}
364+
const esmContext = vm.createContext(baseModuleScope, {
365+
name: "context for esm"
366+
});
367+
330368
// eslint-disable-next-line no-loop-func
331369
const _require = (
332370
currentDirectory,
@@ -380,41 +418,7 @@ const describeCases = config => {
380418
options.experiments &&
381419
options.experiments.outputModule;
382420

383-
let runInNewContext = false;
384-
385-
const moduleScope = {
386-
console: console,
387-
it: _it,
388-
beforeEach: _beforeEach,
389-
afterEach: _afterEach,
390-
expect,
391-
jest,
392-
__STATS__: jsonStats,
393-
nsObj: m => {
394-
Object.defineProperty(m, Symbol.toStringTag, {
395-
value: "Module"
396-
});
397-
return m;
398-
}
399-
};
400-
401-
if (
402-
options.target === "web" ||
403-
options.target === "webworker"
404-
) {
405-
moduleScope.window = globalContext;
406-
moduleScope.self = globalContext;
407-
moduleScope.URL = URL;
408-
moduleScope.Worker =
409-
require("./helpers/createFakeWorker")({
410-
outputDirectory
411-
});
412-
runInNewContext = true;
413-
}
414421
if (isModule) {
415-
if (testConfig.moduleScope) {
416-
testConfig.moduleScope(moduleScope);
417-
}
418422
if (!vm.SourceTextModule)
419423
throw new Error(
420424
"Running this test requires '--experimental-vm-modules'.\nRun with 'node --experimental-vm-modules node_modules/jest-cli/bin/jest'."
@@ -424,11 +428,7 @@ const describeCases = config => {
424428
esm = new vm.SourceTextModule(content, {
425429
identifier: esmIdentifier + "-" + p,
426430
url: pathToFileURL(p).href + "?" + esmIdentifier,
427-
context:
428-
(parentModule && parentModule.context) ||
429-
vm.createContext(moduleScope, {
430-
name: `context for ${p}`
431-
}),
431+
context: esmContext,
432432
initializeImportMeta: (meta, module) => {
433433
meta.url = pathToFileURL(p).href;
434434
},
@@ -488,7 +488,8 @@ const describeCases = config => {
488488
exports: {}
489489
};
490490
requireCache[p] = m;
491-
Object.assign(moduleScope, {
491+
const moduleScope = {
492+
...baseModuleScope,
492493
require: _require.bind(
493494
null,
494495
path.dirname(p),
@@ -511,7 +512,7 @@ const describeCases = config => {
511512
__dirname: path.dirname(p),
512513
__filename: p,
513514
_globalAssign: { expect }
514-
});
515+
};
515516
if (testConfig.moduleScope) {
516517
testConfig.moduleScope(moduleScope);
517518
}
@@ -549,14 +550,14 @@ const describeCases = config => {
549550
results.push(
550551
_require(
551552
outputDirectory,
552-
optionsArr[i],
553+
options,
553554
"./" + bundlePathItem
554555
)
555556
);
556557
}
557558
} else {
558559
results.push(
559-
_require(outputDirectory, optionsArr[i], bundlePath)
560+
_require(outputDirectory, options, bundlePath)
560561
);
561562
}
562563
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
22
findBundle: function () {
3-
return ["./runtime.js", "./main.js"];
3+
return ["./runtime.mjs", "./main.mjs"];
44
}
55
};

test/configCases/module/runtime-chunk/webpack.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/** @type {import("../../../../").Configuration} */
22
module.exports = {
33
output: {
4-
filename: "[name].js"
4+
filename: "[name].mjs"
55
},
6-
target: "web",
6+
target: ["web", "es2020"],
77
experiments: {
88
outputModule: true
99
},

0 commit comments

Comments
 (0)