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
This commit is contained in:
Will Binns-Smith 2024-03-05 20:40:26 -08:00 committed by GitHub
parent 0d3481b6b2
commit f9aec9005a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 24 additions and 10 deletions

View file

@ -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)
}

View file

@ -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')
},

View file

@ -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) {
@ -1890,6 +1891,7 @@ export default class NextNodeServer extends BaseServer {
),
},
useCache: true,
onError: params.onError,
onWarning: params.onWarning,
incrementalCache:
(globalThis as any).__incrementalCache ||

View file

@ -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

View file

@ -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,

View file

@ -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')

View file

@ -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