diff --git a/packages/angular/build/src/builders/application/build-action.ts b/packages/angular/build/src/builders/application/build-action.ts index eaae50848a48..8d06213833cd 100644 --- a/packages/angular/build/src/builders/application/build-action.ts +++ b/packages/angular/build/src/builders/application/build-action.ts @@ -314,11 +314,11 @@ function* emitOutputResults( } // Initially assume all previous assets files have been removed - const removedAssetFiles = new Map(previousAssetsInfo); + const removedAssetFiles = new Set(previousAssetsInfo); for (const { source, destination } of assetFiles) { - removedAssetFiles.delete(source); + removedAssetFiles.delete(destination); - if (!previousAssetsInfo.has(source)) { + if (!previousAssetsInfo.has(destination)) { incrementalResult.added.push(destination); incrementalResult.background = false; } else if (changes.modified.has(source)) { @@ -353,7 +353,7 @@ function* emitOutputResults( path, type, })), - ...Array.from(removedAssetFiles.values(), (path) => ({ + ...Array.from(removedAssetFiles, (path) => ({ path, type: BuildOutputFileType.Browser, })), diff --git a/packages/angular/build/src/builders/application/tests/behavior/rebuild-assets_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/rebuild-assets_spec.ts index 7bfcca94d242..1c36f64c1892 100644 --- a/packages/angular/build/src/builders/application/tests/behavior/rebuild-assets_spec.ts +++ b/packages/angular/build/src/builders/application/tests/behavior/rebuild-assets_spec.ts @@ -76,5 +76,39 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { }, ]); }); + + it('remove all outputs of a deleted asset copied to multiple destinations', async () => { + harness.useTarget('build', { + ...BASE_OPTIONS, + assets: [ + { + glob: '**/*', + input: 'public', + }, + { + glob: '**/*', + input: 'public', + output: 'static', + }, + ], + watch: true, + }); + + await harness.executeWithCases([ + async ({ result }) => { + expect(result?.success).toBeTrue(); + harness.expectFile('dist/browser/asset.txt').toExist(); + harness.expectFile('dist/browser/static/asset.txt').toExist(); + + await harness.removeFile('public/asset.txt'); + }, + + ({ result }) => { + expect(result?.success).toBeTrue(); + harness.expectFile('dist/browser/asset.txt').toNotExist(); + harness.expectFile('dist/browser/static/asset.txt').toNotExist(); + }, + ]); + }); }); }); diff --git a/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts b/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts index 991ad988662b..3777d2835f5a 100644 --- a/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts +++ b/packages/angular/build/src/tools/esbuild/bundler-execution-result.ts @@ -31,7 +31,7 @@ export interface RebuildState { string, { hash: string; type: BuildOutputFileType; path: string } >; - previousAssetsInfo: ReadonlyMap; + previousAssetsInfo: ReadonlySet; templateUpdates?: Map; } @@ -176,9 +176,8 @@ export class ExecutionResult { previousOutputInfo: new Map( this.outputFiles.map(({ path, hash, type }) => [`${type}:${path}`, { hash, type, path }]), ), - previousAssetsInfo: new Map( - this.assetFiles.map(({ source, destination }) => [source, destination]), - ), + // Tracked by destination since one source can be copied to several outputs (e.g. per locale). + previousAssetsInfo: new Set(this.assetFiles.map(({ destination }) => destination)), templateUpdates: this.templateUpdates, }; }