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";
|
||||
|
||||
const { ConcatSource, RawSource } = require("webpack-sources");
|
||||
const { ConcatSource } = require("webpack-sources");
|
||||
const { RuntimeGlobals } = require("..");
|
||||
const HotUpdateChunk = require("../HotUpdateChunk");
|
||||
const Template = require("../Template");
|
||||
const { getAllChunks } = require("../javascript/ChunkHelpers");
|
||||
const {
|
||||
getCompilationHooks,
|
||||
getChunkFilenameTemplate
|
||||
} = require("../javascript/JavascriptModulesPlugin");
|
||||
const {
|
||||
generateEntryStartup,
|
||||
updateHashForEntryStartup
|
||||
} = require("../javascript/StartupHelpers");
|
||||
const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
|
||||
|
||||
/** @typedef {import("../Compiler")} Compiler */
|
||||
|
||||
|
@ -84,63 +82,90 @@ class ModuleChunkFormatPlugin {
|
|||
}
|
||||
)
|
||||
.split("/");
|
||||
const runtimeOutputName = compilation
|
||||
.getPath(
|
||||
getChunkFilenameTemplate(
|
||||
runtimeChunk,
|
||||
compilation.outputOptions
|
||||
),
|
||||
{
|
||||
chunk: runtimeChunk,
|
||||
contentHashType: "javascript"
|
||||
}
|
||||
)
|
||||
.split("/");
|
||||
|
||||
// remove filename, we only need the directory
|
||||
const outputFilename = currentOutputName.pop();
|
||||
currentOutputName.pop();
|
||||
|
||||
// remove common parts
|
||||
while (
|
||||
currentOutputName.length > 0 &&
|
||||
runtimeOutputName.length > 0 &&
|
||||
currentOutputName[0] === runtimeOutputName[0]
|
||||
) {
|
||||
currentOutputName.shift();
|
||||
runtimeOutputName.shift();
|
||||
}
|
||||
const getRelativePath = chunk => {
|
||||
const baseOutputName = currentOutputName.slice();
|
||||
const chunkOutputName = compilation
|
||||
.getPath(
|
||||
getChunkFilenameTemplate(
|
||||
chunk,
|
||||
compilation.outputOptions
|
||||
),
|
||||
{
|
||||
chunk: chunk,
|
||||
contentHashType: "javascript"
|
||||
}
|
||||
)
|
||||
.split("/");
|
||||
|
||||
// create final path
|
||||
const runtimePath =
|
||||
(currentOutputName.length > 0
|
||||
? "../".repeat(currentOutputName.length)
|
||||
: "./") + runtimeOutputName.join("/");
|
||||
// remove common parts
|
||||
while (
|
||||
baseOutputName.length > 0 &&
|
||||
chunkOutputName.length > 0 &&
|
||||
baseOutputName[0] === chunkOutputName[0]
|
||||
) {
|
||||
baseOutputName.shift();
|
||||
chunkOutputName.shift();
|
||||
}
|
||||
// create final path
|
||||
return (
|
||||
(baseOutputName.length > 0
|
||||
? "../".repeat(baseOutputName.length)
|
||||
: "./") + chunkOutputName.join("/")
|
||||
);
|
||||
};
|
||||
|
||||
const entrySource = new ConcatSource();
|
||||
entrySource.add(source);
|
||||
entrySource.add(";\n\n// load runtime\n");
|
||||
entrySource.add(
|
||||
`import __webpack_require__ from ${JSON.stringify(
|
||||
runtimePath
|
||||
getRelativePath(runtimeChunk)
|
||||
)};\n`
|
||||
);
|
||||
entrySource.add(
|
||||
`import * as __webpack_self_exports__ from ${JSON.stringify(
|
||||
"./" + outputFilename
|
||||
)};\n`
|
||||
);
|
||||
entrySource.add(
|
||||
`${RuntimeGlobals.externalInstallChunk}(__webpack_self_exports__);\n`
|
||||
);
|
||||
const startupSource = new RawSource(
|
||||
generateEntryStartup(
|
||||
chunkGraph,
|
||||
runtimeTemplate,
|
||||
entries,
|
||||
chunk,
|
||||
false
|
||||
)
|
||||
|
||||
const startupSource = new ConcatSource();
|
||||
startupSource.add(
|
||||
`var __webpack_exec__ = ${runtimeTemplate.returningFunction(
|
||||
`__webpack_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(
|
||||
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(
|
||||
hooks.renderStartup.call(
|
||||
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";
|
||||
|
||||
const Entrypoint = require("../Entrypoint");
|
||||
const RuntimeGlobals = require("../RuntimeGlobals");
|
||||
const Template = require("../Template");
|
||||
const { isSubset } = require("../util/SetHelpers");
|
||||
const { getAllChunks } = require("./ChunkHelpers");
|
||||
const { chunkHasJs } = require("./JavascriptModulesPlugin");
|
||||
|
||||
/** @typedef {import("../util/Hash")} Hash */
|
||||
|
@ -19,30 +19,6 @@ const { chunkHasJs } = require("./JavascriptModulesPlugin");
|
|||
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
|
||||
/** @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__ = ";
|
||||
|
||||
/**
|
||||
|
|
|
@ -305,6 +305,7 @@ const describeCases = config => {
|
|||
if (testConfig.beforeExecute) testConfig.beforeExecute();
|
||||
const results = [];
|
||||
for (let i = 0; i < optionsArr.length; i++) {
|
||||
const options = optionsArr[i];
|
||||
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
|
||||
if (bundlePath) {
|
||||
filesCount++;
|
||||
|
@ -327,6 +328,43 @@ const describeCases = config => {
|
|||
const requireCache = Object.create(null);
|
||||
const esmCache = new Map();
|
||||
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
|
||||
const _require = (
|
||||
currentDirectory,
|
||||
|
@ -380,41 +418,7 @@ const describeCases = config => {
|
|||
options.experiments &&
|
||||
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 (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(moduleScope);
|
||||
}
|
||||
if (!vm.SourceTextModule)
|
||||
throw new Error(
|
||||
"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, {
|
||||
identifier: esmIdentifier + "-" + p,
|
||||
url: pathToFileURL(p).href + "?" + esmIdentifier,
|
||||
context:
|
||||
(parentModule && parentModule.context) ||
|
||||
vm.createContext(moduleScope, {
|
||||
name: `context for ${p}`
|
||||
}),
|
||||
context: esmContext,
|
||||
initializeImportMeta: (meta, module) => {
|
||||
meta.url = pathToFileURL(p).href;
|
||||
},
|
||||
|
@ -488,7 +488,8 @@ const describeCases = config => {
|
|||
exports: {}
|
||||
};
|
||||
requireCache[p] = m;
|
||||
Object.assign(moduleScope, {
|
||||
const moduleScope = {
|
||||
...baseModuleScope,
|
||||
require: _require.bind(
|
||||
null,
|
||||
path.dirname(p),
|
||||
|
@ -511,7 +512,7 @@ const describeCases = config => {
|
|||
__dirname: path.dirname(p),
|
||||
__filename: p,
|
||||
_globalAssign: { expect }
|
||||
});
|
||||
};
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(moduleScope);
|
||||
}
|
||||
|
@ -549,14 +550,14 @@ const describeCases = config => {
|
|||
results.push(
|
||||
_require(
|
||||
outputDirectory,
|
||||
optionsArr[i],
|
||||
options,
|
||||
"./" + bundlePathItem
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
results.push(
|
||||
_require(outputDirectory, optionsArr[i], bundlePath)
|
||||
_require(outputDirectory, options, bundlePath)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
findBundle: function () {
|
||||
return ["./runtime.js", "./main.js"];
|
||||
return ["./runtime.mjs", "./main.mjs"];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
output: {
|
||||
filename: "[name].js"
|
||||
filename: "[name].mjs"
|
||||
},
|
||||
target: "web",
|
||||
target: ["web", "es2020"],
|
||||
experiments: {
|
||||
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