mirror of https://github.com/webpack/webpack.git
fix outputModule with initial splitChunks
This commit is contained in:
parent
041287ff58
commit
c69e37c39d
|
@ -5,18 +5,16 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { ConcatSource, RawSource } = require("webpack-sources");
|
const { ConcatSource } = require("webpack-sources");
|
||||||
const { RuntimeGlobals } = require("..");
|
const { RuntimeGlobals } = require("..");
|
||||||
const HotUpdateChunk = require("../HotUpdateChunk");
|
const HotUpdateChunk = require("../HotUpdateChunk");
|
||||||
const Template = require("../Template");
|
const Template = require("../Template");
|
||||||
|
const { getAllChunks } = require("../javascript/ChunkHelpers");
|
||||||
const {
|
const {
|
||||||
getCompilationHooks,
|
getCompilationHooks,
|
||||||
getChunkFilenameTemplate
|
getChunkFilenameTemplate
|
||||||
} = require("../javascript/JavascriptModulesPlugin");
|
} = require("../javascript/JavascriptModulesPlugin");
|
||||||
const {
|
const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
|
||||||
generateEntryStartup,
|
|
||||||
updateHashForEntryStartup
|
|
||||||
} = require("../javascript/StartupHelpers");
|
|
||||||
|
|
||||||
/** @typedef {import("../Compiler")} Compiler */
|
/** @typedef {import("../Compiler")} Compiler */
|
||||||
|
|
||||||
|
@ -84,63 +82,90 @@ class ModuleChunkFormatPlugin {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.split("/");
|
.split("/");
|
||||||
const runtimeOutputName = compilation
|
|
||||||
.getPath(
|
|
||||||
getChunkFilenameTemplate(
|
|
||||||
runtimeChunk,
|
|
||||||
compilation.outputOptions
|
|
||||||
),
|
|
||||||
{
|
|
||||||
chunk: runtimeChunk,
|
|
||||||
contentHashType: "javascript"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.split("/");
|
|
||||||
|
|
||||||
// remove filename, we only need the directory
|
// remove filename, we only need the directory
|
||||||
const outputFilename = currentOutputName.pop();
|
currentOutputName.pop();
|
||||||
|
|
||||||
// remove common parts
|
const getRelativePath = chunk => {
|
||||||
while (
|
const baseOutputName = currentOutputName.slice();
|
||||||
currentOutputName.length > 0 &&
|
const chunkOutputName = compilation
|
||||||
runtimeOutputName.length > 0 &&
|
.getPath(
|
||||||
currentOutputName[0] === runtimeOutputName[0]
|
getChunkFilenameTemplate(
|
||||||
) {
|
chunk,
|
||||||
currentOutputName.shift();
|
compilation.outputOptions
|
||||||
runtimeOutputName.shift();
|
),
|
||||||
}
|
{
|
||||||
|
chunk: chunk,
|
||||||
|
contentHashType: "javascript"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.split("/");
|
||||||
|
|
||||||
// create final path
|
// remove common parts
|
||||||
const runtimePath =
|
while (
|
||||||
(currentOutputName.length > 0
|
baseOutputName.length > 0 &&
|
||||||
? "../".repeat(currentOutputName.length)
|
chunkOutputName.length > 0 &&
|
||||||
: "./") + runtimeOutputName.join("/");
|
baseOutputName[0] === chunkOutputName[0]
|
||||||
|
) {
|
||||||
|
baseOutputName.shift();
|
||||||
|
chunkOutputName.shift();
|
||||||
|
}
|
||||||
|
// create final path
|
||||||
|
return (
|
||||||
|
(baseOutputName.length > 0
|
||||||
|
? "../".repeat(baseOutputName.length)
|
||||||
|
: "./") + chunkOutputName.join("/")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const entrySource = new ConcatSource();
|
const entrySource = new ConcatSource();
|
||||||
entrySource.add(source);
|
entrySource.add(source);
|
||||||
entrySource.add(";\n\n// load runtime\n");
|
entrySource.add(";\n\n// load runtime\n");
|
||||||
entrySource.add(
|
entrySource.add(
|
||||||
`import __webpack_require__ from ${JSON.stringify(
|
`import __webpack_require__ from ${JSON.stringify(
|
||||||
runtimePath
|
getRelativePath(runtimeChunk)
|
||||||
)};\n`
|
)};\n`
|
||||||
);
|
);
|
||||||
entrySource.add(
|
|
||||||
`import * as __webpack_self_exports__ from ${JSON.stringify(
|
const startupSource = new ConcatSource();
|
||||||
"./" + outputFilename
|
startupSource.add(
|
||||||
)};\n`
|
`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
|
||||||
);
|
`__webpack_require__(${RuntimeGlobals.entryModuleId} = moduleId)`,
|
||||||
entrySource.add(
|
"moduleId"
|
||||||
`${RuntimeGlobals.externalInstallChunk}(__webpack_self_exports__);\n`
|
)}\n`
|
||||||
);
|
|
||||||
const startupSource = new RawSource(
|
|
||||||
generateEntryStartup(
|
|
||||||
chunkGraph,
|
|
||||||
runtimeTemplate,
|
|
||||||
entries,
|
|
||||||
chunk,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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(
|
||||||
|
entrypoint,
|
||||||
|
runtimeChunk,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
for (const chunk of chunks) {
|
||||||
|
if (loadedChunks.has(chunk)) 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 __webpack_exports__ = " : ""
|
||||||
|
}__webpack_exec__(${JSON.stringify(moduleId)});\n`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
entrySource.add(
|
entrySource.add(
|
||||||
hooks.renderStartup.call(
|
hooks.renderStartup.call(
|
||||||
startupSource,
|
startupSource,
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||||
|
Author Tobias Koppers @sokra
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Entrypoint = require("../Entrypoint");
|
||||||
|
|
||||||
|
/** @typedef {import("../Chunk")} Chunk */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Entrypoint} entrypoint a chunk group
|
||||||
|
* @param {Chunk} excludedChunk1 current chunk which is excluded
|
||||||
|
* @param {Chunk} excludedChunk2 runtime chunk which is excluded
|
||||||
|
* @returns {Set<Chunk>} chunks
|
||||||
|
*/
|
||||||
|
const getAllChunks = (entrypoint, excludedChunk1, excludedChunk2) => {
|
||||||
|
const queue = new Set([entrypoint]);
|
||||||
|
const chunks = new Set();
|
||||||
|
for (const entrypoint of queue) {
|
||||||
|
for (const chunk of entrypoint.chunks) {
|
||||||
|
if (chunk === excludedChunk1) continue;
|
||||||
|
if (chunk === excludedChunk2) continue;
|
||||||
|
chunks.add(chunk);
|
||||||
|
}
|
||||||
|
for (const parent of entrypoint.parentsIterable) {
|
||||||
|
if (parent instanceof Entrypoint) queue.add(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chunks;
|
||||||
|
};
|
||||||
|
exports.getAllChunks = getAllChunks;
|
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const Entrypoint = require("../Entrypoint");
|
|
||||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||||
const Template = require("../Template");
|
const Template = require("../Template");
|
||||||
const { isSubset } = require("../util/SetHelpers");
|
const { isSubset } = require("../util/SetHelpers");
|
||||||
|
const { getAllChunks } = require("./ChunkHelpers");
|
||||||
const { chunkHasJs } = require("./JavascriptModulesPlugin");
|
const { chunkHasJs } = require("./JavascriptModulesPlugin");
|
||||||
|
|
||||||
/** @typedef {import("../util/Hash")} Hash */
|
/** @typedef {import("../util/Hash")} Hash */
|
||||||
|
@ -19,30 +19,6 @@ const { chunkHasJs } = require("./JavascriptModulesPlugin");
|
||||||
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
|
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
|
||||||
/** @typedef {(string|number)[]} EntryItem */
|
/** @typedef {(string|number)[]} EntryItem */
|
||||||
|
|
||||||
// TODO move to this file to ../javascript/ChunkHelpers.js
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Entrypoint} entrypoint a chunk group
|
|
||||||
* @param {Chunk} excludedChunk1 current chunk which is excluded
|
|
||||||
* @param {Chunk} excludedChunk2 runtime chunk which is excluded
|
|
||||||
* @returns {Set<Chunk>} chunks
|
|
||||||
*/
|
|
||||||
const getAllChunks = (entrypoint, excludedChunk1, excludedChunk2) => {
|
|
||||||
const queue = new Set([entrypoint]);
|
|
||||||
const chunks = new Set();
|
|
||||||
for (const entrypoint of queue) {
|
|
||||||
for (const chunk of entrypoint.chunks) {
|
|
||||||
if (chunk === excludedChunk1) continue;
|
|
||||||
if (chunk === excludedChunk2) continue;
|
|
||||||
chunks.add(chunk);
|
|
||||||
}
|
|
||||||
for (const parent of entrypoint.parentsIterable) {
|
|
||||||
if (parent instanceof Entrypoint) queue.add(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chunks;
|
|
||||||
};
|
|
||||||
|
|
||||||
const EXPORT_PREFIX = "var __webpack_exports__ = ";
|
const EXPORT_PREFIX = "var __webpack_exports__ = ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -305,6 +305,7 @@ const describeCases = config => {
|
||||||
if (testConfig.beforeExecute) testConfig.beforeExecute();
|
if (testConfig.beforeExecute) testConfig.beforeExecute();
|
||||||
const results = [];
|
const results = [];
|
||||||
for (let i = 0; i < optionsArr.length; i++) {
|
for (let i = 0; i < optionsArr.length; i++) {
|
||||||
|
const options = optionsArr[i];
|
||||||
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
|
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
|
||||||
if (bundlePath) {
|
if (bundlePath) {
|
||||||
filesCount++;
|
filesCount++;
|
||||||
|
@ -327,6 +328,43 @@ const describeCases = config => {
|
||||||
const requireCache = Object.create(null);
|
const requireCache = Object.create(null);
|
||||||
const esmCache = new Map();
|
const esmCache = new Map();
|
||||||
const esmIdentifier = `${category.name}-${testName}-${i}`;
|
const esmIdentifier = `${category.name}-${testName}-${i}`;
|
||||||
|
const baseModuleScope = {
|
||||||
|
console: console,
|
||||||
|
it: _it,
|
||||||
|
beforeEach: _beforeEach,
|
||||||
|
afterEach: _afterEach,
|
||||||
|
expect,
|
||||||
|
jest,
|
||||||
|
__STATS__: jsonStats,
|
||||||
|
nsObj: m => {
|
||||||
|
Object.defineProperty(m, Symbol.toStringTag, {
|
||||||
|
value: "Module"
|
||||||
|
});
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let runInNewContext = false;
|
||||||
|
if (
|
||||||
|
options.target === "web" ||
|
||||||
|
options.target === "webworker"
|
||||||
|
) {
|
||||||
|
baseModuleScope.window = globalContext;
|
||||||
|
baseModuleScope.self = globalContext;
|
||||||
|
baseModuleScope.URL = URL;
|
||||||
|
baseModuleScope.Worker =
|
||||||
|
require("./helpers/createFakeWorker")({
|
||||||
|
outputDirectory
|
||||||
|
});
|
||||||
|
runInNewContext = true;
|
||||||
|
}
|
||||||
|
if (testConfig.moduleScope) {
|
||||||
|
testConfig.moduleScope(baseModuleScope);
|
||||||
|
}
|
||||||
|
const esmContext = vm.createContext(baseModuleScope, {
|
||||||
|
name: "context for esm"
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-loop-func
|
// eslint-disable-next-line no-loop-func
|
||||||
const _require = (
|
const _require = (
|
||||||
currentDirectory,
|
currentDirectory,
|
||||||
|
@ -380,41 +418,7 @@ const describeCases = config => {
|
||||||
options.experiments &&
|
options.experiments &&
|
||||||
options.experiments.outputModule;
|
options.experiments.outputModule;
|
||||||
|
|
||||||
let runInNewContext = false;
|
|
||||||
|
|
||||||
const moduleScope = {
|
|
||||||
console: console,
|
|
||||||
it: _it,
|
|
||||||
beforeEach: _beforeEach,
|
|
||||||
afterEach: _afterEach,
|
|
||||||
expect,
|
|
||||||
jest,
|
|
||||||
__STATS__: jsonStats,
|
|
||||||
nsObj: m => {
|
|
||||||
Object.defineProperty(m, Symbol.toStringTag, {
|
|
||||||
value: "Module"
|
|
||||||
});
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
options.target === "web" ||
|
|
||||||
options.target === "webworker"
|
|
||||||
) {
|
|
||||||
moduleScope.window = globalContext;
|
|
||||||
moduleScope.self = globalContext;
|
|
||||||
moduleScope.URL = URL;
|
|
||||||
moduleScope.Worker =
|
|
||||||
require("./helpers/createFakeWorker")({
|
|
||||||
outputDirectory
|
|
||||||
});
|
|
||||||
runInNewContext = true;
|
|
||||||
}
|
|
||||||
if (isModule) {
|
if (isModule) {
|
||||||
if (testConfig.moduleScope) {
|
|
||||||
testConfig.moduleScope(moduleScope);
|
|
||||||
}
|
|
||||||
if (!vm.SourceTextModule)
|
if (!vm.SourceTextModule)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Running this test requires '--experimental-vm-modules'.\nRun with 'node --experimental-vm-modules node_modules/jest-cli/bin/jest'."
|
"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 => {
|
||||||
esm = new vm.SourceTextModule(content, {
|
esm = new vm.SourceTextModule(content, {
|
||||||
identifier: esmIdentifier + "-" + p,
|
identifier: esmIdentifier + "-" + p,
|
||||||
url: pathToFileURL(p).href + "?" + esmIdentifier,
|
url: pathToFileURL(p).href + "?" + esmIdentifier,
|
||||||
context:
|
context: esmContext,
|
||||||
(parentModule && parentModule.context) ||
|
|
||||||
vm.createContext(moduleScope, {
|
|
||||||
name: `context for ${p}`
|
|
||||||
}),
|
|
||||||
initializeImportMeta: (meta, module) => {
|
initializeImportMeta: (meta, module) => {
|
||||||
meta.url = pathToFileURL(p).href;
|
meta.url = pathToFileURL(p).href;
|
||||||
},
|
},
|
||||||
|
@ -488,7 +488,8 @@ const describeCases = config => {
|
||||||
exports: {}
|
exports: {}
|
||||||
};
|
};
|
||||||
requireCache[p] = m;
|
requireCache[p] = m;
|
||||||
Object.assign(moduleScope, {
|
const moduleScope = {
|
||||||
|
...baseModuleScope,
|
||||||
require: _require.bind(
|
require: _require.bind(
|
||||||
null,
|
null,
|
||||||
path.dirname(p),
|
path.dirname(p),
|
||||||
|
@ -511,7 +512,7 @@ const describeCases = config => {
|
||||||
__dirname: path.dirname(p),
|
__dirname: path.dirname(p),
|
||||||
__filename: p,
|
__filename: p,
|
||||||
_globalAssign: { expect }
|
_globalAssign: { expect }
|
||||||
});
|
};
|
||||||
if (testConfig.moduleScope) {
|
if (testConfig.moduleScope) {
|
||||||
testConfig.moduleScope(moduleScope);
|
testConfig.moduleScope(moduleScope);
|
||||||
}
|
}
|
||||||
|
@ -549,14 +550,14 @@ const describeCases = config => {
|
||||||
results.push(
|
results.push(
|
||||||
_require(
|
_require(
|
||||||
outputDirectory,
|
outputDirectory,
|
||||||
optionsArr[i],
|
options,
|
||||||
"./" + bundlePathItem
|
"./" + bundlePathItem
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results.push(
|
results.push(
|
||||||
_require(outputDirectory, optionsArr[i], bundlePath)
|
_require(outputDirectory, options, bundlePath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
findBundle: function () {
|
findBundle: function () {
|
||||||
return ["./runtime.js", "./main.js"];
|
return ["./runtime.mjs", "./main.mjs"];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/** @type {import("../../../../").Configuration} */
|
/** @type {import("../../../../").Configuration} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
output: {
|
output: {
|
||||||
filename: "[name].js"
|
filename: "[name].mjs"
|
||||||
},
|
},
|
||||||
target: "web",
|
target: ["web", "es2020"],
|
||||||
experiments: {
|
experiments: {
|
||||||
outputModule: true
|
outputModule: true
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import value from "./separate";
|
||||||
|
import { test as t } from "external-self";
|
||||||
|
|
||||||
|
it("should compile", () => {
|
||||||
|
expect(value).toBe(42);
|
||||||
|
});
|
||||||
|
it("should circular depend on itself external", () => {
|
||||||
|
expect(test()).toBe(42);
|
||||||
|
expect(t()).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { test };
|
|
@ -0,0 +1 @@
|
||||||
|
export default 42;
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
findBundle: function () {
|
||||||
|
return ["./runtime.mjs", "./separate.mjs", "./main.mjs"];
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
/** @type {import("../../../../").Configuration} */
|
||||||
|
module.exports = {
|
||||||
|
output: {
|
||||||
|
filename: "[name].mjs",
|
||||||
|
library: {
|
||||||
|
type: "module"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
target: ["web", "es2020"],
|
||||||
|
experiments: {
|
||||||
|
outputModule: true
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: true,
|
||||||
|
runtimeChunk: "single",
|
||||||
|
splitChunks: {
|
||||||
|
cacheGroups: {
|
||||||
|
separate: {
|
||||||
|
test: /separate/,
|
||||||
|
chunks: "all",
|
||||||
|
filename: "separate.mjs",
|
||||||
|
enforce: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
"external-self": "./main.mjs"
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue