From c066a1d3fb1d97421170b82d0d0b55719e5d1376 Mon Sep 17 00:00:00 2001 From: Nayeem Hossain Date: Sat, 17 Oct 2026 11:31:45 +0600 Subject: [PATCH 1/2] fix(@angular/build): invalidate Vite module cache for removed dev server output files Content-hashed chunks are renamed on every rebuild that changes them. The dev server removed the old names from its own file maps but left their transformed code and source maps in Vite's module graph, so memory grew with each save and the old chunk URLs kept serving stale code. Invalidate the Vite modules for removed files, as is already done for updated files. --- .../behavior/serve-renamed-chunks_spec.ts | 57 +++++++++++++++++++ .../src/builders/dev-server/vite/index.ts | 7 +++ 2 files changed, 64 insertions(+) create mode 100644 packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts new file mode 100644 index 000000000000..a20e99af1729 --- /dev/null +++ b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts @@ -0,0 +1,57 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { executeDevServer } from '../../index'; +import { describeServeBuilder } from '../jasmine-helpers'; +import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup'; + +describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => { + describe('Behavior: "Renamed chunks"', () => { + beforeEach(async () => { + setupTarget(harness); + + await harness.writeFile('src/lazy.ts', `export const value = 'first';`); + await harness.writeFile('src/main.ts', `import('./lazy').then((m) => console.log(m.value));`); + }); + + it('does not serve a lazy chunk that a rebuild renamed', async () => { + harness.useTarget('serve', { + ...BASE_OPTIONS, + watch: true, + }); + + let oldChunk = ''; + + await harness.executeWithCases([ + async ({ result }) => { + expect(result?.success).toBeTrue(); + + const main = await (await fetch(new URL('main.js', `${result?.baseUrl}`))).text(); + oldChunk = main.match(/chunk-[A-Z0-9]+\.js/)?.[0] ?? ''; + expect(oldChunk).not.toBe(''); + + // Request the chunk so that Vite transforms and caches it + const response = await fetch(new URL(oldChunk, `${result?.baseUrl}`)); + expect(response.status).toBe(200); + expect(await response.text()).toContain('first'); + + await harness.modifyFile('src/lazy.ts', (content) => content.replace('first', 'second')); + }, + async ({ result }) => { + expect(result?.success).toBeTrue(); + + const main = await (await fetch(new URL('main.js', `${result?.baseUrl}`))).text(); + expect(main).not.toContain(oldChunk); + + const response = await fetch(new URL(oldChunk, `${result?.baseUrl}`)); + expect(response.status).toBe(404); + }, + ]); + }); + }); +}); diff --git a/packages/angular/build/src/builders/dev-server/vite/index.ts b/packages/angular/build/src/builders/dev-server/vite/index.ts index 4800559a2171..7f36740cc3f5 100644 --- a/packages/angular/build/src/builders/dev-server/vite/index.ts +++ b/packages/angular/build/src/builders/dev-server/vite/index.ts @@ -288,6 +288,13 @@ export async function* serveWithVite( const filePath = '/' + normalizePath(removed.path); generatedFiles.delete(filePath); assetFiles.delete(filePath); + + // Release Vite's cached transform result for output files that no longer exist, + // such as content-hashed chunks that were renamed by the rebuild. + const moduleGraph = server.moduleGraph; + moduleGraph + .getModulesByFile(normalizePath(join(server.config.root, filePath))) + ?.forEach((m) => moduleGraph.invalidateModule(m)); } for (const file of result.files) { From f74bfdbd4926b92ad9dfacfa79a299a103404224 Mon Sep 17 00:00:00 2001 From: Nayeem Hossain Date: Sat, 17 Oct 2026 13:47:35 +0600 Subject: [PATCH 2/2] fixup! fix(@angular/build): invalidate Vite module cache for removed dev server output files --- .../dev-server/tests/behavior/serve-renamed-chunks_spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts index a20e99af1729..d43d47def463 100644 --- a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts +++ b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-renamed-chunks_spec.ts @@ -32,7 +32,7 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT expect(result?.success).toBeTrue(); const main = await (await fetch(new URL('main.js', `${result?.baseUrl}`))).text(); - oldChunk = main.match(/chunk-[A-Z0-9]+\.js/)?.[0] ?? ''; + oldChunk = main.match(/chunk-[a-zA-Z0-9]+\.js/)?.[0] ?? ''; expect(oldChunk).not.toBe(''); // Request the chunk so that Vite transforms and caches it