Skip to content

Commit afd3006

Browse files
refactor(module): add internal parser/generator lifecycle for incremental rebuilds (#21555)
* refactor(exports): add internal ExportsInfo reset APIs for incremental rebuilds Reset provide-side and use-side export info to the undetermined state so FlagDependencyExports/FlagDependencyUsage can recompute them against a persisted module graph. No behavior change. * refactor(exports): recurse use-info reset into nested and redirected exports Mirror setHasUseInfo: _resetUseInfo recurses into an owned nested exportsInfo and _resetUsedExports follows the redirect target, so no stale usage survives a use-only reset. * refactor(exports): keep nested exports info on provide reset; drop stale inlined flag Reset an owned nested exportsInfo in place instead of discarding it (its use state must survive a provide-only reset), and clear the _hasInlinedExports fast-path flag when use info is reset. * refactor(exports): recurse setHasProvideInfo into owned nested exports info After a provide reset the nested exports are back to undefined, and FlagDependencyExports only upgrades provided from false/null, so the module-level re-init must reach nested exports (mirrors setHasUseInfo). * refactor(exports): never follow the redirect target in the use reset The redirect points into another module's exports info; the global incremental reset loop already resets it via its own module, so following it wiped foreign state (and double-reset shared targets). * test(exports): assert runtime-usage, target-memo and deep-nested resets * test(exports): fix type errors in reset assertions Assert use-state resets through the public getUsed/getUsedName API instead of private fields, and cast the setTarget/canInlineProvide fixtures. * refactor(module): add internal parser/generator lifecycle for incremental rebuilds _cleanupParserForCache releases the parser between rebuilds so it does not retain the previous compilation; _restoreParserAndGenerator re-acquires both from the factory so the new compilation's plugin hooks apply. AsyncQueue.invalidate now tolerates items absent from the current run. No behavior change. * refactor(module): align override JSDoc with the base lifecycle methods
1 parent a8279a4 commit afd3006

5 files changed

Lines changed: 73 additions & 3 deletions

File tree

‎lib/Module.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,21 @@ class Module extends DependenciesBlock {
12501250
this.resolveOptions = undefined;
12511251
}
12521252

1253+
/**
1254+
* Re-acquire factory-derived helpers (parser/generator) for an incremental
1255+
* rebuild so this compilation's plugin hooks apply.
1256+
* @param {NormalModuleFactory} normalModuleFactory the normal module factory
1257+
* @returns {void}
1258+
*/
1259+
_restoreParserAndGenerator(normalModuleFactory) {}
1260+
1261+
/**
1262+
* Releases the cached parser between incremental rebuilds so it doesn't keep
1263+
* the previous compilation alive.
1264+
* @returns {void}
1265+
*/
1266+
_cleanupParserForCache() {}
1267+
12531268
/**
12541269
* Gets the original source.
12551270
* @returns {Source | null} the original source for the module before webpack transformation

‎lib/NormalModule.js‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,29 @@ class NormalModule extends Module {
815815
this._sideEffectsStateValue = undefined;
816816
}
817817

818+
/**
819+
* Releases the cached parser between incremental rebuilds so it doesn't keep
820+
* the previous compilation alive.
821+
* @returns {void}
822+
*/
823+
_cleanupParserForCache() {
824+
this.parser = undefined;
825+
}
826+
827+
/**
828+
* Re-acquire factory-derived helpers (parser/generator) for an incremental
829+
* rebuild so this compilation's plugin hooks apply.
830+
* @param {NormalModuleFactory} normalModuleFactory the normal module factory
831+
* @returns {void}
832+
*/
833+
_restoreParserAndGenerator(normalModuleFactory) {
834+
this.parser = normalModuleFactory.getParser(this.type, this.parserOptions);
835+
this.generator = normalModuleFactory.getGenerator(
836+
this.type,
837+
this.generatorOptions
838+
);
839+
}
840+
818841
/**
819842
* Module should be unsafe cached. Get data that's needed for that.
820843
* This data will be passed to restoreFromUnsafeCache later.

‎lib/util/AsyncQueue.js‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,10 @@ class AsyncQueue {
226226
*/
227227
invalidate(item) {
228228
const key = this._getKey(item);
229-
const entry =
230-
/** @type {AsyncQueueEntry<T, K, R>} */
231-
(this._entries.get(key));
229+
const entry = this._entries.get(key);
230+
// nothing queued for this item in the current run (e.g. incremental rebuild
231+
// against a persisted graph) — nothing to invalidate
232+
if (entry === undefined) return;
232233
this._entries.delete(key);
233234
if (entry.state === QUEUED_STATE) {
234235
this._queued.delete(entry);

‎test/AsyncQueue.unittest.js‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,10 @@ describe("AsyncQueue", () => {
162162
// stop before the async beforeAdd hook completes
163163
queue.stop();
164164
});
165+
166+
it("should ignore invalidate for an item that was never added", () => {
167+
const queue = createQueue();
168+
169+
expect(() => queue.invalidate(42)).not.toThrow();
170+
});
165171
});

‎test/NormalModule.unittest.js‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ describe("NormalModule", () => {
7272
});
7373
});
7474

75+
describe("#_cleanupParserForCache and #_restoreParserAndGenerator", () => {
76+
it("releases the parser and re-acquires parser and generator from the factory", () => {
77+
const factoryParser = /** @type {Parser} */ (
78+
/** @type {unknown} */ ({ parse() {} })
79+
);
80+
const factoryGenerator = /** @type {Generator} */ (
81+
/** @type {unknown} */ ({ generate() {} })
82+
);
83+
const normalModuleFactory =
84+
/** @type {import("../lib/NormalModuleFactory")} */ (
85+
/** @type {unknown} */ ({
86+
getParser: () => factoryParser,
87+
getGenerator: () => factoryGenerator
88+
})
89+
);
90+
91+
normalModule._cleanupParserForCache();
92+
expect(normalModule.parser).toBeUndefined();
93+
94+
normalModule._restoreParserAndGenerator(normalModuleFactory);
95+
expect(normalModule.parser).toBe(factoryParser);
96+
expect(normalModule.generator).toBe(factoryGenerator);
97+
});
98+
});
99+
75100
describe("#readableIdentifier", () => {
76101
it("calls the given requestShortener with the user request", () => {
77102
const spy = jest.fn();

0 commit comments

Comments
 (0)