Skip to content

Commit b24089e

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular/build): ensure full rebuild after initial error build in watch mode
If an initial build of an application results in an error during watch mode (including `ng serve`), the following non-error rebuild will now always be a full build result. This ensures that all new files are available for later incremental build result updates.
1 parent 5c13601 commit b24089e

File tree

4 files changed

+82
-1
lines changed

4 files changed

+82
-1
lines changed

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ export async function* runEsBuildBuildAction(
152152
return;
153153
}
154154

155+
// Used to force a full result on next rebuild if there were initial errors.
156+
// This ensures at least one full result is emitted.
157+
let hasInitialErrors = result.errors.length > 0;
158+
155159
// Wait for changes and rebuild as needed
156160
const currentWatchFiles = new Set(result.watchFiles);
157161
try {
@@ -201,10 +205,13 @@ export async function* runEsBuildBuildAction(
201205
result,
202206
outputOptions,
203207
changes,
204-
incrementalResults ? rebuildState : undefined,
208+
incrementalResults && !hasInitialErrors ? rebuildState : undefined,
205209
)) {
206210
yield outputResult;
207211
}
212+
213+
// Clear initial build errors flag if no errors are now present
214+
hasInitialErrors &&= result.errors.length > 0;
208215
}
209216
} finally {
210217
// Stop the watcher and cleanup incremental rebuild state
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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.dev/license
7+
*/
8+
9+
import { concatMap, count, take, timeout } from 'rxjs';
10+
import { executeDevServer } from '../../index';
11+
import { describeServeBuilder } from '../jasmine-helpers';
12+
import { BASE_OPTIONS, BUILD_TIMEOUT, DEV_SERVER_BUILDER_INFO } from '../setup';
13+
import { logging } from '@angular-devkit/core';
14+
15+
describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
16+
describe('Behavior: "Rebuild Error Detection"', () => {
17+
beforeEach(() => {
18+
setupTarget(harness);
19+
});
20+
21+
it('Emits full build result with incremental enabled and initial build has errors', async () => {
22+
harness.useTarget('serve', {
23+
...BASE_OPTIONS,
24+
watch: true,
25+
});
26+
27+
// Missing ending `>` on the div will cause an error
28+
await harness.appendToFile('src/app/app.component.html', '<div>Hello, world!</div');
29+
30+
const buildCount = await harness
31+
.execute({ outputLogsOnFailure: false })
32+
.pipe(
33+
timeout(BUILD_TIMEOUT),
34+
concatMap(async ({ result, logs }, index) => {
35+
switch (index) {
36+
case 0:
37+
expect(result?.success).toBeFalse();
38+
debugger;
39+
expect(logs).toContain(
40+
jasmine.objectContaining<logging.LogEntry>({
41+
message: jasmine.stringMatching('Unexpected character "EOF"'),
42+
}),
43+
);
44+
45+
await harness.appendToFile('src/app/app.component.html', '>');
46+
47+
break;
48+
case 1:
49+
expect(result?.success).toBeTrue();
50+
expect(logs).not.toContain(
51+
jasmine.objectContaining<logging.LogEntry>({
52+
message: jasmine.stringMatching('Unexpected character "EOF"'),
53+
}),
54+
);
55+
break;
56+
}
57+
}),
58+
take(2),
59+
count(),
60+
)
61+
.toPromise();
62+
63+
expect(buildCount).toBe(2);
64+
});
65+
});
66+
});

packages/angular/build/src/builders/dev-server/vite-server.ts

+2
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ export async function* serveWithVite(
213213
},
214214
});
215215
}
216+
217+
yield { baseUrl: '', success: false };
216218
continue;
217219
}
218220
// Clear existing error overlay on successful result

tests/legacy-cli/e2e/tests/build/jit-ngmodule.ts

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ export default async function () {
3232
};
3333
}
3434

35+
// Remove bundle budgets due to the increased size from JIT
36+
build.configurations.production = {
37+
...build.configurations.production,
38+
budgets: undefined,
39+
};
40+
3541
build.options.aot = false;
3642
});
3743
// Test it works

0 commit comments

Comments
 (0)