Skip to content

Commit 9bd8fab

Browse files
committed
fix(@angular/build): invalidate cached SCSS errors when source files are corrected
Normalize the file path in MemoryLoadResultCache.invalidate() to match how watch file paths are stored in put(). Without normalization, paths produced by fileURLToPath() or path.join() during Sass error reporting may use different separators than the normalized paths stored as keys in #fileDependencies. This mismatch caused the invalidation lookup to fail, leaving stale error results in the load cache after fixing an SCSS partial. The fix ensures the path is normalized before the lookup so the correct cache entries are cleared and the next rebuild picks up the fix. Fixes #32744
1 parent f1ed025 commit 9bd8fab

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

packages/angular/build/src/builders/application/tests/behavior/rebuild-global_styles_spec.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,69 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
8787
);
8888
});
8989

90+
it('recovers from error in SCSS partial after fix on rebuild using @use', async () => {
91+
harness.useTarget('build', {
92+
...BASE_OPTIONS,
93+
watch: true,
94+
styles: ['src/styles.scss'],
95+
});
96+
97+
await harness.writeFile('src/styles.scss', "@use './variables' as v;\nh1 { color: v.$primary; }");
98+
await harness.writeFile('src/variables.scss', '$primary: aqua;');
99+
100+
await harness.executeWithCases(
101+
[
102+
async ({ result }) => {
103+
expect(result?.success).toBe(true);
104+
harness.expectFile('dist/browser/styles.css').content.toContain('color: aqua');
105+
106+
// Introduce a syntax error in the imported partial
107+
await harness.writeFile('src/variables.scss', '$primary: aqua\n$broken;');
108+
},
109+
async ({ result }) => {
110+
expect(result?.success).toBe(false);
111+
112+
// Fix the partial — the cached error should be cleared
113+
await harness.writeFile('src/variables.scss', '$primary: blue;');
114+
},
115+
({ result }) => {
116+
expect(result?.success).toBe(true);
117+
harness.expectFile('dist/browser/styles.css').content.not.toContain('color: aqua');
118+
harness.expectFile('dist/browser/styles.css').content.toContain('color: blue');
119+
},
120+
],
121+
{ outputLogsOnFailure: false },
122+
);
123+
});
124+
125+
it('recovers from error in SCSS partial after fix on initial build using @use', async () => {
126+
harness.useTarget('build', {
127+
...BASE_OPTIONS,
128+
watch: true,
129+
styles: ['src/styles.scss'],
130+
});
131+
132+
await harness.writeFile('src/styles.scss', "@use './variables' as v;\nh1 { color: v.$primary; }");
133+
// Start with an error in the partial
134+
await harness.writeFile('src/variables.scss', '$primary: aqua\n$broken;');
135+
136+
await harness.executeWithCases(
137+
[
138+
async ({ result }) => {
139+
expect(result?.success).toBe(false);
140+
141+
// Fix the partial
142+
await harness.writeFile('src/variables.scss', '$primary: aqua;');
143+
},
144+
({ result }) => {
145+
expect(result?.success).toBe(true);
146+
harness.expectFile('dist/browser/styles.css').content.toContain('color: aqua');
147+
},
148+
],
149+
{ outputLogsOnFailure: false },
150+
);
151+
});
152+
90153
it('rebuilds dependent Sass stylesheets after error on initial build from import', async () => {
91154
harness.useTarget('build', {
92155
...BASE_OPTIONS,

packages/angular/build/src/tools/esbuild/load-result-cache.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,13 @@ export class MemoryLoadResultCache implements LoadResultCache {
7070
}
7171

7272
invalidate(path: string): boolean {
73-
const affectedPaths = this.#fileDependencies.get(path);
73+
// Normalize the path to match how watch file paths are stored in `put()`.
74+
// Without normalization, paths produced by `fileURLToPath()` or `path.join()`
75+
// during error reporting may use different separators than the normalized paths
76+
// stored as keys in `#fileDependencies`, causing the lookup to fail and leaving
77+
// stale error results in the cache after the source file is corrected.
78+
const normalizedPath = normalize(path);
79+
const affectedPaths = this.#fileDependencies.get(normalizedPath);
7480
let found = false;
7581

7682
if (affectedPaths) {
@@ -79,7 +85,7 @@ export class MemoryLoadResultCache implements LoadResultCache {
7985
found = true;
8086
}
8187
}
82-
this.#fileDependencies.delete(path);
88+
this.#fileDependencies.delete(normalizedPath);
8389
}
8490

8591
return found;

0 commit comments

Comments
 (0)