Skip to content

Commit 8cff5e7

Browse files
committed
fix(@angular/build): ensure recreated files are watched
When deleting a file and then recreating a file that is again referenced in the application, the watch state will now be correctly synchronized. Previously the application builder would consider the previously deleted file as still watched which would prevent it from being watched again. This situation can happen when switching source control branches while a build is active watching.
1 parent f724866 commit 8cff5e7

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

packages/angular/build/src/builders/application/build-action.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ export async function* runEsBuildBuildAction(
180180
logger.info(changes.toDebugString());
181181
}
182182

183+
// Clear removed files from current watch files
184+
changes.removed.forEach((removedPath) => currentWatchFiles.delete(removedPath));
185+
183186
result = await withProgress('Changes detected. Rebuilding...', () =>
184187
action(result.createRebuildState(changes)),
185188
);
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { concatMap, count, take, timeout } from 'rxjs';
10+
import { buildApplication } from '../../index';
11+
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
12+
13+
/**
14+
* Maximum time in milliseconds for single build/rebuild
15+
* This accounts for CI variability.
16+
*/
17+
export const BUILD_TIMEOUT = 30_000;
18+
19+
describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
20+
describe('Behavior: "Rebuild updates in general cases"', () => {
21+
it('detects changes after a file was deleted and recreated', async () => {
22+
harness.useTarget('build', {
23+
...BASE_OPTIONS,
24+
watch: true,
25+
});
26+
27+
const fileAContent = `
28+
console.log('FILE-A');
29+
export {};
30+
`;
31+
32+
// Create a file and add to application
33+
await harness.writeFile('src/app/file-a.ts', fileAContent);
34+
await harness.writeFile(
35+
'src/app/app.component.ts',
36+
`
37+
import { Component } from '@angular/core';
38+
import './file-a';
39+
@Component({
40+
selector: 'app-root',
41+
template: 'App component',
42+
})
43+
export class AppComponent { }
44+
`,
45+
);
46+
47+
const buildCount = await harness
48+
.execute({ outputLogsOnFailure: false })
49+
.pipe(
50+
timeout(BUILD_TIMEOUT),
51+
concatMap(async ({ result, logs }, index) => {
52+
switch (index) {
53+
case 0:
54+
expect(result?.success).toBeTrue();
55+
harness.expectFile('dist/browser/main.js').content.toContain('FILE-A');
56+
57+
// Delete the imported file
58+
await harness.removeFile('src/app/file-a.ts');
59+
60+
break;
61+
case 1:
62+
// Should fail from missing import
63+
expect(result?.success).toBeFalse();
64+
65+
// Remove the failing import
66+
await harness.modifyFile('src/app/app.component.ts', (content) =>
67+
content.replace(`import './file-a';`, ''),
68+
);
69+
70+
break;
71+
case 2:
72+
expect(result?.success).toBeTrue();
73+
74+
harness.expectFile('dist/browser/main.js').content.not.toContain('FILE-A');
75+
76+
// Recreate the file and the import
77+
await harness.writeFile('src/app/file-a.ts', fileAContent);
78+
await harness.modifyFile(
79+
'src/app/app.component.ts',
80+
(content) => `import './file-a';\n` + content,
81+
);
82+
83+
break;
84+
case 3:
85+
expect(result?.success).toBeTrue();
86+
87+
harness.expectFile('dist/browser/main.js').content.toContain('FILE-A');
88+
89+
// Change the imported file
90+
await harness.modifyFile('src/app/file-a.ts', (content) =>
91+
content.replace('FILE-A', 'FILE-B'),
92+
);
93+
94+
break;
95+
case 4:
96+
expect(result?.success).toBeTrue();
97+
98+
harness.expectFile('dist/browser/main.js').content.toContain('FILE-B');
99+
100+
break;
101+
}
102+
}),
103+
take(5),
104+
count(),
105+
)
106+
.toPromise();
107+
108+
expect(buildCount).toBe(5);
109+
});
110+
});
111+
});

0 commit comments

Comments
 (0)