diff --git a/napi/angular-compiler/test/hmr-hot-update.test.ts b/napi/angular-compiler/test/hmr-hot-update.test.ts
index 67de01cf5..d38f3994d 100644
--- a/napi/angular-compiler/test/hmr-hot-update.test.ts
+++ b/napi/angular-compiler/test/hmr-hot-update.test.ts
@@ -3724,3 +3724,46 @@ describe('@ng/component endpoint resolves the styles per class', () => {
expect(body, 'a definitively styleless class must still be cleared').toContain('styles: []')
})
})
+
+describe('a template edited while its component fails to compile', () => {
+ it('is compiled from the edited file once the component compiles again', async () => {
+ const dir = join(tempDir, 'src', 'stale')
+ mkdirSync(dir, { recursive: true })
+ const template = join(dir, 'stale.component.html')
+ const component = join(dir, 'stale.component.ts')
+ const source = `
+ import { Component } from '@angular/core';
+
+ @Component({ selector: 'app-stale', templateUrl: './stale.component.html' })
+ export class StaleComponent {}
+ `
+ writeFileSync(component, source)
+ writeFileSync(template, '
Before
')
+
+ const plugin = getAngularPlugin()
+ const server = await setupPluginWithServer(plugin)
+ const transform = async (code: string) => {
+ if (!plugin.transform || typeof plugin.transform === 'function') {
+ throw new Error('Expected plugin transform handler')
+ }
+ return (await plugin.transform.handler.call(
+ { error() {}, warn() {}, addWatchFile() {} } as any,
+ code,
+ component,
+ )) as { code: string } | undefined
+ }
+
+ expect((await transform(source))?.code).toContain('Before')
+
+ // the component stops compiling, and its template is edited meanwhile
+ await transform(
+ source.replace('export class StaleComponent {}', 'export class StaleComponent { x = ; }'),
+ ).catch(() => {})
+ writeFileSync(template, 'After
')
+ await callHandleHotUpdate(plugin, createMockHmrContext(template, [], server))
+
+ const fixed = (await transform(source))?.code
+ expect(fixed).toContain('After')
+ expect(fixed).not.toContain('Before')
+ })
+})
diff --git a/napi/angular-compiler/vite-plugin/index.ts b/napi/angular-compiler/vite-plugin/index.ts
index e022d15b7..4f26b15c6 100644
--- a/napi/angular-compiler/vite-plugin/index.ts
+++ b/napi/angular-compiler/vite-plugin/index.ts
@@ -1286,6 +1286,13 @@ export function angular(options: PluginOptions = {}): Plugin[] {
// anything below can preprocess one (see `compiledStyles`).
startStyleCycle()
+ // The file's cached contents are stale, whatever its role now. A
+ // template edited while its component fails to compile is not
+ // registered as the component's at that moment (the failed transform
+ // found no templateUrl), so the branches below would leave its old
+ // contents cached for the component's next transform.
+ resourceCache.delete(normalizePath(ctx.file))
+
if (!pluginOptions.liveReload) return
debugHmr('handleHotUpdate file=%s', ctx.file)