Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): resolve and load sourcemaps when …
Browse files Browse the repository at this point in the history
…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.

(cherry picked from commit 886cb31)
  • Loading branch information
alan-agius4 committed Oct 20, 2023
1 parent 6e8bf75 commit 651e319
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 231 deletions.
Expand Up @@ -12,6 +12,7 @@ import {
createBrowserCodeBundleOptions,
createBrowserPolyfillBundleOptions,
createServerCodeBundleOptions,
createServerPolyfillBundleOptions,
} from '../../tools/esbuild/application-code-bundle';
import { generateBudgetStats } from '../../tools/esbuild/budget-stats';
import { BuildOutputFileType, BundlerContext } from '../../tools/esbuild/bundler-context';
Expand Down Expand Up @@ -82,14 +83,14 @@ export async function executeBuild(
);

// Browser polyfills code
const polyfillBundleOptions = createBrowserPolyfillBundleOptions(
const browserPolyfillBundleOptions = createBrowserPolyfillBundleOptions(
options,
target,
codeBundleCache,
);
if (polyfillBundleOptions) {
if (browserPolyfillBundleOptions) {
bundlerContexts.push(
new BundlerContext(workspaceRoot, !!options.watch, polyfillBundleOptions),
new BundlerContext(workspaceRoot, !!options.watch, browserPolyfillBundleOptions),
);
}

Expand Down Expand Up @@ -122,18 +123,36 @@ export async function executeBuild(
}
}

// Server application code
// Skip server build when none of the features are enabled.
if (serverEntryPoint && (prerenderOptions || appShellOptions || ssrOptions)) {
const nodeTargets = getSupportedNodeTargets();
const nodeTargets = [...target, ...getSupportedNodeTargets()];
// Server application code
bundlerContexts.push(
new BundlerContext(
workspaceRoot,
!!options.watch,
createServerCodeBundleOptions(options, [...target, ...nodeTargets], codeBundleCache),
createServerCodeBundleOptions(options, nodeTargets, codeBundleCache),
() => false,
),
);

// Server polyfills code
const serverPolyfillBundleOptions = createServerPolyfillBundleOptions(
options,
nodeTargets,
codeBundleCache,
);

if (serverPolyfillBundleOptions) {
bundlerContexts.push(
new BundlerContext(
workspaceRoot,
!!options.watch,
serverPolyfillBundleOptions,
() => false,
),
);
}
}
}

Expand Down
Expand Up @@ -45,7 +45,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
const builderAbort = new AbortController();
const buildCount = await firstValueFrom(
harness.execute({ outputLogsOnFailure: false, signal: builderAbort.signal }).pipe(
timeout(20_000),
timeout(30_000),
concatMap(async ({ result, logs }, index) => {
switch (index) {
case 0:
Expand Down
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import remapping, { SourceMapInput } from '@ampproject/remapping';
import type { BuilderContext } from '@angular-devkit/architect';
import type { json, logging } from '@angular-devkit/core';
import type { Plugin } from 'esbuild';
Expand Down Expand Up @@ -70,6 +71,10 @@ export async function* serveWithVite(
// This is so instead of prerendering all the routes for every change, the page is "prerendered" when it is requested.
browserOptions.ssr = true;
browserOptions.prerender = false;

// https://nodejs.org/api/process.html#processsetsourcemapsenabledval
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(process as any).setSourceMapsEnabled(true);
}

// Set all packages as external to support Vite's prebundle caching
Expand Down Expand Up @@ -426,6 +431,31 @@ export async function setupServer(
};
},
configureServer(server) {
const originalssrTransform = server.ssrTransform;
server.ssrTransform = async (code, map, url, originalCode) => {
const result = await originalssrTransform(code, null, url, originalCode);
if (!result) {
return null;
}

let transformedCode = result.code;
if (result.map && map) {
transformedCode +=
`\n//# sourceMappingURL=` +
`data:application/json;base64,${Buffer.from(
JSON.stringify(
remapping([result.map as SourceMapInput, map as SourceMapInput], () => null),
),
).toString('base64')}`;
}

return {
...result,
map: null,
code: transformedCode,
};
};

// Assets and resources get handled first
server.middlewares.use(function angularAssetsMiddleware(req, res, next) {
if (req.url === undefined || res.writableEnded) {
Expand Down Expand Up @@ -501,9 +531,8 @@ export async function setupServer(
route: pathnameWithoutServePath(url, serverOptions),
serverContext: 'ssr',
loadBundle: (path: string) =>
server.ssrLoadModule(path.slice(1)) as ReturnType<
NonNullable<RenderOptions['loadBundle']>
>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
server.ssrLoadModule(path.slice(1)) as any,
// Files here are only needed for critical CSS inlining.
outputFiles: {},
// TODO: add support for critical css inlining.
Expand Down

0 comments on commit 651e319

Please sign in to comment.