From f9aec9005ae2136076660a026e178ee4206bc5a4 Mon Sep 17 00:00:00 2001 From: Will Binns-Smith Date: Tue, 5 Mar 2024 20:40:26 -0800 Subject: [PATCH] Turbopack: Trace edge runtime app render errors through source maps (#62901) Similar to #62611, this implements error stack translation for edge runtime app render errors. Test Plan: `TURBOPACK=1 pnpm test-dev test/development/app-render-error-log/app-render-error-log.test.ts` Closes PACK-2665 --- .../src/server/app-render/create-error-handler.tsx | 14 ++++++++------ packages/next/src/server/dev/next-dev-server.ts | 1 + packages/next/src/server/next-server.ts | 2 ++ packages/next/src/server/web/sandbox/context.ts | 7 +++++++ packages/next/src/server/web/sandbox/sandbox.ts | 3 +++ .../app-render-error-log.test.ts | 2 +- test/turbopack-tests-manifest.json | 5 ++--- 7 files changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/next/src/server/app-render/create-error-handler.tsx b/packages/next/src/server/app-render/create-error-handler.tsx index bbbfc4e04ebee..3b4d93f66fa95 100644 --- a/packages/next/src/server/app-render/create-error-handler.tsx +++ b/packages/next/src/server/app-render/create-error-handler.tsx @@ -4,6 +4,10 @@ import { SpanStatusCode, getTracer } from '../lib/trace/tracer' import { isAbortError } from '../pipe-readable' import { isDynamicUsageError } from '../../export/helpers/is-dynamic-usage-error' +declare global { + var __next_log_error__: undefined | ((err: unknown) => void) +} + export type ErrorHandler = ( err: unknown, errorInfo: unknown @@ -98,12 +102,10 @@ export function createErrorHandler({ errorLogger(err).catch(() => {}) } else { // The error logger is currently not provided in the edge runtime. - // Use `log-app-dir-error` instead. - // It won't log the source code, but the error will be more useful. - if (process.env.NODE_ENV !== 'production') { - const { logAppDirError } = - require('../dev/log-app-dir-error') as typeof import('../dev/log-app-dir-error') - logAppDirError(err) + // Use the exposed `__next_log_error__` instead. + // This will trace error traces to the original source code. + if (typeof __next_log_error__ === 'function') { + __next_log_error__(err) } else { console.error(err) } diff --git a/packages/next/src/server/dev/next-dev-server.ts b/packages/next/src/server/dev/next-dev-server.ts index f3a698ed20742..fd72464d33e54 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -432,6 +432,7 @@ export default class DevServer extends Server { try { return super.runEdgeFunction({ ...params, + onError: (err) => this.logErrorWithOriginalStack(err, 'app-dir'), onWarning: (warn) => { this.logErrorWithOriginalStack(warn, 'warning') }, diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 604f2d350385c..ab0c8434b0aeb 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -1815,6 +1815,7 @@ export default class NextNodeServer extends BaseServer { page: string appPaths: string[] | null match?: RouteMatch + onError?: (err: unknown) => void onWarning?: (warning: Error) => void }): Promise { if (process.env.NEXT_MINIMAL) { @@ -1890,6 +1891,7 @@ export default class NextNodeServer extends BaseServer { ), }, useCache: true, + onError: params.onError, onWarning: params.onWarning, incrementalCache: (globalThis as any).__incrementalCache || diff --git a/packages/next/src/server/web/sandbox/context.ts b/packages/next/src/server/web/sandbox/context.ts index 4bbb7ad2a1423..1dc56c209ebf8 100644 --- a/packages/next/src/server/web/sandbox/context.ts +++ b/packages/next/src/server/web/sandbox/context.ts @@ -264,6 +264,12 @@ async function createModuleContext(options: ModuleContextOptions) { }, }) + if (process.env.NODE_ENV !== 'production') { + context.__next_log_error__ = function (err: unknown) { + options.onError(err) + } + } + context.__next_eval__ = function __next_eval__(fn: Function) { const key = fn.toString() if (!warnedEvals.has(key)) { @@ -458,6 +464,7 @@ Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), interface ModuleContextOptions { moduleName: string + onError: (err: unknown) => void onWarning: (warn: Error) => void useCache: boolean distDir: string diff --git a/packages/next/src/server/web/sandbox/sandbox.ts b/packages/next/src/server/web/sandbox/sandbox.ts index 9b286fd2181ff..2a5fb572ba038 100644 --- a/packages/next/src/server/web/sandbox/sandbox.ts +++ b/packages/next/src/server/web/sandbox/sandbox.ts @@ -15,6 +15,7 @@ const FORBIDDEN_HEADERS = [ type RunnerFn = (params: { name: string + onError?: (err: unknown) => void onWarning?: (warn: Error) => void paths: string[] request: NodejsRequestData @@ -54,6 +55,7 @@ function withTaggedErrors(fn: RunnerFn): RunnerFn { export async function getRuntimeContext(params: { name: string onWarning?: any + onError?: (err: unknown) => void useCache: boolean edgeFunctionEntry: any distDir: string @@ -63,6 +65,7 @@ export async function getRuntimeContext(params: { const { runtime, evaluateInContext } = await getModuleContext({ moduleName: params.name, onWarning: params.onWarning ?? (() => {}), + onError: params.onError ?? (() => {}), useCache: params.useCache !== false, edgeFunctionEntry: params.edgeFunctionEntry, distDir: params.distDir, diff --git a/test/development/app-render-error-log/app-render-error-log.test.ts b/test/development/app-render-error-log/app-render-error-log.test.ts index f9b2c7386d5bf..f587fbf9a1905 100644 --- a/test/development/app-render-error-log/app-render-error-log.test.ts +++ b/test/development/app-render-error-log/app-render-error-log.test.ts @@ -33,7 +33,7 @@ createNextDescribe( await check(() => cliOutput, /digest:/) expect(cliOutput).toInclude('Error: boom') expect(cliOutput).toInclude('at fn2 (./app/fn.ts') - expect(cliOutput).toInclude('at fn1 (./app/fn.ts') + expect(cliOutput).toMatch(/at (Module\.)?fn1 \(\.\/app\/fn\.ts/) expect(cliOutput).toInclude('at EdgePage (./app/edge/page.tsx') expect(cliOutput).not.toInclude('webpack-internal') diff --git a/test/turbopack-tests-manifest.json b/test/turbopack-tests-manifest.json index 0b2747b32f1d0..9f179ec575553 100644 --- a/test/turbopack-tests-manifest.json +++ b/test/turbopack-tests-manifest.json @@ -1514,11 +1514,10 @@ }, "test/development/app-render-error-log/app-render-error-log.test.ts": { "passed": [ - "app-render-error-log should log the correct values on app-render error" - ], - "failed": [ + "app-render-error-log should log the correct values on app-render error", "app-render-error-log should log the correct values on app-render error with edge runtime" ], + "failed": [], "pending": [], "flakey": [], "runtimeError": false