Skip to content

Commit 886cb31

Browse files
committed
fix(@angular-devkit/build-angular): resolve and load sourcemaps when using vite dev server with prerendering and ssr
This commit improves the printed error messages when using Vite with SSR and/or SSG by doing a couple of things. - Enabling resolving and loading of sourcemap in Node.js by using `process.setSourceMapsEnabled`. See https://nodejs.org/api/process.html#processsetsourcemapsenabledval - Amends Vite's `ssrTransform` method to remap the sourcemaps and inlines them. - Enables `__zone_symbol__DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION` Zone.js flag to output cleaner stacktraces. To enable, the above mentioned zone.js flag we had to create a server polyfill bundle as otherwise in some cases, zone.js would have been split and loaded before the flag. **Before** ``` ERROR ReferenceError: window is not defined at new _AppComponent (/main.server.mjs:36:19) at NodeInjectorFactory.AppComponent_Factory [as factory] (/main.server.mjs:42:12) at getNodeInjectable (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:4277:44) at createRootComponent (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14399:35) at ComponentFactory.create (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14263:25) at ApplicationRef.bootstrap (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:31122:42) at file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:30644:32 at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:368:26) at Object.onInvoke (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:11202:33) at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:367:52) ERROR Error: Uncaught (in promise): ReferenceError: window is not defined ReferenceError: window is not defined at new _AppComponent (/main.server.mjs:36:19) at NodeInjectorFactory.AppComponent_Factory [as factory] (/main.server.mjs:42:12) at getNodeInjectable (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:4277:44) at createRootComponent (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14399:35) at ComponentFactory.create (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14263:25) at ApplicationRef.bootstrap (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:31122:42) at file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:30644:32 at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:368:26) at Object.onInvoke (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:11202:33) at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:367:52) at resolvePromise (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:1124:31) at /usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:1195:17 at _ZoneDelegate.invokeTask (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:402:31) at AsyncStackTaggingZoneSpec.onInvokeTask (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:10879:28) at _ZoneDelegate.invokeTask (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:401:60) at Object.onInvokeTask (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:11189:33) at _ZoneDelegate.invokeTask (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:401:60) at Zone.runTask (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:173:47) at drainMicroTaskQueue (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:581:35) { rejection: ReferenceError: window is not defined at new _AppComponent (/main.server.mjs:36:19) at NodeInjectorFactory.AppComponent_Factory [as factory] (/main.server.mjs:42:12) at getNodeInjectable (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:4277:44) at createRootComponent (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14399:35) at ComponentFactory.create (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14263:25) at ApplicationRef.bootstrap (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:31122:42) at file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:30644:32 at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:368:26) at Object.onInvoke (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:11202:33) at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:367:52), promise: ZoneAwarePromise [Promise] { __zone_symbol__state: 0, __zone_symbol__value: ReferenceError: window is not defined at new _AppComponent (/main.server.mjs:36:19) at NodeInjectorFactory.AppComponent_Factory [as factory] (/main.server.mjs:42:12) at getNodeInjectable (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:4277:44) at createRootComponent (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14399:35) at ComponentFactory.create (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14263:25) at ApplicationRef.bootstrap (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:31122:42) at file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:30644:32 at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:368:26) at Object.onInvoke (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:11202:33) at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:367:52) }, zone: <ref *1> Zone { _parent: Zone { _parent: [Zone], _name: 'asyncStackTagging for Angular', _properties: {}, _zoneDelegate: [_ZoneDelegate] }, _name: 'angular', _properties: { isAngularZone: true }, _zoneDelegate: <ref *2> _ZoneDelegate { _taskCounts: [Object], zone: [Circular *1], _parentDelegate: [_ZoneDelegate], _forkZS: null, _forkDlgt: null, _forkCurrZone: null, _interceptZS: null, _interceptDlgt: null, _interceptCurrZone: null, _invokeZS: [Object], _invokeDlgt: [_ZoneDelegate], _invokeCurrZone: [Circular *1], _handleErrorZS: [Object], _handleErrorDlgt: [_ZoneDelegate], _handleErrorCurrZone: [Circular *1], _scheduleTaskZS: [Object], _scheduleTaskDlgt: [_ZoneDelegate], 8:23:50 AM [vite] Internal server error: window is not defined at new _AppComponent (/main.server.mjs:36:19) at NodeInjectorFactory.AppComponent_Factory [as factory] (/main.server.mjs:42:12) at getNodeInjectable (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:4277:44) at createRootComponent (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14399:35) at ComponentFactory.create (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:14263:25) at ApplicationRef.bootstrap (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:31122:42) at file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:30644:32 at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:368:26) at Object.onInvoke (file:///usr/local/xxx/cli-reproduction/test-ssr-/node_modules/@angular/core/fesm2022/core.mjs:11202:33) at _ZoneDelegate.invoke (/usr/local/xxx/cli-reproduction/test-ssr-/node_modules/zone.js/fesm2015/zone-node.js:367:52) ``` **After** ``` ERROR ReferenceError: window is not defined at console (/src/app/app.component.ts:17:3) at NodeInjectorFactory.AppComponent_Factory (/src/app/app.component.ts:12:26) at getNodeInjectable (/usr/local/xxx/git/packages/core/src/render3/di.ts:659:38) at createRootComponent (/usr/local/xxx/git/packages/core/src/render3/component_ref.ts:464:31) at ComponentFactory.create (/usr/local/xxx/git/packages/core/src/render3/component_ref.ts:288:19) at ApplicationRef.bootstrap (/usr/local/xxx/git/packages/core/src/application_ref.ts:1017:38) at <anonymous> (/usr/local/xxx/git/packages/core/src/application_ref.ts:287:20) at _ZoneDelegate.invoke (/node_modules/zone.js/fesm2015/zone-node.js:370:40) at Object.onInvoke (/usr/local/xxx/git/packages/core/src/zone/ng_zone.ts:443:29) at _ZoneDelegate.invoke (/node_modules/zone.js/fesm2015/zone-node.js:370:40) ERROR ReferenceError: window is not defined at console (/src/app/app.component.ts:17:3) at NodeInjectorFactory.AppComponent_Factory (/src/app/app.component.ts:12:26) at getNodeInjectable (/usr/local/xxx/git/packages/core/src/render3/di.ts:659:38) at createRootComponent (/usr/local/xxx/git/packages/core/src/render3/component_ref.ts:464:31) at ComponentFactory.create (/usr/local/xxx/git/packages/core/src/render3/component_ref.ts:288:19) at ApplicationRef.bootstrap (/usr/local/xxx/git/packages/core/src/application_ref.ts:1017:38) at <anonymous> (/usr/local/xxx/git/packages/core/src/application_ref.ts:287:20) at _ZoneDelegate.invoke (/node_modules/zone.js/fesm2015/zone-node.js:370:40) at Object.onInvoke (/usr/local/xxx/git/packages/core/src/zone/ng_zone.ts:443:29) at _ZoneDelegate.invoke (/node_modules/zone.js/fesm2015/zone-node.js:370:40) 8:13:37 AM [vite] Internal server error: window is not defined at console (/src/app/app.component.ts:17:3) at NodeInjectorFactory.AppComponent_Factory (/src/app/app.component.ts:12:26) at getNodeInjectable (/usr/local/xxx/git/packages/core/src/render3/di.ts:659:38) at createRootComponent (/usr/local/xxx/git/packages/core/src/render3/component_ref.ts:464:31) at ComponentFactory.create (/usr/local/xxx/git/packages/core/src/render3/component_ref.ts:288:19) at ApplicationRef.bootstrap (/usr/local/xxx/git/packages/core/src/application_ref.ts:1017:38) at <anonymous> (/usr/local/xxx/git/packages/core/src/application_ref.ts:287:20) at _ZoneDelegate.invoke (/node_modules/zone.js/fesm2015/zone-node.js:370:40) at Object.onInvoke (/usr/local/xxx/git/packages/core/src/zone/ng_zone.ts:443:29) at _ZoneDelegate.invoke (/node_modules/zone.js/fesm2015/zone-node.js:370:40) ``` **Note:** in the above case the error is printed 3x, this will be addressed in the future.
1 parent 84db78d commit 886cb31

