mirror of https://github.com/webpack/webpack.git
fix: fix the case where an ESM entryChunk depends on the runtimeChunk hash (#19570)
Github Actions / lint (push) Has been cancelled
Details
Github Actions / validate-legacy-node (push) Has been cancelled
Details
Github Actions / benchmark (1/4) (push) Has been cancelled
Details
Github Actions / benchmark (2/4) (push) Has been cancelled
Details
Github Actions / benchmark (3/4) (push) Has been cancelled
Details
Github Actions / benchmark (4/4) (push) Has been cancelled
Details
Github Actions / basic (push) Has been cancelled
Details
Github Actions / unit (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Has been cancelled
Details
Update examples / examples (push) Has been cancelled
Details
Github Actions / lint (push) Has been cancelled
Details
Github Actions / validate-legacy-node (push) Has been cancelled
Details
Github Actions / benchmark (1/4) (push) Has been cancelled
Details
Github Actions / benchmark (2/4) (push) Has been cancelled
Details
Github Actions / benchmark (3/4) (push) Has been cancelled
Details
Github Actions / benchmark (4/4) (push) Has been cancelled
Details
Github Actions / basic (push) Has been cancelled
Details
Github Actions / unit (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Has been cancelled
Details
Update examples / examples (push) Has been cancelled
Details
This commit is contained in:
parent
1b34181550
commit
e805dc1b09
|
|
@ -4366,16 +4366,21 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
|
|||
/** @type {Chunk[]} */
|
||||
const unorderedRuntimeChunks = [];
|
||||
/** @type {Chunk[]} */
|
||||
const otherChunks = [];
|
||||
const initialChunks = [];
|
||||
/** @type {Chunk[]} */
|
||||
const asyncChunks = [];
|
||||
for (const c of this.chunks) {
|
||||
if (c.hasRuntime()) {
|
||||
unorderedRuntimeChunks.push(c);
|
||||
} else if (c.canBeInitial()) {
|
||||
initialChunks.push(c);
|
||||
} else {
|
||||
otherChunks.push(c);
|
||||
asyncChunks.push(c);
|
||||
}
|
||||
}
|
||||
unorderedRuntimeChunks.sort(byId);
|
||||
otherChunks.sort(byId);
|
||||
initialChunks.sort(byId);
|
||||
asyncChunks.sort(byId);
|
||||
|
||||
/** @typedef {{ chunk: Chunk, referencedBy: RuntimeChunkInfo[], remaining: number }} RuntimeChunkInfo */
|
||||
/** @type {Map<Chunk, RuntimeChunkInfo>} */
|
||||
|
|
@ -4541,8 +4546,9 @@ This prevents using hashes of each other and should be avoided.`);
|
|||
}
|
||||
this.logger.timeAggregate("hashing: hash chunks");
|
||||
};
|
||||
for (const chunk of otherChunks) processChunk(chunk);
|
||||
for (const chunk of asyncChunks) processChunk(chunk);
|
||||
for (const chunk of runtimeChunks) processChunk(chunk);
|
||||
for (const chunk of initialChunks) processChunk(chunk);
|
||||
if (errors.length > 0) {
|
||||
errors.sort(compareSelect(err => err.module, compareModulesByIdentifier));
|
||||
for (const error of errors) {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,33 @@ const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
|
|||
const { getUndoPath } = require("../util/identifier");
|
||||
|
||||
/** @typedef {import("../Chunk")} Chunk */
|
||||
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
||||
/** @typedef {import("../ChunkGroup")} ChunkGroup */
|
||||
/** @typedef {import("../Compiler")} Compiler */
|
||||
/** @typedef {import("../Entrypoint")} Entrypoint */
|
||||
/** @typedef {import("../Module")} Module */
|
||||
|
||||
/**
|
||||
* Gets information about a chunk including its entries and runtime chunk
|
||||
* @param {Chunk} chunk The chunk to get information for
|
||||
* @param {ChunkGraph} chunkGraph The chunk graph containing the chunk
|
||||
* @returns {{entries: Array<[Module, Entrypoint | undefined]>, runtimeChunk: Chunk|null}} Object containing chunk entries and runtime chunk
|
||||
*/
|
||||
function getChunkInfo(chunk, chunkGraph) {
|
||||
const entries = Array.from(
|
||||
chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
|
||||
);
|
||||
const runtimeChunk =
|
||||
entries.length > 0
|
||||
? /** @type {Entrypoint[][]} */
|
||||
(entries)[0][1].getRuntimeChunk()
|
||||
: null;
|
||||
|
||||
return {
|
||||
entries,
|
||||
runtimeChunk
|
||||
};
|
||||
}
|
||||
|
||||
class ModuleChunkFormatPlugin {
|
||||
/**
|
||||
|
|
@ -76,13 +101,8 @@ class ModuleChunkFormatPlugin {
|
|||
)
|
||||
);
|
||||
}
|
||||
const entries = Array.from(
|
||||
chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
|
||||
);
|
||||
if (entries.length > 0) {
|
||||
const runtimeChunk =
|
||||
/** @type {Entrypoint[][]} */
|
||||
(entries)[0][1].getRuntimeChunk();
|
||||
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
|
||||
if (runtimeChunk) {
|
||||
const currentOutputName = compilation
|
||||
.getPath(
|
||||
getChunkFilenameTemplate(chunk, compilation.outputOptions),
|
||||
|
|
@ -207,11 +227,15 @@ class ModuleChunkFormatPlugin {
|
|||
"ModuleChunkFormatPlugin",
|
||||
(chunk, hash, { chunkGraph, runtimeTemplate }) => {
|
||||
if (chunk.hasRuntime()) return;
|
||||
const { entries, runtimeChunk } = getChunkInfo(chunk, chunkGraph);
|
||||
hash.update("ModuleChunkFormatPlugin");
|
||||
hash.update("1");
|
||||
const entries = Array.from(
|
||||
chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
|
||||
);
|
||||
if (runtimeChunk && runtimeChunk.hash) {
|
||||
// Any change to runtimeChunk should trigger a hash update,
|
||||
// we shouldn't depend on or inspect its internal implementation.
|
||||
// import __webpack_require__ from "./runtime-main.e9400aee33633a3973bd.js";
|
||||
hash.update(runtimeChunk.hash);
|
||||
}
|
||||
updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ const path = require("path");
|
|||
const fs = require("graceful-fs");
|
||||
const vm = require("vm");
|
||||
const rimraf = require("rimraf");
|
||||
const { pathToFileURL, fileURLToPath } = require("url");
|
||||
const checkArrayExpectation = require("./checkArrayExpectation");
|
||||
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
|
||||
const { remove } = require("./helpers/remove");
|
||||
const prepareOptions = require("./helpers/prepareOptions");
|
||||
const deprecationTracking = require("./helpers/deprecationTracking");
|
||||
const FakeDocument = require("./helpers/FakeDocument");
|
||||
const asModule = require("./helpers/asModule");
|
||||
|
||||
/**
|
||||
* @param {string} src src
|
||||
|
|
@ -200,7 +202,7 @@ const describeCases = config => {
|
|||
{
|
||||
aggregateTimeout: 1000
|
||||
},
|
||||
(err, stats) => {
|
||||
async (err, stats) => {
|
||||
if (err) return compilationFinished(err);
|
||||
if (!stats) {
|
||||
return compilationFinished(
|
||||
|
|
@ -273,29 +275,122 @@ const describeCases = config => {
|
|||
document: new FakeDocument()
|
||||
};
|
||||
|
||||
const baseModuleScope = {
|
||||
console,
|
||||
it: run.it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
expect,
|
||||
jest,
|
||||
STATS_JSON: jsonStats,
|
||||
nsObj: m => {
|
||||
Object.defineProperty(m, Symbol.toStringTag, {
|
||||
value: "Module"
|
||||
});
|
||||
return m;
|
||||
},
|
||||
window: globalContext,
|
||||
self: globalContext,
|
||||
WATCH_STEP: run.name,
|
||||
STATE: state
|
||||
};
|
||||
|
||||
const esmCache = new Map();
|
||||
const esmIdentifier = `${category.name}-${testName}`;
|
||||
const esmContext = vm.createContext(baseModuleScope, {
|
||||
name: "context for esm"
|
||||
});
|
||||
// ESM
|
||||
const isModule =
|
||||
options.experiments && options.experiments.outputModule;
|
||||
|
||||
/**
|
||||
* @param {string} currentDirectory the current directory
|
||||
* @param {TODO} module a module
|
||||
* @param {string} currentDirectory The directory to resolve relative paths from
|
||||
* @param {string} module The module to require
|
||||
* @param {("unlinked"|"evaluated")} esmMode The mode for ESM module handling
|
||||
* @returns {EXPECTED_ANY} required module
|
||||
* @private
|
||||
*/
|
||||
function _require(currentDirectory, module) {
|
||||
if (Array.isArray(module) || /^\.\.?\//.test(module)) {
|
||||
function _require(currentDirectory, module, esmMode) {
|
||||
if (/^\.\.?\//.test(module) || path.isAbsolute(module)) {
|
||||
let fn;
|
||||
let content;
|
||||
let p;
|
||||
if (Array.isArray(module)) {
|
||||
p = path.join(currentDirectory, module[0]);
|
||||
content = module
|
||||
.map(arg => {
|
||||
p = path.join(currentDirectory, arg);
|
||||
return fs.readFileSync(p, "utf-8");
|
||||
})
|
||||
.join("\n");
|
||||
} else {
|
||||
p = path.join(currentDirectory, module);
|
||||
content = fs.readFileSync(p, "utf-8");
|
||||
const p = path.isAbsolute(module)
|
||||
? module
|
||||
: path.join(currentDirectory, module);
|
||||
const content = fs.readFileSync(p, "utf-8");
|
||||
|
||||
if (isModule) {
|
||||
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'."
|
||||
);
|
||||
let esm = esmCache.get(p);
|
||||
if (!esm) {
|
||||
esm = new vm.SourceTextModule(content, {
|
||||
identifier: `${esmIdentifier}-${p}`,
|
||||
url: `${pathToFileURL(p).href}?${esmIdentifier}`,
|
||||
context: esmContext,
|
||||
initializeImportMeta: (meta, module) => {
|
||||
meta.url = pathToFileURL(p).href;
|
||||
},
|
||||
importModuleDynamically: async (
|
||||
specifier,
|
||||
module
|
||||
) => {
|
||||
const normalizedSpecifier =
|
||||
specifier.startsWith("file:")
|
||||
? `./${path.relative(
|
||||
path.dirname(p),
|
||||
fileURLToPath(specifier)
|
||||
)}`
|
||||
: specifier.replace(
|
||||
/https:\/\/test.cases\/path\//,
|
||||
"./"
|
||||
);
|
||||
const result = await _require(
|
||||
currentDirectory,
|
||||
normalizedSpecifier,
|
||||
"evaluated"
|
||||
);
|
||||
return await asModule(result, module.context);
|
||||
}
|
||||
});
|
||||
esmCache.set(p, esm);
|
||||
}
|
||||
if (esmMode === "unlinked") return esm;
|
||||
return (async () => {
|
||||
if (esmMode === "unlinked") return esm;
|
||||
if (esm.status !== "evaluated") {
|
||||
await esm.link(
|
||||
async (specifier, referencingModule) =>
|
||||
await asModule(
|
||||
await _require(
|
||||
path.dirname(
|
||||
referencingModule.identifier
|
||||
? referencingModule.identifier.slice(
|
||||
esmIdentifier.length + 1
|
||||
)
|
||||
: fileURLToPath(referencingModule.url)
|
||||
),
|
||||
specifier,
|
||||
"unlinked"
|
||||
),
|
||||
referencingModule.context,
|
||||
true
|
||||
)
|
||||
);
|
||||
// node.js 10 needs instantiate
|
||||
if (esm.instantiate) esm.instantiate();
|
||||
await esm.evaluate();
|
||||
}
|
||||
if (esmMode === "evaluated") return esm;
|
||||
const ns = esm.namespace;
|
||||
return ns.default && ns.default instanceof Promise
|
||||
? ns.default
|
||||
: ns;
|
||||
})();
|
||||
}
|
||||
|
||||
if (
|
||||
options.target === "web" ||
|
||||
options.target === "webworker"
|
||||
|
|
@ -358,10 +453,33 @@ const describeCases = config => {
|
|||
|
||||
if (testConfig.noTests)
|
||||
return process.nextTick(compilationFinished);
|
||||
_require(
|
||||
|
||||
const getBundle = (outputDirectory, module) => {
|
||||
if (Array.isArray(module)) {
|
||||
return module.map(arg =>
|
||||
path.join(outputDirectory, arg)
|
||||
);
|
||||
} else if (module instanceof RegExp) {
|
||||
return fs
|
||||
.readdirSync(outputDirectory)
|
||||
.filter(f => module.test(f))
|
||||
.map(f => path.join(outputDirectory, f));
|
||||
}
|
||||
return [path.join(outputDirectory, module)];
|
||||
};
|
||||
|
||||
const promises = [];
|
||||
for (const p of getBundle(
|
||||
outputDirectory,
|
||||
testConfig.bundlePath || "./bundle.js"
|
||||
);
|
||||
)) {
|
||||
promises.push(
|
||||
Promise.resolve().then(() =>
|
||||
_require(outputDirectory, p)
|
||||
)
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
if (run.getNumberOfTests() < 1)
|
||||
return compilationFinished(
|
||||
|
|
@ -431,6 +549,12 @@ const describeCases = config => {
|
|||
afterAll(() => {
|
||||
remove(tempDirectory);
|
||||
});
|
||||
|
||||
const {
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach
|
||||
} = createLazyTestEnv(10000);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export var value = "0";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export var value = "0";
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { react } from "./react";
|
||||
|
||||
it("should work where an ESM entryChunk depends on the runtimeChunk", async function (done) {
|
||||
const mainChunk = STATS_JSON.chunks.find((chunk) => chunk.id === "main");
|
||||
const runtimeChunk = STATS_JSON.chunks.find((chunk) => chunk.id === "runtime-main");
|
||||
const dynamic1Chunk = STATS_JSON.chunks.find((chunk) => chunk.id === "dynamic-1_js");
|
||||
const dynamic2Chunk = STATS_JSON.chunks.find((chunk) => chunk.id === "dynamic-2_js");
|
||||
const reactChunk = STATS_JSON.chunks.find((chunk) => chunk.id === "react");
|
||||
expect(mainChunk).toBeDefined();
|
||||
expect(react).toBe("react");
|
||||
|
||||
await import('./dynamic-1').then(console.log)
|
||||
await import('./dynamic-2').then(console.log)
|
||||
|
||||
if (WATCH_STEP === "0") {
|
||||
STATE.mainChunkHash = mainChunk.hash;
|
||||
STATE.dynamic1ChunkHash = dynamic1Chunk.hash;
|
||||
STATE.dynamic2ChunkHash = dynamic2Chunk.hash;
|
||||
STATE.runtimeChunkHash = runtimeChunk.hash;
|
||||
STATE.reactChunkHash = reactChunk.hash;
|
||||
} else {
|
||||
// async dynamic2Chunk needn't be updated
|
||||
expect(dynamic2Chunk.hash).toBe(STATE.dynamic2ChunkHash);
|
||||
// initial reactChunk is needn't be updated
|
||||
expect(reactChunk.hash).toBe(STATE.reactChunkHash);
|
||||
|
||||
|
||||
// initial mainChunk need to be updated
|
||||
expect(mainChunk.hash).not.toBe(STATE.mainChunkHash);
|
||||
// async dynamic1Chunk need to be updated
|
||||
expect(dynamic1Chunk.hash).not.toBe(STATE.dynamic1ChunkHash);
|
||||
// runtime runtimeChunk need to be updated
|
||||
expect(runtimeChunk.hash).not.toBe(STATE.runtimeChunkHash);
|
||||
}
|
||||
done()
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
export const react = "react";
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export var value = "1";
|
||||
|
||||
import("./dynamic-2").then(console.log)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
bundlePath: /^main\./
|
||||
};
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
experiments: {
|
||||
outputModule: true
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: "all",
|
||||
minSize: 1,
|
||||
cacheGroups: {
|
||||
react: {
|
||||
test: /react/,
|
||||
name: "react",
|
||||
chunks: "all",
|
||||
priority: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
runtimeChunk: {
|
||||
/**
|
||||
* @param {import("../../../../").Entrypoint} entrypoint The entrypoint to generate runtime chunk name for
|
||||
* @returns {string} The generated runtime chunk name
|
||||
*/
|
||||
name: entrypoint => `runtime-${entrypoint.name}`
|
||||
}
|
||||
},
|
||||
output: {
|
||||
filename: "[name].[contenthash].js",
|
||||
chunkFilename: "[name].[contenthash].js"
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue