Skip to content

Commit 31c0456

Browse files
committed
fix(@angular/build): prevent stale bundler caching and correctly resolve load cache
Introduce an invalidation epoch guard to prevent caching stale esbuild results when an invalidation occurs while bundling is in-flight, and correctly await load cache checks before falling back to alternate keys.
1 parent 72dc967 commit 31c0456

1 file changed

Lines changed: 10 additions & 6 deletions

File tree

packages/angular/build/src/tools/esbuild/bundler-context.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export class BundlerContext {
8080
#optionsFactory: BundlerOptionsFactory<BuildOptions & { metafile: true; write: false }>;
8181
#shouldCacheResult: boolean;
8282
#loadCache?: LoadResultCache;
83+
#invalidationEpoch = 0;
8384
readonly watchFiles = new Set<string>();
8485

8586
constructor(
@@ -128,8 +129,8 @@ export class BundlerContext {
128129
const externalImportsBrowser = new Set<string>();
129130
const externalImportsServer = new Set<string>();
130131

131-
const outputFiles = [];
132-
let externalConfiguration;
132+
const outputFiles: BuildOutputFile[] = [];
133+
let externalConfiguration: Set<string> | undefined;
133134
for (const result of results) {
134135
warnings.push(...result.warnings);
135136
if (result.errors) {
@@ -202,6 +203,7 @@ export class BundlerContext {
202203
return this.#activeBundlePromise;
203204
}
204205

206+
const bundleEpoch = this.#invalidationEpoch;
205207
const bundlePromise = this.#performBundle().finally(() => {
206208
if (this.#activeBundlePromise === bundlePromise) {
207209
this.#activeBundlePromise = undefined;
@@ -210,7 +212,7 @@ export class BundlerContext {
210212
this.#activeBundlePromise = bundlePromise;
211213

212214
const result = await bundlePromise;
213-
if (this.#shouldCacheResult) {
215+
if (this.#shouldCacheResult && bundleEpoch === this.#invalidationEpoch) {
214216
this.#esbuildResult = result;
215217
}
216218

@@ -286,9 +288,10 @@ export class BundlerContext {
286288
}
287289

288290
if (this.#loadCache) {
289-
const cachedLoad = await (this.#loadCache.get(input) ??
290-
this.#loadCache.get(input.replace(';', ':')) ??
291-
this.#loadCache.get('file:' + normalizedAbsoluteInput));
291+
const cachedLoad =
292+
(await this.#loadCache.get(input)) ??
293+
(await this.#loadCache.get(input.replace(';', ':'))) ??
294+
(await this.#loadCache.get('file:' + normalizedAbsoluteInput));
292295
if (cachedLoad?.watchFiles) {
293296
for (const file of cachedLoad.watchFiles) {
294297
if (!isInternalAngularFile(file)) {
@@ -551,6 +554,7 @@ export class BundlerContext {
551554
}
552555

553556
if (invalid) {
557+
this.#invalidationEpoch++;
554558
this.#esbuildResult = undefined;
555559
}
556560

0 commit comments

Comments
 (0)