File tree

9 files changed

+382
-231
lines changed

9 files changed

+382
-231
lines changed

packages/angular_devkit/build_angular/src/builders/application/execute-build.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
createBrowserCodeBundleOptions,
1313
createBrowserPolyfillBundleOptions,
1414
createServerCodeBundleOptions,
15+
createServerPolyfillBundleOptions,
1516
} from '../../tools/esbuild/application-code-bundle';
1617
import { generateBudgetStats } from '../../tools/esbuild/budget-stats';
1718
import { BuildOutputFileType, BundlerContext } from '../../tools/esbuild/bundler-context';
@@ -82,14 +83,14 @@ export async function executeBuild(
8283
);
8384

8485
// Browser polyfills code
85-
const polyfillBundleOptions = createBrowserPolyfillBundleOptions(
86+
const browserPolyfillBundleOptions = createBrowserPolyfillBundleOptions(
8687
options,
8788
target,
8889
codeBundleCache,
8990
);
90-
if (polyfillBundleOptions) {
91+
if (browserPolyfillBundleOptions) {
9192
bundlerContexts.push(
92-
new BundlerContext(workspaceRoot, !!options.watch, polyfillBundleOptions),
93+
new BundlerContext(workspaceRoot, !!options.watch, browserPolyfillBundleOptions),
9394
);
9495
}
9596

@@ -122,18 +123,36 @@ export async function executeBuild(
122123
}
123124
}
124125

125-
// Server application code
126126
// Skip server build when none of the features are enabled.
127127
if (serverEntryPoint && (prerenderOptions || appShellOptions || ssrOptions)) {
128-
const nodeTargets = getSupportedNodeTargets();
128+
const nodeTargets = [...target, ...getSupportedNodeTargets()];
129+
// Server application code
129130
bundlerContexts.push(
130131
new BundlerContext(
131132
workspaceRoot,
132133
!!options.watch,
133-
createServerCodeBundleOptions(options, [...target, ...nodeTargets], codeBundleCache),
134+
createServerCodeBundleOptions(options, nodeTargets, codeBundleCache),
134135
() => false,
135136
),
136137
);
138+
139+
// Server polyfills code
140+
const serverPolyfillBundleOptions = createServerPolyfillBundleOptions(
141+
options,
142+
nodeTargets,
143+
codeBundleCache,
144+
);
145+
146+
if (serverPolyfillBundleOptions) {
147+
bundlerContexts.push(
148+
new BundlerContext(
149+
workspaceRoot,
150+
!!options.watch,
151+
serverPolyfillBundleOptions,
152+
() => false,
153+
),
154+
);
155+
}
137156
}
138157
}
139158

