diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts index 46caab6a7d5720..99ba9aac84247a 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts @@ -83,7 +83,7 @@ function createStackFrame(searchParams: URLSearchParams) { } satisfies TurbopackStackFrame } -export async function createOriginalStackFrame( +async function createOriginalStackFrame( project: Project, frame: TurbopackStackFrame ): Promise { diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware.ts index 923241cf496248..1f048d460443e2 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware.ts @@ -177,7 +177,7 @@ export async function createOriginalStackFrame({ } } -export async function getSourceMapFromCompilation( +async function getSourceMapFromCompilation( id: string, compilation: webpack.Compilation ): Promise { diff --git a/packages/next/src/server/dev/log-app-dir-error.ts b/packages/next/src/server/dev/log-app-dir-error.ts deleted file mode 100644 index bc92f7025fe0ef..00000000000000 --- a/packages/next/src/server/dev/log-app-dir-error.ts +++ /dev/null @@ -1,32 +0,0 @@ -import isError from '../../lib/is-error' -import * as Log from '../../build/output/log' - -export function logAppDirError(err: unknown) { - if (isError(err) && err?.stack) { - const cleanedStack = err.stack.split('\n').map((line: string) => - // Remove 'webpack-internal:' noise from the path - line.replace(/(webpack-internal:\/\/\/|file:\/\/)(\(.*?\)\/)?/, '') - ) - const filteredStack = cleanedStack - // Only display stack frames from the user's code - .filter( - (line: string) => - !/^\s+at.+next[\\/]dist[\\/]compiled/.test(line) && - !/^\s+at.+node_modules[\\/]/.test(line) && - !/^\s+at.+node:internal[\\/]/.test(line) - ) - if (filteredStack.length === 1) { - // This is an error that happened outside of user code, keep full stack - Log.error(`Internal error: ${cleanedStack.join('\n')}`) - } else { - Log.error(filteredStack.join('\n')) - } - if (typeof (err as any).digest !== 'undefined') { - console.error(`digest: ${JSON.stringify((err as any).digest)}`) - } - - if (err.cause) console.error('Cause:', err.cause) - } else { - Log.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 5bf7cb2dea701d..d1d4c964ba7aba 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -316,12 +316,10 @@ export default class DevServer extends Server { // not really errors. They're just part of rendering. return } - this.logErrorWithOriginalStack(reason, 'unhandledRejection').catch( - () => {} - ) + this.logErrorWithOriginalStack(reason, 'unhandledRejection') }) process.on('uncaughtException', (err) => { - this.logErrorWithOriginalStack(err, 'uncaughtException').catch(() => {}) + this.logErrorWithOriginalStack(err, 'uncaughtException') }) } @@ -561,7 +559,7 @@ export default class DevServer extends Server { } catch (error) { const err = getProperError(error) formatServerError(err) - this.logErrorWithOriginalStack(err).catch(() => {}) + this.logErrorWithOriginalStack(err) if (!res.sent) { res.statusCode = 500 try { @@ -576,11 +574,11 @@ export default class DevServer extends Server { } } - protected async logErrorWithOriginalStack( + protected logErrorWithOriginalStack( err?: unknown, type?: 'unhandledRejection' | 'uncaughtException' | 'warning' | 'app-dir' - ): Promise { - await this.bundlerService.logErrorWithOriginalStack(err, type) + ): void { + this.bundlerService.logErrorWithOriginalStack(err, type) } protected getPagesManifest(): PagesManifest | undefined { @@ -911,7 +909,6 @@ export default class DevServer extends Server { await super.instrumentationOnRequestError(...args) const err = args[0] - // Safe catch to avoid floating promises - this.logErrorWithOriginalStack(err, 'app-dir').catch(() => {}) + this.logErrorWithOriginalStack(err, 'app-dir') } } diff --git a/packages/next/src/server/lib/dev-bundler-service.ts b/packages/next/src/server/lib/dev-bundler-service.ts index 01b0e0aa6034df..3cd148df9d7a55 100644 --- a/packages/next/src/server/lib/dev-bundler-service.ts +++ b/packages/next/src/server/lib/dev-bundler-service.ts @@ -33,10 +33,8 @@ export class DevBundlerService { return await this.bundler.hotReloader.ensurePage(definition) } - public logErrorWithOriginalStack: typeof this.bundler.logErrorWithOriginalStack = - async (...args) => { - return await this.bundler.logErrorWithOriginalStack(...args) - } + public logErrorWithOriginalStack = + this.bundler.logErrorWithOriginalStack.bind(this.bundler) public async getFallbackErrorComponents(url?: string) { await this.bundler.hotReloader.buildFallbackError() diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 999a4d0805dd2d..453f6448a76e17 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -13,6 +13,7 @@ import path from 'path' import loadConfig from '../config' import { serveStatic } from '../serve-static' import setupDebug from 'next/dist/compiled/debug' +import * as Log from '../../build/output/log' import { DecodeError } from '../../shared/lib/utils' import { findPagesDir } from '../../lib/find-pages-dir' import { setupFsCheck } from './router-utils/filesystem' @@ -641,7 +642,11 @@ export async function initialize(opts: { // not really errors. They're just part of rendering. return } - await developmentBundler?.logErrorWithOriginalStack(err, type) + if (type === 'unhandledRejection') { + Log.error('unhandledRejection: ', err) + } else if (type === 'uncaughtException') { + Log.error('uncaughtException: ', err) + } } process.on('uncaughtException', logError.bind(null, 'uncaughtException')) diff --git a/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts b/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts index 03dc055e1dbb38..6f65015502a7c2 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts @@ -10,7 +10,6 @@ import type { PropagateToWorkersField } from './types' import type { NextJsHotReloaderInterface } from '../../dev/hot-reloader-types' import { createDefineEnv } from '../../../build/swc' -import type { Project } from '../../../build/swc/types' import fs from 'fs' import { mkdir } from 'fs/promises' import url from 'url' @@ -18,7 +17,6 @@ import path from 'path' import qs from 'querystring' import Watchpack from 'next/dist/compiled/watchpack' import { loadEnvConfig } from '@next/env' -import isError, { type NextError } from '../../../lib/is-error' import findUp from 'next/dist/compiled/find-up' import { buildCustomRoute } from './filesystem' import * as Log from '../../../build/output/log' @@ -30,7 +28,6 @@ import loadJsConfig from '../../../build/load-jsconfig' import { createValidFileMatcher } from '../find-page-file' import { eventCliSession } from '../../../telemetry/events' import { getDefineEnv } from '../../../build/webpack/plugins/define-env-plugin' -import { logAppDirError } from '../../dev/log-app-dir-error' import { getSortedRoutes } from '../../../shared/lib/router/utils' import { getStaticInfoIncludingLayouts, @@ -49,7 +46,6 @@ import { generateInterceptionRoutesRewrites } from '../../../lib/generate-interc import { CLIENT_STATIC_FILES_PATH, - COMPILER_NAMES, DEV_CLIENT_PAGES_MANIFEST, DEV_CLIENT_MIDDLEWARE_MANIFEST, PHASE_DEVELOPMENT_SERVER, @@ -64,24 +60,12 @@ import { getPossibleMiddlewareFilenames, getPossibleInstrumentationHookFilenames, } from '../../../build/utils' -import { - createOriginalStackFrame, - getSourceMapFromCompilation, - getSourceMapFromFile, - parseStack, -} from '../../../client/components/react-dev-overlay/server/middleware' -import { - batchedTraceSource, - createOriginalStackFrame as createOriginalTurboStackFrame, -} from '../../../client/components/react-dev-overlay/server/middleware-turbopack' -import type { OriginalStackFrameResponse } from '../../../client/components/react-dev-overlay/server/shared' + import { devPageFiles } from '../../../build/webpack/plugins/next-types-plugin/shared' import type { LazyRenderServerInstance } from '../router-server' import { HMR_ACTIONS_SENT_TO_BROWSER } from '../../dev/hot-reloader-types' import { PAGE_TYPES } from '../../../lib/page-types' import { createHotReloaderTurbopack } from '../../dev/hot-reloader-turbopack' -import { getErrorSource } from '../../../shared/lib/error-source' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import { generateEncryptionKeyBase64 } from '../../app-render/encryption-utils-server' import { ModuleBuildError, @@ -939,126 +923,24 @@ async function startWatcher(opts: SetupOpts) { return { finished: false } } - async function logErrorWithOriginalStack( + function logErrorWithOriginalStack( err: unknown, type?: 'unhandledRejection' | 'uncaughtException' | 'warning' | 'app-dir' ) { - let usedOriginalStack = false - - if (isError(err) && err.stack) { - try { - const frames = parseStack(err.stack!) - // Filter out internal edge related runtime stack - const frame = frames.find( - ({ file }) => - !file?.startsWith('eval') && - !file?.includes('web/adapter') && - !file?.includes('web/globals') && - !file?.includes('sandbox/context') && - !file?.includes('') - ) - - let originalFrame: OriginalStackFrameResponse | null = null - let isEdgeCompiler = false - const frameFile = frame?.file - if (frame?.lineNumber && frameFile) { - if (hotReloader.turbopackProject) { - try { - originalFrame = await createOriginalTurboStackFrame( - hotReloader.turbopackProject, - { - file: frameFile, - methodName: frame.methodName, - line: frame.lineNumber ?? 0, - column: frame.column ?? undefined, - isServer: true, - } - ) - } catch {} - } else { - const moduleId = frameFile.replace( - /^(webpack-internal:\/\/\/|file:\/\/)/, - '' - ) - const modulePath = frameFile.replace( - /^(webpack-internal:\/\/\/|file:\/\/)(\(.*\)\/)?/, - '' - ) - - const src = getErrorSource(err as Error) - isEdgeCompiler = src === COMPILER_NAMES.edgeServer - const compilation = ( - isEdgeCompiler - ? hotReloader.edgeServerStats?.compilation - : hotReloader.serverStats?.compilation - )! - - const sourceMap = await (frame.file?.startsWith(path.sep) || - frame.file?.startsWith('file:') - ? getSourceMapFromFile(frame.file) - : getSourceMapFromCompilation(moduleId, compilation)) - - if (sourceMap) { - try { - originalFrame = await createOriginalStackFrame({ - source: { - type: 'bundle', - sourceMap, - compilation, - moduleId, - modulePath, - }, - frame, - rootDirectory: opts.dir, - errorMessage: err.message, - }) - } catch {} - } - } - - if ( - originalFrame?.originalCodeFrame && - originalFrame.originalStackFrame - ) { - const { originalCodeFrame, originalStackFrame } = originalFrame - const { file, lineNumber, column, methodName } = originalStackFrame - - Log[type === 'warning' ? 'warn' : 'error']( - `${file} (${lineNumber}:${column}) @ ${methodName}` - ) - - let errorToLog - if (isEdgeCompiler) { - errorToLog = err.message - } else if (isError(err) && hotReloader.turbopackProject) { - const stack = await traceTurbopackErrorStack( - hotReloader.turbopackProject, - err, - frames - ) - - const error: NextError = new Error(err.message) - error.stack = stack - error.digest = err.digest - errorToLog = error - } else { - errorToLog = err - } - - logError(errorToLog, type) - console[type === 'warning' ? 'warn' : 'error'](originalCodeFrame) - usedOriginalStack = true - } - } - } catch (_) { - // failed to load original stack using source maps - // this un-actionable by users so we don't show the - // internal error and only show the provided stack - } - } - - if (!usedOriginalStack) { - logError(err, type) + if (err instanceof ModuleBuildError) { + // Errors that may come from issues from the user's code + Log.error(err.message) + } else if (err instanceof TurbopackInternalError) { + // An internal Turbopack error that has been handled by next-swc, written + // to disk and a simplified message shown to user on the Rust side. + } else if (type === 'warning') { + Log.warn(err) + } else if (type === 'app-dir') { + Log.error(err) + } else if (type) { + Log.error(`${type}:`, err) + } else { + Log.error(err) } } @@ -1080,27 +962,6 @@ async function startWatcher(opts: SetupOpts) { } } -function logError( - err: unknown, - type?: 'unhandledRejection' | 'uncaughtException' | 'warning' | 'app-dir' -) { - if (err instanceof ModuleBuildError) { - // Errors that may come from issues from the user's code - Log.error(err.message) - } else if (err instanceof TurbopackInternalError) { - // An internal Turbopack error that has been handled by next-swc, written - // to disk and a simplified message shown to user on the Rust side. - } else if (type === 'warning') { - Log.warn(err) - } else if (type === 'app-dir') { - logAppDirError(err) - } else if (type) { - Log.error(`${type}:`, err) - } else { - Log.error(err) - } -} - export async function setupDevBundler(opts: SetupOpts) { const isSrcDir = path .relative(opts.dir, opts.pagesDir || opts.appDir || '') @@ -1130,68 +991,3 @@ export async function setupDevBundler(opts: SetupOpts) { export type DevBundler = Awaited> // Returns a trace rewritten through Turbopack's sourcemaps -async function traceTurbopackErrorStack( - project: Project, - error: Error, - frames: StackFrame[] -): Promise { - let originalFrames = await Promise.all( - frames.map(async (f) => { - try { - const traced = await batchedTraceSource(project, { - file: f.file!, - methodName: f.methodName, - line: f.lineNumber ?? 0, - column: f.column ?? undefined, - isServer: true, - }) - - return traced?.frame ?? f - } catch { - return f - } - }) - ) - - return ( - error.name + - ': ' + - error.message + - '\n' + - originalFrames - .map((f) => { - if (f == null) { - return null - } - - let line = ' at' - if (f.methodName != null) { - line += ' ' + f.methodName - } - - if (f.file != null) { - const file = - f.file.startsWith('/') || - // Built-in "filenames" like `` shouldn't be made relative - f.file.startsWith('<') || - f.file.startsWith('node:') - ? f.file - : `./${f.file}` - - line += ` (${file}` - if (f.lineNumber != null) { - line += ':' + f.lineNumber - - if (f.column != null) { - line += ':' + f.column - } - } - line += ')' - } - - return line - }) - .filter(Boolean) - .join('\n') - ) -} diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 68abe96091b33b..f5900c5f5b8fde 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -1049,7 +1049,7 @@ export default class NextNodeServer extends BaseServer< const { formatServerError } = require('../lib/format-server-error') as typeof import('../lib/format-server-error') formatServerError(err) - await this.logErrorWithOriginalStack(err) + this.logErrorWithOriginalStack(err) } else { this.logError(err) } @@ -1063,10 +1063,10 @@ export default class NextNodeServer extends BaseServer< } // Used in development only, overloaded in next-dev-server - protected async logErrorWithOriginalStack( + protected logErrorWithOriginalStack( _err?: unknown, _type?: 'unhandledRejection' | 'uncaughtException' | 'warning' | 'app-dir' - ): Promise { + ): void { throw new Error( 'Invariant: logErrorWithOriginalStack can only be called on the development server' ) diff --git a/packages/next/src/server/node-environment-extensions/error-inspect.tsx b/packages/next/src/server/node-environment-extensions/error-inspect.tsx index e086cbd003bd69..01ba40d7b0c0a5 100644 --- a/packages/next/src/server/node-environment-extensions/error-inspect.tsx +++ b/packages/next/src/server/node-environment-extensions/error-inspect.tsx @@ -1,3 +1,3 @@ import { patchErrorInspect } from '../patch-error-inspect' -patchErrorInspect() +patchErrorInspect(globalThis.Error) diff --git a/packages/next/src/server/patch-error-inspect.ts b/packages/next/src/server/patch-error-inspect.ts index 2e96e413bb6b47..1c68cf560c854c 100644 --- a/packages/next/src/server/patch-error-inspect.ts +++ b/packages/next/src/server/patch-error-inspect.ts @@ -210,12 +210,12 @@ function parseAndSourceMap(error: Error): string { ) } -export function patchErrorInspect() { - Error.prepareStackTrace = prepareUnsourcemappedStackTrace +export function patchErrorInspect(errorClass: ErrorConstructor): void { + errorClass.prepareStackTrace = prepareUnsourcemappedStackTrace // @ts-expect-error -- TODO upstream types // eslint-disable-next-line no-extend-native -- We're not extending but overriding. - Error.prototype[inspectSymbol] = function ( + errorClass.prototype[inspectSymbol] = function ( depth: number, inspectOptions: util.InspectOptions, inspect: typeof util.inspect @@ -227,8 +227,8 @@ export function patchErrorInspect() { const newError = this.cause !== undefined ? // Setting an undefined `cause` would print `[cause]: undefined` - new Error(this.message, { cause: this.cause }) - : new Error(this.message) + new errorClass(this.message, { cause: this.cause }) + : new errorClass(this.message) // TODO: Ensure `class MyError extends Error {}` prints `MyError` as the name newError.stack = parseAndSourceMap(this) diff --git a/packages/next/src/server/web/sandbox/context.ts b/packages/next/src/server/web/sandbox/context.ts index 3428ab7f34a434..b41586825fcb5a 100644 --- a/packages/next/src/server/web/sandbox/context.ts +++ b/packages/next/src/server/web/sandbox/context.ts @@ -22,6 +22,7 @@ import UtilImplementation from 'node:util' import AsyncHooksImplementation from 'node:async_hooks' import { intervalsManager, timeoutsManager } from './resource-managers' import { createLocalRequestContext } from '../../after/builtin-request-context' +import { patchErrorInspect } from '../../patch-error-inspect' interface ModuleContext { runtime: EdgeRuntime @@ -477,6 +478,8 @@ Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), decorateUnhandledRejection ) + patchErrorInspect(runtime.context.Error) + return { runtime, paths: new Map(), diff --git a/test/development/middleware-errors/index.test.ts b/test/development/middleware-errors/index.test.ts index 110846256309c0..1eb9521c38c24b 100644 --- a/test/development/middleware-errors/index.test.ts +++ b/test/development/middleware-errors/index.test.ts @@ -36,19 +36,23 @@ describe('middleware - development errors', () => { await retry(() => { expect(stripAnsi(next.cliOutput)).toContain('boom') }) - // TODO: assert on full, ignore-listed stack expect(stripAnsi(next.cliOutput)).toContain( isTurbopack - ? '\n ⨯ middleware.js (3:15) @ __TURBOPACK__default__export__' + - '\n ⨯ Error: boom' + - '\n at __TURBOPACK__default__export__ (./middleware.js:3:15)' - : '\n ⨯ middleware.js (3:15) @ default' + - '\n ⨯ boom' + - '\n 1 |' + - '\n 2 | export default function () {' + - "\n> 3 | throw new Error('boom')" + - '\n | ^' + ? '\n ⨯ Error: boom' + + // TODO(veil): Should be sourcemapped + '\n at __TURBOPACK__default__export__ (' + : '\n ⨯ Error: boom' + + '\n at default (middleware.js:3:14)' + + // TODO(veil): Should be ignore-listed + '\n at eval (webpack' ) + if (isTurbopack) { + // TODO(veil): Should have codeframe + } else { + expect(stripAnsi(next.cliOutput)).toContain( + "\n> 3 | throw new Error('boom')" + ) + } }) it('renders the error correctly and recovers', async () => { @@ -85,14 +89,23 @@ describe('middleware - development errors', () => { 'unhandledRejection: Error: async boom!' ) }) - // TODO: assert on full, ignore-listed stack expect(stripAnsi(next.cliOutput)).toContain( isTurbopack - ? 'unhandledRejection: Error: async boom!\n at throwError (' - : 'unhandledRejection: Error: async boom!' + - '\n at throwError (webpack-internal:///(middleware)/./middleware.js:8:11)' + - '\n at __WEBPACK_DEFAULT_EXPORT__ (webpack-internal:///(middleware)/./middleware.js:11:5)' + ? // TODO(veil): Should be sourcemapped + ' ⨯ unhandledRejection: Error: async boom!\n at throwError (/' + : '\n ⨯ unhandledRejection: Error: async boom!' + + '\n at throwError (middleware.js:4:14)' + + '\n at throwError (middleware.js:7:8)' + + // TODO(veil): Should be ignore-listed + '\n at eval (webpack' ) + if (isTurbopack) { + // TODO(veil): Should have codeframe + } else { + expect(stripAnsi(next.cliOutput)).toContain( + "> 4 | throw new Error('async boom!')" + ) + } }) it('does not render the error', async () => { @@ -123,20 +136,19 @@ describe('middleware - development errors', () => { await retry(() => { expect(stripAnsi(next.cliOutput)).toContain('Dynamic Code Evaluation') }) - // TODO: assert on full, ignore-listed stack if (isTurbopack) { // Locally, prefixes the "test is not defined". // In CI, it prefixes "Dynamic Code Evaluation". expect(stripAnsi(next.cliOutput)).toContain( - '\n ⚠ middleware.js (3:22) @ __TURBOPACK__default__export__' + - '\n ⨯ middleware.js (4:9) @ eval' + // TODO(veil): Should be sourcemapped + '\n at __TURBOPACK__default__export__ (/' ) } expect(stripAnsi(next.cliOutput)).toContain( isTurbopack - ? '\n ⨯ Error: test is not defined' + - '\n at eval (./middleware.js:4:9)' + - '\n at (./middleware.js:4:9' + ? '\n ⨯ Error [ReferenceError]: test is not defined' + + // TODO(veil): Should be sourcemapped + '\n at eval ' : '\n ⨯ Error [ReferenceError]: test is not defined' + // TODO: Redundant and not clickable '\n at eval (file://webpack-internal:///(middleware)/./middleware.js)' + @@ -146,12 +158,15 @@ describe('middleware - development errors', () => { ) expect(stripAnsi(next.cliOutput)).toContain( isTurbopack - ? "\n ⚠ Error: Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" + + ? "\n ⚠ DynamicCodeEvaluationWarning: Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" + '\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation' + - '\n at __TURBOPACK__default__export__ (./middleware.js:3:22)' - : '\n ⚠ middleware.js (4:9) @ eval' + - "\n ⚠ Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" + - '\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation' + // TODO(veil): Should be sourcemapped + '\n at __TURBOPACK__default__export__ (' + : "\n ⚠ DynamicCodeEvaluationWarning: Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" + + '\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation' + + '\n at eval (middleware.js:4:8)' + + // TODO(veil): Should be ignore-listed + '\n at eval (webpack' ) }) @@ -186,9 +201,9 @@ describe('middleware - development errors', () => { }) expect(stripAnsi(next.cliOutput)).toContain( isTurbopack - ? '\n ⨯ middleware.js (3:13) @ [project]/middleware.js [middleware] (ecmascript)' + - '\n ⨯ Error: booooom!' + - '\n at ([project]/middleware.js [middleware] (ecmascript) (./middleware.js:3:13)' + ? '\n ⨯ Error: booooom!' + + // TODO(veil): Should be sourcemapped + '\n at [project]/middleware.js [middleware] (ecmascript)' : '\n ⨯ Error: booooom!' + // TODO: Should be anonymous method without a method name '\n at (middleware.js:3)' + diff --git a/test/e2e/app-dir/errors/index.test.ts b/test/e2e/app-dir/errors/index.test.ts index 6bf778b31fd988..d26da376738dfd 100644 --- a/test/e2e/app-dir/errors/index.test.ts +++ b/test/e2e/app-dir/errors/index.test.ts @@ -67,7 +67,7 @@ describe('app-dir - errors', () => { expect(stripAnsi(next.cliOutput)).toEqual( expect.stringMatching( isNextDev - ? /Error: this is a test.*digest: "custom"/s + ? /Error: this is a test.*digest: 'custom'/s : /Error: this is a test.*digest: 'custom'/s ) ) @@ -90,7 +90,7 @@ describe('app-dir - errors', () => { expect(stripAnsi(next.cliOutput)).toEqual( expect.stringMatching( isNextDev - ? /Error: An undefined error was thrown.*digest: "\d+"/s + ? /Error: An undefined error was thrown.*digest: '\d+'/s : /Error: undefined.*digest: '\d+'/s ) ) @@ -113,7 +113,7 @@ describe('app-dir - errors', () => { expect(stripAnsi(next.cliOutput)).toEqual( expect.stringMatching( isNextDev - ? /Error: A null error was thrown.*digest: "\d+"/s + ? /Error: A null error was thrown.*digest: '\d+'/s : /Error: null.*digest: '\d+'/s ) ) @@ -136,7 +136,7 @@ describe('app-dir - errors', () => { expect(stripAnsi(next.cliOutput)).toEqual( expect.stringMatching( isNextDev - ? /Error: this is a test.*digest: "\d+"/s + ? /Error: this is a test.*digest: '\d+'/s : /Error: An error occurred in the Server Components render.*digest: '\d+'/s ) ) diff --git a/test/e2e/app-dir/server-source-maps/server-source-maps-edge.test.ts b/test/e2e/app-dir/server-source-maps/server-source-maps-edge.test.ts index bc3ad09eaa966d..f91c77a94f1cbd 100644 --- a/test/e2e/app-dir/server-source-maps/server-source-maps-edge.test.ts +++ b/test/e2e/app-dir/server-source-maps/server-source-maps-edge.test.ts @@ -53,15 +53,22 @@ describe('app-dir - server source maps edge runtime', () => { expect(cliOutput).toContain( isTurbopack ? '\n ⨯ Error: Boom' + - '\n at throwError (./app/ssr-throw/page.js:4:9)' + - '\n at Page (./app/ssr-throw/page.js:8:3)' + - '\ndigest: "' + // TODO(veil): Apply sourcemap + '\n at throwError (/' : '\n ⨯ Error: Boom' + - '\n at throwError (./app/ssr-throw/page.js:6:11)' + - '\n at Page (./app/ssr-throw/page.js:9:5)' + - '\ndigest: "' + '\n at throwError (app/ssr-throw/page.js:4:8)' + + // TODO(veil): Method name should be "Page" + '\n at throwError (app/ssr-throw/page.js:8:2)' + + '\n 2 |' + + '\n 3 | function throwError() {' + + "\n> 4 | throw new Error('Boom')" + + '\n | ^' + + '\n 5 | }' + + '\n 6 |' + + '\n 7 | export default function Page() { {' + + "\n digest: '" ) - expect(cliOutput).toMatch(/digest: "\d+"/) + expect(cliOutput).toMatch(/digest: '\d+'/) } else { // TODO: Test `next build` with `--enable-source-maps`. } @@ -81,19 +88,21 @@ describe('app-dir - server source maps edge runtime', () => { expect(cliOutput).toContain( isTurbopack ? '\n ⨯ Error: Boom' + - '\n at throwError (./app/rsc-throw/page.js:2:9)' + - '\n at Page (./app/rsc-throw/page.js:6:3)' + - // TODO(veil): Hide Node.js internal stackframes - '\n at AsyncLocalStorage.run (node:async_hooks:346:14)' + - '\ndigest: "' + // TODO(veil): Apply sourcemap + '\n at throwError (/' : '\n ⨯ Error: Boom' + - '\n at throwError (./app/rsc-throw/page.js:6:11)' + - '\n at Page (./app/rsc-throw/page.js:9:5)' + - // TODO(veil): Hide Node.js internal stackframes - '\n at AsyncLocalStorage.run (node:async_hooks:346:14)' + - '\ndigest: "' + '\n at throwError (app/rsc-throw/page.js:2:8)' + + // TODO(veil): Method name should be "Page" + '\n at throwError (app/rsc-throw/page.js:6:2)' + + '\n 1 | function throwError() {' + + "\n> 2 | throw new Error('Boom')" + + '\n | ^' + + '\n 3 | }' + + '\n 4 |' + + '\n 5 | export default function Page() { {' + + "\n digest: '" ) - expect(cliOutput).toMatch(/digest: "\d+"/) + expect(cliOutput).toMatch(/digest: '\d+'/) } else { // TODO: Test `next build` with `--enable-source-maps`. } diff --git a/test/e2e/app-dir/server-source-maps/server-source-maps.test.ts b/test/e2e/app-dir/server-source-maps/server-source-maps.test.ts index feb320c1b75ea0..674a0effe84e4d 100644 --- a/test/e2e/app-dir/server-source-maps/server-source-maps.test.ts +++ b/test/e2e/app-dir/server-source-maps/server-source-maps.test.ts @@ -62,7 +62,7 @@ describe('app-dir - server source maps', () => { await retry(() => { expect(next.cliOutput.slice(outputIndex)).toContain('Error: Boom') }) - expect(normalizeCliOutput(next.cliOutput)).toContain( + expect(normalizeCliOutput(next.cliOutput.slice(outputIndex))).toContain( '\nError: Boom' + '\n at logError (app/rsc-error-log-cause/page.js:4:16)' + (isTurbopack @@ -92,8 +92,8 @@ describe('app-dir - server source maps', () => { } }) - // FIXME: Turbopack resolver bug - // FIXME: Turbopack build? bugs taint the whole dev server + // TODO(veil): Turbopack resolver bug + // TODO(veil): Turbopack build? bugs taint the whole dev server ;(isTurbopack ? it.skip : it)( 'stack frames are ignore-listed in ssr', async () => { @@ -106,11 +106,11 @@ describe('app-dir - server source maps', () => { }) expect(normalizeCliOutput(next.cliOutput.slice(outputIndex))).toContain( isTurbopack - ? // FIXME: Turbopack resolver bug + ? // TODO(veil): Turbopack resolver bug "Module not found: Can't resolve 'internal-pkg'" : '\nError: Boom' + '\n at logError (app/ssr-error-log-ignore-listed/page.js:5:16)' + - // FIXME: Method name should be "Page" + // TODO(veil): Method name should be "Page" '\n at logError (app/ssr-error-log-ignore-listed/page.js:10:12)' + '\n at Page (app/ssr-error-log-ignore-listed/page.js:10:6)' + '\n 3 |' @@ -121,8 +121,8 @@ describe('app-dir - server source maps', () => { } ) - // FIXME: Turbopack resolver bug - // FIXME: Turbopack build? bugs taint the whole dev server + // TODO(veil): Turbopack resolver bug + // TODO(veil): Turbopack build? bugs taint the whole dev server ;(isTurbopack ? it.skip : it)( 'stack frames are ignore-listed in rsc', async () => { @@ -137,11 +137,11 @@ describe('app-dir - server source maps', () => { }) expect(normalizeCliOutput(next.cliOutput.slice(outputIndex))).toContain( isTurbopack - ? // FIXME: Turbopack resolver bug + ? // TODO(veil): Turbopack resolver bug "Module not found: Can't resolve 'internal-pkg'" : '\nError: Boom' + '\n at logError (app/rsc-error-log-ignore-listed/page.js:5:16)' + - // FIXME: Method name should be "Page" + // TODO(veil): Method name should be "Page" '\n at logError (app/rsc-error-log-ignore-listed/page.js:12:12)' + '\n at Page (app/rsc-error-log-ignore-listed/page.js:12:6)' + '\n 3 |' @@ -165,15 +165,22 @@ describe('app-dir - server source maps', () => { expect(cliOutput).toContain( isTurbopack ? '\n ⨯ Error: Boom' + - '\n at throwError (./app/ssr-throw/Thrower.js:4:9)' + - '\n at Thrower (./app/ssr-throw/Thrower.js:8:3)' + - '\ndigest: "' + // TODO(veil): Apply sourcemap + '\n at throwError (/' : '\n ⨯ Error: Boom' + - '\n at throwError (./app/ssr-throw/Thrower.js:6:11)' + - '\n at Thrower (./app/ssr-throw/Thrower.js:9:5)' + - '\ndigest: "' + '\n at throwError (app/ssr-throw/Thrower.js:4:8)' + + // TODO(veil): Method name should be "Thrower" + '\n at throwError (app/ssr-throw/Thrower.js:8:2)' + + '\n 2 |' + + '\n 3 | function throwError() {' + + "\n> 4 | throw new Error('Boom')" + + '\n | ^' + + '\n 5 | }' + + '\n 6 |' + + '\n 7 | export function Thrower() { {' + + "\n digest: '" ) - expect(cliOutput).toMatch(/digest: "\d+"/) + expect(cliOutput).toMatch(/digest: '\d+'/) } else { // TODO: Test `next build` with `--enable-source-maps`. } diff --git a/test/e2e/fetch-failures-have-good-stack-traces-in-edge-runtime/fetch-failures-have-good-stack-traces-in-edge-runtime.test.ts b/test/e2e/fetch-failures-have-good-stack-traces-in-edge-runtime/fetch-failures-have-good-stack-traces-in-edge-runtime.test.ts index d6b05103910539..56e1e0d4d6f119 100644 --- a/test/e2e/fetch-failures-have-good-stack-traces-in-edge-runtime/fetch-failures-have-good-stack-traces-in-edge-runtime.test.ts +++ b/test/e2e/fetch-failures-have-good-stack-traces-in-edge-runtime/fetch-failures-have-good-stack-traces-in-edge-runtime.test.ts @@ -25,7 +25,8 @@ describe('fetch failures have good stack traces in edge runtime', () => { if (isNextStart) { expect(next.cliOutput).toMatch(/at.+\/pages\/api\/unknown-domain.js/) } else if (isNextDev) { - expect(next.cliOutput).toContain('src/fetcher.js') + // TODO(veil): Apply sourcemap + expect(next.cliOutput).toContain('\n at anotherFetcher (') await assertHasRedbox(browser) const source = await getRedboxSource(browser) diff --git a/test/integration/edge-runtime-dynamic-code/test/index.test.js b/test/integration/edge-runtime-dynamic-code/test/index.test.js index a434ae10278626..91508e45372010 100644 --- a/test/integration/edge-runtime-dynamic-code/test/index.test.js +++ b/test/integration/edge-runtime-dynamic-code/test/index.test.js @@ -22,6 +22,8 @@ const context = { appDir: join(__dirname, '../'), } +const isTurbopack = process.env.TURBOPACK + describe('Page using eval in development mode', () => { let output = '' @@ -73,7 +75,7 @@ describe.each([ }, ])( '$title usage of dynamic code evaluation', - ({ extractValue, computeRoute }) => { + ({ extractValue, computeRoute, title }) => { ;(process.env.TURBOPACK_BUILD ? describe.skip : describe)( 'development mode', () => { @@ -103,10 +105,26 @@ describe.each([ expect(await extractValue(res)).toEqual(100) await waitFor(500) expect(output).toContain(EVAL_ERROR) - // TODO check why that has a backslash on windows - expect(output).toMatch(/lib[\\/]utils\.js/) - expect(output).toContain('usingEval') - expect(stripAnsi(output)).toContain("value: eval('100')") + if (title === 'Middleware') { + expect(output).toContain( + isTurbopack + ? // TODO(veil): Apply sourcemap + '\n at usingEval (/' + : '\n at eval (../../test/integration/edge-runtime-dynamic-code/lib/utils.js:11:18)' + + '\n at usingEval (../../test/integration/edge-runtime-dynamic-code/middleware.js:12:53)' + + // TODO(veil): Should be ignore-listed + '\n at eval (../packages/next/dist' + ) + } else { + expect(output).toContain( + isTurbopack + ? // TODO(veil): Apply sourcemap + '\n at usingEval (/' + : '\n at eval (../../test/integration/edge-runtime-dynamic-code/lib/utils.js:11:18)' + + '\n at usingEval (../../test/integration/edge-runtime-dynamic-code/pages/api/route.js:13:23)' + + '\n 9 | export async function usingEval() {' + ) + } }) it('does not show warning when no code uses eval', async () => { @@ -127,11 +145,42 @@ describe.each([ expect(await extractValue(res)).toEqual(81) await waitFor(500) expect(output).toContain(WASM_COMPILE_ERROR) - expect(output).toMatch(/lib[\\/]wasm\.js/) - expect(output).toContain('usingWebAssemblyCompile') - expect(stripAnsi(output)).toContain( - 'await WebAssembly.compile(SQUARE_WASM_BUFFER)' - ) + if (title === 'Middleware') { + expect(output).toContain( + isTurbopack + ? // TODO(veil): Apply sourcemap + '\n at usingWebAssemblyCompile (/' + : '\n at WebAssembly (../../test/integration/edge-runtime-dynamic-code/lib/wasm.js:22:23)' + + '\n at middleware (../../test/integration/edge-runtime-dynamic-code/middleware.js:24:68)' + + // TODO(veil): Should be ignore-listed + '\n at eval (../packages/next' + ) + if (isTurbopack) { + // TODO(veil): Display codeframe + } else { + expect(output).toContain( + '\n> 22 | const module = await WebAssembly.compile(SQUARE_WASM_BUFFER)' + ) + } + } else { + expect(output).toContain( + isTurbopack + ? // TODO(veil): Apply sourcemap + '\n at usingWebAssemblyCompile (/' + : '\n at WebAssembly (../../test/integration/edge-runtime-dynamic-code/lib/wasm.js:22:23)' + + '\n at handler (../../test/integration/edge-runtime-dynamic-code/pages/api/route.js:17:42)' + + // TODO(veil): Should be ignore-listed + '\n at' + ) + + if (isTurbopack) { + // TODO(veil): Display codeframe + } else { + expect(output).toContain( + '\n> 22 | const module = await WebAssembly.compile(SQUARE_WASM_BUFFER)' + ) + } + } }) it('shows a warning when running WebAssembly.instantiate with a buffer parameter', async () => { @@ -142,11 +191,39 @@ describe.each([ expect(await extractValue(res)).toEqual(81) await waitFor(500) expect(output).toContain(WASM_INSTANTIATE_ERROR) - expect(output).toMatch(/lib[\\/]wasm\.js/) - expect(output).toContain('usingWebAssemblyInstantiateWithBuffer') - expect(stripAnsi(output)).toContain( - 'await WebAssembly.instantiate(SQUARE_WASM_BUFFER, {})' - ) + if (title === 'Middleware') { + expect(output).toContain( + isTurbopack + ? // TODO(veil): Apply sourcemap + '\n at async usingWebAssemblyInstantiateWithBuffer (/' + : '\n at async usingWebAssemblyInstantiateWithBuffer (../../test/integration/edge-runtime-dynamic-code/lib/wasm.js:28:23)' + + '\n at async middleware (../../test/integration/edge-runtime-dynamic-code/middleware.js:37:29)' + + // TODO(veil): Should be ignore-listed + '\n at ' + ) + expect(stripAnsi(output)).toContain( + isTurbopack + ? // TODO(veil): Use codeframe of a stackframe that's not ignore-listed + '\n> 149 | result = await edgeFunction({' + : '\n> 28 | const { instance } = await WebAssembly.instantiate(SQUARE_WASM_BUFFER, {})' + ) + } else { + expect(output).toContain( + isTurbopack + ? // TODO(veil): Apply sourcemap + '\n at async usingWebAssemblyInstantiateWithBuffer (/' + : '\n at async usingWebAssemblyInstantiateWithBuffer (../../test/integration/edge-runtime-dynamic-code/lib/wasm.js:28:23)' + + '\n at async handler (../../test/integration/edge-runtime-dynamic-code/pages/api/route.js:21:16)' + + // TODO(veil): Should be ignore-listed + '\n at ' + ) + expect(stripAnsi(output)).toContain( + isTurbopack + ? // TODO(veil): Use codeframe of a stackframe that's not ignore-listed + '\n> 149 | result = await edgeFunction({' + : '\n> 28 | const { instance } = await WebAssembly.instantiate(SQUARE_WASM_BUFFER, {})' + ) + } }) it('does not show a warning when running WebAssembly.instantiate with a module parameter', async () => { diff --git a/test/integration/edge-runtime-module-errors/test/utils.js b/test/integration/edge-runtime-module-errors/test/utils.js index 29001a224e4810..394c3380786ea7 100644 --- a/test/integration/edge-runtime-module-errors/test/utils.js +++ b/test/integration/edge-runtime-module-errors/test/utils.js @@ -55,10 +55,12 @@ export function expectUnsupportedModuleDevError( output = context.logs.output ) { expectUnsupportedModuleProdError(moduleName, output) - // turbopack have correct error overly, but doesn't emit those into cli - if (!process.env.TURBOPACK) { - expect(stripAnsi(output)).toContain(importStatement) - } + // TODO(veil): codeframe should point to module call + // We previously asserted that the importStatement appears + // in the codeframe but that's just a coincidence depending on how far + // away the import statement is from the callsite. + // We should instead assert on the sourceframe once errors in edge runtime are + // sourcemapped const moduleNotSupportedMessage = getUnsupportedModule(moduleName) expect(responseText).toContain(escapeLF(moduleNotSupportedMessage)) diff --git a/test/integration/edge-runtime-with-node.js-apis/test/index.test.ts b/test/integration/edge-runtime-with-node.js-apis/test/index.test.ts index a0beee8a087439..b8feed56c09209 100644 --- a/test/integration/edge-runtime-with-node.js-apis/test/index.test.ts +++ b/test/integration/edge-runtime-with-node.js-apis/test/index.test.ts @@ -44,6 +44,8 @@ const unsupportedClasses = [ 'WritableStreamDefaultController', ] +const isTurbopack = process.env.TURBOPACK + describe.each([ { title: 'Middleware', @@ -67,8 +69,10 @@ describe.each([ let appPort: number let app = null - beforeAll(async () => { + beforeEach(() => { output = '' + }) + beforeAll(async () => { appPort = await findPort() app = await launchApp(appDir, appPort, { env: { __NEXT_TEST_WITH_DEVTOOL: '1' }, @@ -109,7 +113,12 @@ describe.each([ expect(output) .toInclude(`A Node.js API is used (${api}) which is not supported in the Edge Runtime. Learn more: https://nextjs.org/docs/api-reference/edge-runtime`) - expect(stripAnsi(output)).toInclude(errorHighlight) + if (isTurbopack) { + expect(stripAnsi(output)).toInclude(errorHighlight) + } else { + // TODO(veil): Use codeframe froma stackframe that's not ignore-listed + expect(stripAnsi(output)).not.toInclude(errorHighlight) + } }) } ) diff --git a/test/integration/server-side-dev-errors/test/index.test.js b/test/integration/server-side-dev-errors/test/index.test.js index 971f5c5ac7f237..c98898a94438f3 100644 --- a/test/integration/server-side-dev-errors/test/index.test.js +++ b/test/integration/server-side-dev-errors/test/index.test.js @@ -63,25 +63,22 @@ describe('server-side dev errors', () => { ) }) - const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { expect(stderrOutput).toStartWith( - ' ⨯ test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' + - '\n ⨯ test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' - ) - expect(stderrOutput).toContain( - ' ⨯ ReferenceError: missingVar is not defined' + - '\n at getStaticProps (./test/integration/server-side-dev-errors/pages/gsp.js:6:3)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): repeated "test/integration/server-side-dev-errors" in path + '\n at getStaticProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at getStaticProps (webpack' ) } else { - expect(stderr).toStartWith( - ' ⨯ pages/gsp.js (6:3) @ missingVar' + - '\n ⨯ ReferenceError: missingVar is not defined' + - '\n at getStaticProps (./pages/gsp.js:19:5)' + + expect(stderrOutput).toStartWith( + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): Should be "at getStaticProps" + '\n at missingVar (../../test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at getStaticProps (webpack' ) } expect(stderr).toContain( @@ -116,25 +113,22 @@ describe('server-side dev errors', () => { ) }) - const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { expect(stderrOutput).toStartWith( - ' ⨯ test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' + - '\n ⨯ test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' - ) - expect(stderrOutput).toContain( - ' ⨯ ReferenceError: missingVar is not defined' + - '\n at getServerSideProps (./test/integration/server-side-dev-errors/pages/gssp.js:6:3)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): repeated "test/integration/server-side-dev-errors" in path + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + // TODO(veil): Should be sourcemapped - '\n a' + '\n at getServerSideProps (webpack' ) } else { expect(stderrOutput).toStartWith( - ' ⨯ pages/gssp.js (6:3) @ missingVar' + - '\n ⨯ ReferenceError: missingVar is not defined' + - '\n at getServerSideProps (./pages/gssp.js:19:5)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): Should be "at getServerSideProps" + '\n at missingVar (../../test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at getServerSideProps (webpack' ) } expect(stderrOutput).toContain( @@ -169,25 +163,22 @@ describe('server-side dev errors', () => { ) }) - const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { expect(stderrOutput).toStartWith( - ' ⨯ test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' + - '\n ⨯ test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' - ) - expect(stderrOutput).toContain( - ' ⨯ ReferenceError: missingVar is not defined' + - '\n at getServerSideProps (./test/integration/server-side-dev-errors/pages/blog/[slug].js:6:3)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): repeated "test/integration/server-side-dev-errors" in path + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/blog/[slug].js:6:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at getServerSideProps (webpack' ) } else { expect(stderrOutput).toStartWith( - ' ⨯ pages/blog/[slug].js (6:3) @ missingVar' + - '\n ⨯ ReferenceError: missingVar is not defined' + - '\n at getServerSideProps (./pages/blog/[slug].js:19:5)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): Should be "at getServerSideProps" + '\n at missingVar (../../test/integration/server-side-dev-errors/pages/blog/[slug].js:6:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at getServerSideProps (webpack' ) } expect(stderrOutput).toContain( @@ -222,25 +213,22 @@ describe('server-side dev errors', () => { ) }) - const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { expect(stderrOutput).toStartWith( - ' ⨯ test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' + - '\n ⨯ test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' - ) - expect(stderrOutput).toContain( - ' ⨯ ReferenceError: missingVar is not defined' + - '\n at handler (./test/integration/server-side-dev-errors/pages/api/hello.js:2:3)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): repeated "test/integration/server-side-dev-errors" in path + '\n at handler (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/hello.js:2:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at resolver (webpack' ) } else { expect(stderrOutput).toStartWith( - ' ⨯ pages/api/hello.js (2:3) @ missingVar' + - '\n ⨯ ReferenceError: missingVar is not defined' + - '\n at handler (./pages/api/hello.js:6:5)' + + '⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): Should be "at handler" + '\n at missingVar (../../test/integration/server-side-dev-errors/pages/api/hello.js:2:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at resolver (webpack' ) } expect(stderrOutput).toContain( @@ -275,27 +263,23 @@ describe('server-side dev errors', () => { ) }) - const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() // FIXME(veil): error repeated - // FIXME(veil): codeframe repeated after " ⨯ unhandledRejection: Error: catch this rejection" if (isTurbopack) { - expect(stderrOutput).toStartWith( - ' ⨯ test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' + - '\n ⨯ test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' - ) expect(stderrOutput).toContain( - '\n ⨯ Error: missingVar is not defined' + - '\n at handler (./test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:3)' + + ' ⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): Should not include `turbopack://[project]` + '\n at handler (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at resolver (webpack' ) } else { - expect(stderrOutput).toStartWith( - ' ⨯ pages/api/blog/[slug].js (2:3) @ missingVar' + - '\n ⨯ ReferenceError: missingVar is not defined' + - '\n at handler (./pages/api/blog/[slug].js:6:5)' + + expect(stderrOutput).toContain( + ' ⨯ ReferenceError: missingVar is not defined' + + // TODO(veil): Should be "at handler" + '\n at missingVar (../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:2)' + // TODO(veil): Should be sourcemapped - '\n at' + '\n at resolver (webpack' ) } @@ -323,10 +307,13 @@ describe('server-side dev errors', () => { }) const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + .replace( + '⚠ Fast Refresh had to perform a full reload due to a runtime error.', + '' + ) + .trim() // FIXME(veil): error repeated - // FIXME(veil): codeframe repeated after " ⨯ unhandledRejection: Error: catch this rejection" if (isTurbopack) { - // TODO(veil): digest: undefined should be omitted? expect(stderrOutput).toMatchInlineSnapshot(` "Error: catch this rejection at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) @@ -337,31 +324,24 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ test/integration/server-side-dev-errors/pages/uncaught-rejection.js (7:20) @ Timeout._onTimeout - ⨯ test/integration/server-side-dev-errors/pages/uncaught-rejection.js (7:20) @ Timeout._onTimeout ⨯ unhandledRejection: Error: catch this rejection - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:20) { - digest: undefined - } + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error('catch this rejection')) - | ^ + | ^ 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ unhandledRejection: Error: catch this rejection - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:20) { - digest: undefined - } + ⨯ unhandledRejection: Error: catch this rejection + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error('catch this rejection')) - | ^ + | ^ 8 | }, 10) 9 | return { - 10 | props: {}, - " + 10 | props: {}," `) } else { expect(stderrOutput).toMatchInlineSnapshot(` @@ -374,7 +354,6 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ pages/uncaught-rejection.js (7:20) @ Timeout.eval [as _onTimeout] ⨯ unhandledRejection: Error: catch this rejection at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { @@ -384,15 +363,7 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error('catch this rejection')) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - ⨯ pages/uncaught-rejection.js (7:20) @ Timeout.eval [as _onTimeout] - ⨯ unhandledRejection: Error: catch this rejection + ⨯ unhandledRejection: Error: catch this rejection at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { @@ -400,15 +371,7 @@ describe('server-side dev errors', () => { | ^ 8 | }, 10) 9 | return { - 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error('catch this rejection')) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - " + 10 | props: {}," `) } }) @@ -422,10 +385,13 @@ describe('server-side dev errors', () => { }) const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + .replace( + '⚠ Fast Refresh had to perform a full reload due to a runtime error.', + '' + ) + .trim() // FIXME(veil): error repeated - // FIXME(veil): codeframe repeated after " ⨯ unhandledRejection: Error:" if (isTurbopack) { - // TODO(veil): digest: undefined should be omitted? expect(stderrOutput).toMatchInlineSnapshot(` "Error: at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) @@ -436,79 +402,55 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js (7:20) @ Timeout._onTimeout - ⨯ test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js (7:20) @ Timeout._onTimeout ⨯ unhandledRejection: Error: - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:20) { - digest: undefined - } + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error()) - | ^ + | ^ 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ unhandledRejection: Error: - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:20) { - digest: undefined - } + ⨯ unhandledRejection: Error: + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error()) - | ^ + | ^ 8 | }, 10) 9 | return { - 10 | props: {}, - " + 10 | props: {}," `) } else { expect(stderrOutput).toMatchInlineSnapshot(` - "Error: - at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error()) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - ⨯ pages/uncaught-empty-rejection.js (7:20) @ Timeout.eval [as _onTimeout] - ⨯ unhandledRejection: Error: - at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error()) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error()) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - ⨯ pages/uncaught-empty-rejection.js (7:20) @ Timeout.eval [as _onTimeout] - ⨯ unhandledRejection: Error: - at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error()) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | Promise.reject(new Error()) - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - " - `) + "Error: + at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) + 5 | export async function getServerSideProps() { + 6 | setTimeout(() => { + > 7 | Promise.reject(new Error()) + | ^ + 8 | }, 10) + 9 | return { + 10 | props: {}, + ⨯ unhandledRejection: Error: + at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) + 5 | export async function getServerSideProps() { + 6 | setTimeout(() => { + > 7 | Promise.reject(new Error()) + | ^ + 8 | }, 10) + 9 | return { + 10 | props: {}, + ⨯ unhandledRejection: Error: + at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) + 5 | export async function getServerSideProps() { + 6 | setTimeout(() => { + > 7 | Promise.reject(new Error()) + | ^ + 8 | }, 10) + 9 | return { + 10 | props: {}," + `) } }) @@ -521,10 +463,13 @@ describe('server-side dev errors', () => { }) const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + .replace( + '⚠ Fast Refresh had to perform a full reload due to a runtime error.', + '' + ) + .trim() // FIXME(veil): error repeated - // FIXME(veil): codeframe repeated after " ⨯ unhandledRejection: Error:" if (isTurbopack) { - // TODO(veil): digest: undefined should be omitted? expect(stderrOutput).toMatchInlineSnapshot(` "Error: catch this exception at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) @@ -535,31 +480,24 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ test/integration/server-side-dev-errors/pages/uncaught-exception.js (7:11) @ Timeout._onTimeout - ⨯ test/integration/server-side-dev-errors/pages/uncaught-exception.js (7:11) @ Timeout._onTimeout ⨯ uncaughtException: Error: catch this exception - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:11) { - digest: undefined - } + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error('catch this exception') - | ^ + | ^ 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ uncaughtException: Error: catch this exception - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:11) { - digest: undefined - } + ⨯ uncaughtException: Error: catch this exception + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error('catch this exception') - | ^ + | ^ 8 | }, 10) 9 | return { - 10 | props: {}, - " + 10 | props: {}," `) } else { expect(stderrOutput).toMatchInlineSnapshot(` @@ -572,7 +510,6 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ pages/uncaught-exception.js (7:11) @ Timeout.eval [as _onTimeout] ⨯ uncaughtException: Error: catch this exception at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { @@ -582,15 +519,7 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error('catch this exception') - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - ⨯ pages/uncaught-exception.js (7:11) @ Timeout.eval [as _onTimeout] - ⨯ uncaughtException: Error: catch this exception + ⨯ uncaughtException: Error: catch this exception at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { @@ -598,15 +527,7 @@ describe('server-side dev errors', () => { | ^ 8 | }, 10) 9 | return { - 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error('catch this exception') - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - " + 10 | props: {}," `) } }) @@ -620,10 +541,13 @@ describe('server-side dev errors', () => { }) const stderrOutput = stripAnsi(stderr.slice(stderrIdx)) + .replace( + '⚠ Fast Refresh had to perform a full reload due to a runtime error.', + '' + ) + .trim() // FIXME(veil): error repeated - // FIXME(veil): codeframe repeated after " ⨯ unhandledRejection: Error:" if (isTurbopack) { - // TODO(veil): digest: undefined should be omitted? expect(stderrOutput).toMatchInlineSnapshot(` "Error: at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) @@ -634,79 +558,55 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js (7:11) @ Timeout._onTimeout - ⨯ test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js (7:11) @ Timeout._onTimeout ⨯ uncaughtException: Error: - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:11) { - digest: undefined - } + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error() - | ^ + | ^ 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ uncaughtException: Error: - at Timeout._onTimeout (./test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:11) { - digest: undefined - } + ⨯ uncaughtException: Error: + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error() - | ^ + | ^ 8 | }, 10) 9 | return { - 10 | props: {}, - " + 10 | props: {}," `) } else { expect(stderrOutput).toMatchInlineSnapshot(` - "Error: - at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error() - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - ⨯ pages/uncaught-empty-exception.js (7:11) @ Timeout.eval [as _onTimeout] - ⨯ uncaughtException: Error: - at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error() - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error() - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - ⨯ pages/uncaught-empty-exception.js (7:11) @ Timeout.eval [as _onTimeout] - ⨯ uncaughtException: Error: - at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error() - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - 5 | export async function getServerSideProps() { - 6 | setTimeout(() => { - > 7 | throw new Error() - | ^ - 8 | }, 10) - 9 | return { - 10 | props: {}, - " - `) + "Error: + at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) + 5 | export async function getServerSideProps() { + 6 | setTimeout(() => { + > 7 | throw new Error() + | ^ + 8 | }, 10) + 9 | return { + 10 | props: {}, + ⨯ uncaughtException: Error: + at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) + 5 | export async function getServerSideProps() { + 6 | setTimeout(() => { + > 7 | throw new Error() + | ^ + 8 | }, 10) + 9 | return { + 10 | props: {}, + ⨯ uncaughtException: Error: + at Timeout.eval [as _onTimeout] (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) + 5 | export async function getServerSideProps() { + 6 | setTimeout(() => { + > 7 | throw new Error() + | ^ + 8 | }, 10) + 9 | return { + 10 | props: {}," + `) } }) })