feat: add `snapshot.contextModule `to configure snapshots for context modules

This commit is contained in:
Xiao 2025-08-26 03:16:48 +08:00 committed by GitHub
parent 076863133b
commit d224b7b334
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 118 additions and 15 deletions

View File

@ -2475,6 +2475,19 @@ export interface SnapshotOptions {
*/
timestamp?: boolean;
};
/**
* Options for snapshotting the context module to determine if it needs to be built again.
*/
contextModule?: {
/**
* Use hashes of the content of the files/directories to determine invalidation.
*/
hash?: boolean;
/**
* Use timestamps of the files/directories to determine invalidation.
*/
timestamp?: boolean;
};
/**
* List of paths that are managed by a package manager and contain a version or hash in its path so all files are immutable.
*/

View File

@ -109,8 +109,6 @@ const makeSerializable = require("./util/makeSerializable");
/** @typedef {Record<ModuleId, FakeMapType>} FakeMap */
const SNAPSHOT_OPTIONS = { timestamp: true };
class ContextModule extends Module {
/**
* @param {ResolveDependencies} resolveDependencies function to get dependencies in this context
@ -535,6 +533,8 @@ class ContextModule extends Module {
}
if (!this.context && !this.options.resource) return callback();
const snapshotOptions = compilation.options.snapshot.contextModule;
compilation.fileSystemInfo.createSnapshot(
startTime,
null,
@ -544,7 +544,7 @@ class ContextModule extends Module {
? [this.options.resource]
: /** @type {string[]} */ (this.options.resource),
null,
SNAPSHOT_OPTIONS,
snapshotOptions,
(err, snapshot) => {
if (err) return callback(err);
/** @type {BuildInfo} */

View File

@ -694,6 +694,7 @@ const applySnapshotDefaults = (snapshot, { production, futureDefaults }) => {
F(snapshot, "module", () =>
production ? { timestamp: true, hash: true } : { timestamp: true }
);
F(snapshot, "contextModule", () => ({ timestamp: true }));
F(snapshot, "resolve", () =>
production ? { timestamp: true, hash: true } : { timestamp: true }
);

View File

@ -457,6 +457,13 @@ const getNormalizedWebpackOptions = (config) => ({
timestamp: module.timestamp,
hash: module.hash
})),
contextModule: optionalNestedConfig(
snapshot.contextModule,
(contextModule) => ({
timestamp: contextModule.timestamp,
hash: contextModule.hash
})
),
immutablePaths: optionalNestedArray(snapshot.immutablePaths, (p) => [...p]),
managedPaths: optionalNestedArray(snapshot.managedPaths, (p) => [...p]),
unmanagedPaths: optionalNestedArray(snapshot.unmanagedPaths, (p) => [...p])

File diff suppressed because one or more lines are too long

View File

@ -5022,6 +5022,21 @@
}
}
},
"contextModule": {
"description": "Options for snapshotting the context module to determine if it needs to be built again.",
"type": "object",
"additionalProperties": false,
"properties": {
"hash": {
"description": "Use hashes of the content of the files/directories to determine invalidation.",
"type": "boolean"
},
"timestamp": {
"description": "Use timestamps of the files/directories to determine invalidation.",
"type": "boolean"
}
}
},
"immutablePaths": {
"description": "List of paths that are managed by a package manager and contain a version or hash in its path so all files are immutable.",
"type": "array",

View File

@ -653,6 +653,9 @@ describe("snapshots", () => {
"hash": true,
"timestamp": true,
},
"contextModule": Object {
"timestamp": true,
},
"immutablePaths": Array [],
"managedPaths": Array [
"<cwd>/node_modules/",

View File

@ -82,6 +82,16 @@ describe("Persistent Caching", () => {
);
});
const getCacheFileTimes = async () => {
const cacheFiles = (await readdir(cachePath)).sort();
return new Map(
cacheFiles.map((f) => [
f,
fs.statSync(path.join(cachePath, f)).mtime.toString()
])
);
};
const execute = () => {
const cache = {};
const require = (name) => {
@ -230,11 +240,7 @@ sum([1,2,3])
`
});
await compile({ entry: "./src/main.js" });
const firstCacheFiles = (await readdir(cachePath)).sort();
// cSpell:words Mtimes
const firstMtimes = firstCacheFiles.map(
(f) => fs.statSync(path.join(cachePath, f)).mtime
);
const firstCacheFileTimes = await getCacheFileTimes();
await updateSrc({
"main.js": `
@ -248,11 +254,29 @@ import 'lodash';
readonly: true
}
});
const cacheFiles = (await readdir(cachePath)).sort();
expect(cacheFiles).toStrictEqual(firstCacheFiles);
expect(
firstCacheFiles.map((f) => fs.statSync(path.join(cachePath, f)).mtime)
// cSpell:words Mtimes
).toStrictEqual(firstMtimes);
await expect(getCacheFileTimes()).resolves.toEqual(firstCacheFileTimes);
}, 20000);
it("should not invalidate cache files if timestamps changed with dynamic import()", async () => {
const configAdditions = {
entry: "./src/main.js",
snapshot: {
resolve: { hash: true },
module: { hash: true },
contextModule: { hash: true }
}
};
await updateSrc({
"newer.js": "export default 2;",
// eslint-disable-next-line no-template-curly-in-string
"main.js": 'const f = "newer.js"; import(`./${f}`);'
});
await compile(configAdditions);
const firstCacheFileTimes = await getCacheFileTimes();
await utimes(path.resolve(srcPath, "newer.js"), new Date(), new Date());
await compile(configAdditions);
await expect(getCacheFileTimes()).resolves.toEqual(firstCacheFileTimes);
}, 20000);
});

View File

@ -8830,6 +8830,32 @@ Object {
"multiple": false,
"simpleType": "boolean",
},
"snapshot-context-module-hash": Object {
"configs": Array [
Object {
"description": "Use hashes of the content of the files/directories to determine invalidation.",
"multiple": false,
"path": "snapshot.contextModule.hash",
"type": "boolean",
},
],
"description": "Use hashes of the content of the files/directories to determine invalidation.",
"multiple": false,
"simpleType": "boolean",
},
"snapshot-context-module-timestamp": Object {
"configs": Array [
Object {
"description": "Use timestamps of the files/directories to determine invalidation.",
"multiple": false,
"path": "snapshot.contextModule.timestamp",
"type": "boolean",
},
],
"description": "Use timestamps of the files/directories to determine invalidation.",
"multiple": false,
"simpleType": "boolean",
},
"snapshot-immutable-paths": Object {
"configs": Array [
Object {

14
types.d.ts vendored
View File

@ -16170,6 +16170,20 @@ declare interface SnapshotOptionsWebpackOptions {
timestamp?: boolean;
};
/**
* Options for snapshotting the context module to determine if it needs to be built again.
*/
contextModule?: {
/**
* Use hashes of the content of the files/directories to determine invalidation.
*/
hash?: boolean;
/**
* Use timestamps of the files/directories to determine invalidation.
*/
timestamp?: boolean;
};
/**
* List of paths that are managed by a package manager and contain a version or hash in its path so all files are immutable.
*/