Skip to content

Commit c832bac

Browse files
committed
fix(@angular/build): show error when Node.js built-ins are used during ng serve
This commit ensures consistent behavior between `ng build` and `ng serve`. Previously, `ng serve` did not display an error message when Node.js built-in modules were included in browser bundles. By default, Vite replaces Node.js built-ins with empty modules, which can lead to unexpected runtime issues. This update addresses the problem by surfacing clear error messages, providing better developer feedback and alignment between the two commands. Closes: #27425
1 parent f717a54 commit c832bac

File tree

3 files changed

+59
-21
lines changed

3 files changed

+59
-21
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,26 @@ function getDepOptimizationConfig({
803803
thirdPartySourcemaps: boolean;
804804
}): DepOptimizationConfig {
805805
const plugins: ViteEsBuildPlugin[] = [
806+
{
807+
name: 'angular-browser-node-built-in',
808+
setup(build) {
809+
// This namespace is configured by vite.
810+
// @see: https://github.com/vitejs/vite/blob/a1dd396da856401a12c921d0cd2c4e97cb63f1b5/packages/vite/src/node/optimizer/esbuildDepPlugin.ts#L109
811+
build.onLoad({ filter: /.*/, namespace: 'browser-external' }, (args) => {
812+
if (!isBuiltin(args.path)) {
813+
return;
814+
}
815+
816+
return {
817+
errors: [
818+
{
819+
text: `The package "${args.path}" wasn't found on the file system but is built into node.`,
820+
},
821+
],
822+
};
823+
});
824+
},
825+
},
806826
{
807827
name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}${
808828
thirdPartySourcemaps ? '-vendor-sourcemap' : ''
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import assert from 'node:assert';
2+
import { execAndWaitForOutputToMatch, ng } from '../../utils/process';
3+
import { writeFile } from '../../utils/fs';
4+
import { getGlobalVariable } from '../../utils/env';
5+
6+
export default async function () {
7+
assert(
8+
getGlobalVariable('argv')['esbuild'],
9+
'This test should not be called in the Webpack suite.',
10+
);
11+
12+
await ng('cache', 'clean');
13+
await ng('cache', 'on');
14+
15+
await writeFile('src/main.ts', `import '@angular-devkit/core/node';`);
16+
17+
const { stderr } = await execAndWaitForOutputToMatch('ng', ['serve'], /ERROR/, {
18+
CI: '0',
19+
NO_COLOR: 'true',
20+
});
21+
22+
assert.match(
23+
stderr,
24+
/The package "node:path" wasn't found on the file system but is built into node/,
25+
);
26+
}
Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,27 @@
11
import assert from 'node:assert';
2+
import { setTimeout } from 'node:timers/promises';
23
import { findFreePort } from '../../utils/network';
3-
import {
4-
execAndWaitForOutputToMatch,
5-
killAllProcesses,
6-
ng,
7-
waitForAnyProcessOutputToMatch,
8-
} from '../../utils/process';
4+
import { execAndWaitForOutputToMatch, killAllProcesses, ng } from '../../utils/process';
95

106
export default async function () {
117
await ng('cache', 'clean');
128
await ng('cache', 'on');
139

1410
const port = await findFreePort();
1511

16-
// Make sure serve is consistent with build
17-
await execAndWaitForOutputToMatch(
18-
'ng',
19-
['serve', '--port', `${port}`],
20-
/bundle generation complete/,
21-
// Use CI:0 to force caching
22-
{ DEBUG: 'vite:deps', CI: '0', NO_COLOR: 'true' },
23-
);
24-
25-
// Wait for vite to write to FS and stablize.
26-
await Promise.all([
27-
waitForAnyProcessOutputToMatch(/dependencies optimized/, 5000),
28-
fetch(`http://localhost:${port}/main.js`).then((r) =>
29-
assert(r.ok, `Expected 'response.ok' to be 'true'.`),
12+
const [, response] = await Promise.all([
13+
execAndWaitForOutputToMatch(
14+
'ng',
15+
['serve', '--port', `${port}`],
16+
/dependencies optimized/,
17+
// Use CI:0 to force caching
18+
{ DEBUG: 'vite:deps', CI: '0', NO_COLOR: 'true' },
3019
),
20+
setTimeout(4_000).then(() => fetch(`http://localhost:${port}/main.js`)),
3121
]);
3222

23+
assert(response.ok, `Expected 'response.ok' to be 'true'.`);
24+
3325
// Terminate the dev-server
3426
await killAllProcesses();
3527

@@ -38,6 +30,6 @@ export default async function () {
3830
['serve', '--port=0'],
3931
/Hash is consistent\. Skipping/,
4032
// Use CI:0 to force caching
41-
{ DEBUG: 'vite:deps', CI: '0' },
33+
{ DEBUG: 'vite:deps', CI: '0', NO_COLOR: 'true' },
4234
);
4335
}

0 commit comments

Comments
 (0)