Skip to content

Commit

Permalink
Turbopack: Trace edge runtime app render errors through source maps (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
wbinnssmith committed Mar 6, 2024
1 parent 0d3481b commit f9aec90
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 10 deletions.
14 changes: 8 additions & 6 deletions packages/next/src/server/app-render/create-error-handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/dev/next-dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
},
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FetchEventResult | null> {
if (process.env.NEXT_MINIMAL) {
Expand Down Expand Up @@ -1890,6 +1891,7 @@ export default class NextNodeServer extends BaseServer {
),
},
useCache: true,
onError: params.onError,
onWarning: params.onWarning,
incrementalCache:
(globalThis as any).__incrementalCache ||
Expand Down
7 changes: 7 additions & 0 deletions packages/next/src/server/web/sandbox/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions packages/next/src/server/web/sandbox/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const FORBIDDEN_HEADERS = [

type RunnerFn = (params: {
name: string
onError?: (err: unknown) => void
onWarning?: (warn: Error) => void
paths: string[]
request: NodejsRequestData
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
5 changes: 2 additions & 3 deletions test/turbopack-tests-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f9aec90

Please sign in to comment.