packages/angular_devkit/build_angular/src/builders/application/tests/behavior/typescript-rebuild-lazy_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
4545
const builderAbort = new AbortController();
4646
const buildCount = await firstValueFrom(
4747
harness.execute({ outputLogsOnFailure: false, signal: builderAbort.signal }).pipe(
48-
timeout(20_000),
48+
timeout(30_000),
4949
concatMap(async ({ result, logs }, index) => {
5050
switch (index) {
5151
case 0:

packages/angular_devkit/build_angular/src/builders/dev-server/vite-server.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import remapping, { SourceMapInput } from '@ampproject/remapping';
910
import type { BuilderContext } from '@angular-devkit/architect';
1011
import type { json, logging } from '@angular-devkit/core';
1112
import type { Plugin } from 'esbuild';
@@ -70,6 +71,10 @@ export async function* serveWithVite(
7071
// This is so instead of prerendering all the routes for every change, the page is "prerendered" when it is requested.
7172
browserOptions.ssr = true;
7273
browserOptions.prerender = false;
74+
75+
// https://nodejs.org/api/process.html#processsetsourcemapsenabledval
76+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
77+
(process as any).setSourceMapsEnabled(true);
7378
}
7479

7580
// Set all packages as external to support Vite's prebundle caching
@@ -426,6 +431,31 @@ export async function setupServer(
426431
};
427432
},
428433
configureServer(server) {
434+
const originalssrTransform = server.ssrTransform;
435+
server.ssrTransform = async (code, map, url, originalCode) => {
436+
const result = await originalssrTransform(code, null, url, originalCode);
437+
if (!result) {
438+
return null;
439+
}
440+
441+
let transformedCode = result.code;
442+
if (result.map && map) {
443+
transformedCode +=
444+
`\n//# sourceMappingURL=` +
445+
`data:application/json;base64,${Buffer.from(
446+
JSON.stringify(
447+
remapping([result.map as SourceMapInput, map as SourceMapInput], () => null),
448+
),
449+
).toString('base64')}`;
450+
}
451+
452+
return {
453+
...result,
454+
map: null,
455+
code: transformedCode,
456+
};
457+
};
458+
429459
// Assets and resources get handled first
430460
server.middlewares.use(function angularAssetsMiddleware(req, res, next) {
431461
if (req.url === undefined || res.writableEnded) {
@@ -501,9 +531,8 @@ export async function setupServer(
501531
route: pathnameWithoutServePath(url, serverOptions),
502532
serverContext: 'ssr',
503533
loadBundle: (path: string) =>
504-
server.ssrLoadModule(path.slice(1)) as ReturnType<
505-
NonNullable<RenderOptions['loadBundle']>
506-
>,
534+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
535+
server.ssrLoadModule(path.slice(1)) as any,
507536
// Files here are only needed for critical CSS inlining.
508537
outputFiles: {},
509538
// TODO: add support for critical css inlining.

0 commit comments

Comments
 (0)