diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index d51739e98e04f..6a7bed2af50ea 100644 --- a/crates/napi/src/next_api/project.rs +++ b/crates/napi/src/next_api/project.rs @@ -2,7 +2,7 @@ use std::{path::PathBuf, sync::Arc, thread, time::Duration}; use anyhow::{anyhow, bail, Context, Result}; use napi::{ - bindgen_prelude::External, + bindgen_prelude::{within_runtime_if_available, External}, threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, JsFunction, Status, }; @@ -1202,6 +1202,16 @@ pub async fn project_get_source_map( Ok(source_map) } +#[napi] +pub fn project_get_source_map_sync( + #[napi(ts_arg_type = "{ __napiType: \"Project\" }")] project: External, + file_path: String, +) -> napi::Result> { + within_runtime_if_available(|| { + tokio::runtime::Handle::current().block_on(project_get_source_map(project, file_path)) + }) +} + /// Runs exit handlers for the project registered using the [`ExitHandler`] API. #[napi] pub async fn project_on_exit( diff --git a/packages/next/src/build/swc/generated-native.d.ts b/packages/next/src/build/swc/generated-native.d.ts index 17494f97077ac..898d829882723 100644 --- a/packages/next/src/build/swc/generated-native.d.ts +++ b/packages/next/src/build/swc/generated-native.d.ts @@ -281,10 +281,18 @@ export function projectGetSourceForAsset( project: { __napiType: 'Project' }, filePath: string ): Promise +export function projectGetSourceForAssetSync( + project: { __napiType: 'Project' }, + filePath: string +): string | null export function projectGetSourceMap( project: { __napiType: 'Project' }, filePath: string ): Promise +export function projectGetSourceMapSync( + project: { __napiType: 'Project' }, + filePath: string +): string | null /** Runs exit handlers for the project registered using the [`ExitHandler`] API. */ export function projectOnExit(project: { __napiType: 'Project' }): Promise export function rootTaskDispose(rootTask: { __napiType: 'RootTask' }): void diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 1f96f027724f1..86455e1d0414d 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -759,6 +759,10 @@ function bindingToApi( return binding.projectGetSourceMap(this._nativeProject, filePath) } + getSourceMapSync(filePath: string): string | null { + return binding.projectGetSourceMapSync(this._nativeProject, filePath) + } + updateInfoSubscribe(aggregationMs: number) { return subscribe>(true, async (callback) => binding.projectUpdateInfoSubscribe( diff --git a/packages/next/src/build/swc/types.ts b/packages/next/src/build/swc/types.ts index 12ba804fa69f9..fb6ae80142f56 100644 --- a/packages/next/src/build/swc/types.ts +++ b/packages/next/src/build/swc/types.ts @@ -204,6 +204,7 @@ export interface Project { getSourceForAsset(filePath: string): Promise getSourceMap(filePath: string): Promise + getSourceMapSync(filePath: string): string | null traceSource( stackFrame: TurbopackStackFrame diff --git a/packages/next/src/server/dev/hot-reloader-turbopack.ts b/packages/next/src/server/dev/hot-reloader-turbopack.ts index 15092ff62bf14..272ce33ddd9ce 100644 --- a/packages/next/src/server/dev/hot-reloader-turbopack.ts +++ b/packages/next/src/server/dev/hot-reloader-turbopack.ts @@ -1,6 +1,7 @@ import type { Socket } from 'net' import { mkdir, writeFile } from 'fs/promises' import { join, extname } from 'path' +import { pathToFileURL } from 'url' import ws from 'next/dist/compiled/ws' @@ -20,6 +21,7 @@ import type { Endpoint, WrittenEndpoint, TurbopackResult, + Project, } from '../../build/swc/types' import { createDefineEnv } from '../../build/swc' import * as Log from '../../build/output/log' @@ -85,6 +87,10 @@ import { isAppPageRouteDefinition } from '../route-definitions/app-page-route-de import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths' import { getNodeDebugType } from '../lib/utils' import { isMetadataRouteFile } from '../../lib/metadata/is-metadata-route' +import { + setBundlerFindSourceMapImplementation, + type ModernSourceMapPayload, +} from '../patch-error-inspect' // import { getSupportedBrowsers } from '../../build/utils' const wsServer = new ws.Server({ noServer: true }) @@ -96,6 +102,51 @@ const isTestMode = !!( const sessionId = Math.floor(Number.MAX_SAFE_INTEGER * Math.random()) +/** + * Replaces turbopack://[project] with the specified project in the `source` field. + */ +function rewriteTurbopackSources( + projectRoot: string, + sourceMap: ModernSourceMapPayload +): void { + if ('sections' in sourceMap) { + for (const section of sourceMap.sections) { + rewriteTurbopackSources(projectRoot, section.map) + } + } else { + for (let i = 0; i < sourceMap.sources.length; i++) { + sourceMap.sources[i] = pathToFileURL( + join( + projectRoot, + sourceMap.sources[i].replace(/turbopack:\/\/\[project\]/, '') + ) + ).toString() + } + } +} + +function getSourceMapFromTurbopack( + project: Project, + projectRoot: string, + sourceURL: string +): ModernSourceMapPayload | undefined { + let sourceMapJson: string | null = null + + try { + sourceMapJson = project.getSourceMapSync(sourceURL) + } catch (err) {} + + if (sourceMapJson === null) { + return undefined + } else { + const payload: ModernSourceMapPayload = JSON.parse(sourceMapJson) + // The sourcemap from Turbopack is not yet written to disk so its `sources` + // are not absolute paths yet. We need to rewrite them to be absolute paths. + rewriteTurbopackSources(projectRoot, payload) + return payload + } +} + export async function createHotReloaderTurbopack( opts: SetupOpts, serverFields: ServerFields, @@ -185,7 +236,13 @@ export async function createHotReloaderTurbopack( memoryLimit: opts.nextConfig.experimental.turbo?.memoryLimit, } ) - opts.onDevServerCleanup?.(() => project.onExit()) + setBundlerFindSourceMapImplementation( + getSourceMapFromTurbopack.bind(null, project, dir) + ) + opts.onDevServerCleanup?.(async () => { + setBundlerFindSourceMapImplementation(() => undefined) + await project.onExit() + }) const entrypointsSubscription = project.entrypointsSubscribe() const currentWrittenEntrypoints: Map = new Map() diff --git a/packages/next/src/server/patch-error-inspect.ts b/packages/next/src/server/patch-error-inspect.ts index f0c8a7717e5c6..d8e3ef4869ce7 100644 --- a/packages/next/src/server/patch-error-inspect.ts +++ b/packages/next/src/server/patch-error-inspect.ts @@ -1,4 +1,7 @@ -import { findSourceMap, type SourceMapPayload } from 'module' +import { + findSourceMap as nativeFindSourceMap, + type SourceMapPayload, +} from 'module' import * as path from 'path' import * as url from 'url' import type * as util from 'util' @@ -9,6 +12,21 @@ import { getOriginalCodeFrame } from '../client/components/react-dev-overlay/ser import { workUnitAsyncStorage } from './app-render/work-unit-async-storage.external' import { dim } from '../lib/picocolors' +type FindSourceMapPayload = ( + sourceURL: string +) => ModernSourceMapPayload | undefined +// Find a source map using the bundler's API. +// This is only a fallback for when Node.js fails to due to bugs e.g. https://github.com/nodejs/node/issues/52102 +// TODO: Remove once all supported Node.js versions are fixed. +// TODO(veil): Set from Webpack as well +let bundlerFindSourceMapPayload: FindSourceMapPayload = () => undefined + +export function setBundlerFindSourceMapImplementation( + findSourceMapImplementation: FindSourceMapPayload +): void { + bundlerFindSourceMapPayload = findSourceMapImplementation +} + /** * https://tc39.es/source-map/#index-map */ @@ -31,7 +49,7 @@ interface ModernRawSourceMap extends SourceMapPayload { ignoreList?: number[] } -type ModernSourceMapPayload = ModernRawSourceMap | IndexSourceMap +export type ModernSourceMapPayload = ModernRawSourceMap | IndexSourceMap interface IgnoreableStackFrame extends StackFrame { ignored: boolean @@ -138,7 +156,7 @@ function getSourcemappedFrameIfPossible( } const sourceMapCacheEntry = sourceMapCache.get(frame.file) - let sourceMap: SyncSourceMapConsumer + let sourceMapConsumer: SyncSourceMapConsumer let sourceMapPayload: ModernSourceMapPayload if (sourceMapCacheEntry === undefined) { let sourceURL = frame.file @@ -148,25 +166,27 @@ function getSourcemappedFrameIfPossible( if (sourceURL.startsWith('/')) { sourceURL = url.pathToFileURL(frame.file).toString() } - const moduleSourceMap = findSourceMap(sourceURL) - if (moduleSourceMap === undefined) { + const maybeSourceMapPayload = + nativeFindSourceMap(sourceURL)?.payload ?? + bundlerFindSourceMapPayload(sourceURL) + if (maybeSourceMapPayload === undefined) { return null } - sourceMapPayload = moduleSourceMap.payload - sourceMap = new SyncSourceMapConsumer( + sourceMapPayload = maybeSourceMapPayload + sourceMapConsumer = new SyncSourceMapConsumer( // @ts-expect-error -- Module.SourceMap['version'] is number but SyncSourceMapConsumer wants a string sourceMapPayload ) sourceMapCache.set(frame.file, { - map: sourceMap, + map: sourceMapConsumer, payload: sourceMapPayload, }) } else { - sourceMap = sourceMapCacheEntry.map + sourceMapConsumer = sourceMapCacheEntry.map sourceMapPayload = sourceMapCacheEntry.payload } - const sourcePosition = sourceMap.originalPositionFor({ + const sourcePosition = sourceMapConsumer.originalPositionFor({ column: frame.column ?? 0, line: frame.lineNumber ?? 1, }) @@ -176,7 +196,7 @@ function getSourcemappedFrameIfPossible( } const sourceContent: string | null = - sourceMap.sourceContentFor( + sourceMapConsumer.sourceContentFor( sourcePosition.source, /* returnNullOnMissing */ true ) ?? null diff --git a/test/development/middleware-errors/index.test.ts b/test/development/middleware-errors/index.test.ts index 419a86814e086..5b1cbe3de4ab6 100644 --- a/test/development/middleware-errors/index.test.ts +++ b/test/development/middleware-errors/index.test.ts @@ -41,17 +41,25 @@ describe('middleware - development errors', () => { isTurbopack ? '\n ⨯ Error: boom' + // TODO(veil): Should be sourcemapped - '\n at __TURBOPACK__default__export__ (' + '\n at __TURBOPACK__default__export__ (middleware.js:3:14)' + + '\n 1 |' + + '\n 2 | export default function () {' + + "\n> 3 | throw new Error('boom')" + + '\n | ^' + + '\n 4 | }' + + '\n' : '\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 + // already asserted on codeframe earlier } else { expect(stripAnsi(next.cliOutput)).toContain( - "\n> 3 | throw new Error('boom')" + '' + + "\n> 3 | throw new Error('boom')" + + '\n | ^' ) } }) @@ -92,8 +100,17 @@ describe('middleware - development errors', () => { }) expect(stripAnsi(next.cliOutput)).toContain( isTurbopack - ? // TODO(veil): Should be sourcemapped - ' ⨯ unhandledRejection: Error: async boom!\n at throwError (/' + ? ' ⨯ unhandledRejection: Error: async boom!' + + '\n at throwError (middleware.js:4:14)' + + // TODO(veil): Sourcemap to original name i.e. "default" + '\n at __TURBOPACK__default__export__ (middleware.js:7:8)' + + "\n 2 | import { NextResponse } from 'next/server'" + + '\n 3 | async function throwError() {' + + "\n> 4 | throw new Error('async boom!')" + + '\n | ^' + + '\n 5 | }' + + '\n 6 | export default function () {' + + '\n 7 | throwError()' : '\n ⨯ unhandledRejection: Error: async boom!' + '\n at throwError (middleware.js:4:14)' + '\n at throwError (middleware.js:7:8)' + @@ -101,10 +118,12 @@ describe('middleware - development errors', () => { '\n at eval (webpack' ) if (isTurbopack) { - // TODO(veil): Should have codeframe + // already asserted on codeframe earlier } else { expect(stripAnsi(next.cliOutput)).toContain( - "> 4 | throw new Error('async boom!')" + '' + + "\n> 4 | throw new Error('async boom!')" + + '\n | ^' ) } }) @@ -142,19 +161,21 @@ describe('middleware - development errors', () => { // In CI, it prefixes "Dynamic Code Evaluation". expect(stripAnsi(next.cliOutput)).toContain( // TODO(veil): Should be sourcemapped - '\n at __TURBOPACK__default__export__ (/' + '\n at __TURBOPACK__default__export__ (.next/' ) } expect(stripAnsi(next.cliOutput)).toContain( isTurbopack ? '\n ⨯ Error [ReferenceError]: test is not defined' + - // TODO(veil): Should be sourcemapped - '\n at eval ' + '\n at eval (middleware.js:4:8)' + + '\n at (middleware.js:4:8)' + + // TODO(veil): Should be ignore-listed + '\n at fn (node_modules' : '\n ⨯ Error [ReferenceError]: test is not defined' + - // TODO: Redundant and not clickable + // TODO(veil): Redundant and not clickable '\n at eval (file://webpack-internal:///(middleware)/./middleware.js)' + '\n at eval (middleware.js:4:8)' + - // TODO: Should be ignore-listed + // TODO(veil): Should be ignore-listed '\n at fn (node_modules' ) expect(stripAnsi(next.cliOutput)).toContain( 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 72a531f185277..9620e226ef0de 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 @@ -29,8 +29,16 @@ describe('app-dir - server source maps edge runtime', () => { expect(normalizeCliOutput(next.cliOutput.slice(outputIndex))).toContain( isTurbopack ? '\nError: Boom' + - // TODO(veil): Should be sourcemapped - '\n at logError (.next' + '\n at logError (app/rsc-error-log/page.js:2:16)' + + '\n at Page (app/rsc-error-log/page.js:6:2)' + + '\n 1 | function logError() {' + + "\n> 2 | console.error(new Error('Boom'))" + + '\n | ^' + + '\n 3 | }' + + '\n 4 |' + + '\n 5 | export default function Page() { {' + + '\n ' + + '\n}' : '\nError: Boom' + '\n at logError (app/rsc-error-log/page.js:2:16)' + '\n at logError (app/rsc-error-log/page.js:6:2)' + @@ -61,8 +69,16 @@ describe('app-dir - server source maps edge runtime', () => { expect(cliOutput).toContain( isTurbopack ? '\n ⨯ Error: Boom' + - // TODO(veil): Apply sourcemap - '\n at throwError (/' + '\n at throwError (app/ssr-throw/page.js:4:8)' + + '\n at Page (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: '" : '\n ⨯ Error: Boom' + '\n at throwError (app/ssr-throw/page.js:4:8)' + // TODO(veil): Method name should be "Page" @@ -96,8 +112,15 @@ describe('app-dir - server source maps edge runtime', () => { expect(cliOutput).toContain( isTurbopack ? '\n ⨯ Error: Boom' + - // TODO(veil): Apply sourcemap - '\n at throwError (/' + '\n at throwError (app/rsc-throw/page.js:2:8)' + + '\n at Page (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: '" : '\n ⨯ Error: Boom' + '\n at throwError (app/rsc-throw/page.js:2:8)' + // TODO(veil): Method name should be "Page" 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 674a0effe84e4..c3fd3a9633bd0 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 @@ -165,8 +165,16 @@ describe('app-dir - server source maps', () => { expect(cliOutput).toContain( isTurbopack ? '\n ⨯ Error: Boom' + - // TODO(veil): Apply sourcemap - '\n at throwError (/' + '\n at throwError (app/ssr-throw/Thrower.js:4:8)' + + '\n at Thrower (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: '" : '\n ⨯ Error: Boom' + '\n at throwError (app/ssr-throw/Thrower.js:4:8)' + // TODO(veil): Method name should be "Thrower" 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 91508e4537201..b91c1e210dd04 100644 --- a/test/integration/edge-runtime-dynamic-code/test/index.test.js +++ b/test/integration/edge-runtime-dynamic-code/test/index.test.js @@ -108,23 +108,45 @@ describe.each([ if (title === 'Middleware') { expect(output).toContain( isTurbopack - ? // TODO(veil): Apply sourcemap - '\n at usingEval (/' + ? '' + + '\n at usingEval (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/lib/utils.js:11:16)' + + '\n at middleware (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/middleware.js:12:52)' + + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' : '\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 usingEval (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/lib/utils.js:11:16)' + + '\n at handler (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/pages/api/route.js:13:22)' + + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' : '\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() {' ) } + + // TODO(veil): Inconsistent cursor position + if (isTurbopack) { + expect(output).toContain( + '' + + "\n> 11 | return { value: eval('100') }" + + '\n | ^' + ) + } else { + expect(output).toContain( + '' + + "\n> 11 | return { value: eval('100') }" + + '\n | ^' + ) + } }) it('does not show warning when no code uses eval', async () => { @@ -148,36 +170,47 @@ describe.each([ if (title === 'Middleware') { expect(output).toContain( isTurbopack - ? // TODO(veil): Apply sourcemap - '\n at usingWebAssemblyCompile (/' + ? '' + + '\n at usingWebAssemblyCompile (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/lib/wasm.js:22:17)' + + '\n at middleware (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/middleware.js:24:68)' + + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' : '\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' + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' ) - 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 usingWebAssemblyCompile (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/lib/wasm.js:22:17)' + + '\n at handler (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/pages/api/route.js:17:42)' + + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' + : '' + + '\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 + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\n at' ) + // TODO(veil): Inconsistent cursor position if (isTurbopack) { - // TODO(veil): Display codeframe + expect(output).toContain( + '' + + '\n> 22 | const module = await WebAssembly.compile(SQUARE_WASM_BUFFER)' + + '\n | ^' + ) } else { expect(output).toContain( - '\n> 22 | const module = await WebAssembly.compile(SQUARE_WASM_BUFFER)' + '' + + '\n> 22 | const module = await WebAssembly.compile(SQUARE_WASM_BUFFER)' + + '\n | ^' ) } } @@ -194,34 +227,45 @@ describe.each([ 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 usingWebAssemblyInstantiateWithBuffer (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/lib/wasm.js:28:23)' + + '\n at async middleware (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/middleware.js:37:29)' + + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' + : '' + + '\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 + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\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, {})' + '' + + '\n> 28 | const { instance } = await WebAssembly.instantiate(SQUARE_WASM_BUFFER, {})' + + '\n | ^' ) } 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)' + + ? '' + + // TODO(veil): Turbopack duplicates project path + '\n at async usingWebAssemblyInstantiateWithBuffer (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/lib/wasm.js:28:23)' + + '\n at async handler (../../test/integration/edge-runtime-dynamic-code/test/integration/edge-runtime-dynamic-code/pages/api/route.js:21:16)' + + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\n at' + : '' + + '\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 ' + // Next.js internal frame. Feel free to adjust. + // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules + '\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, {})' + '' + + '\n> 28 | const { instance } = await WebAssembly.instantiate(SQUARE_WASM_BUFFER, {})' + + '\n | ^' ) } }) 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 732e8d9ae0d4c..9a6e6aaf1869d 100644 --- a/test/integration/server-side-dev-errors/test/index.test.js +++ b/test/integration/server-side-dev-errors/test/index.test.js @@ -66,24 +66,19 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' + - '\n ⨯ ../../test/integration/server-side-dev-errors/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)' + - // TODO(veil): Should be sourcemapped - '\n at getStaticProps (webpack' + '\n at getStaticProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + + // TODO(veil): Should be ignore-listed + '\n at fn' ) } else { 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 getStaticProps (webpack' + // TODO(veil): Should be ignore-listed + '\n at fn' ) } expect(stderr).toContain( @@ -120,24 +115,20 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' + - '\n ⨯ ../../test/integration/server-side-dev-errors/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)' + - // TODO(veil): Should be sourcemapped - '\n at' + // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + + // TODO(veil): Should be ignore-listed + '\n at fn' ) } else { expect(stderrOutput).toStartWith( '⨯ 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 getServerSideProps (webpack' + // TODO(veil): Should be ignore-listed + '\n at fn' ) } expect(stderrOutput).toContain( @@ -174,24 +165,20 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' + - '\n ⨯ ../../test/integration/server-side-dev-errors/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)' + - // TODO(veil): Should be sourcemapped - '\n at getServerSideProps (webpack' + // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. + '\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 ignore-listed + '\n at fn' ) } else { expect(stderrOutput).toStartWith( '⨯ 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 getServerSideProps (webpack' + // TODO(veil): Should be ignore-listed + '\n at fn' ) } expect(stderrOutput).toContain( @@ -228,24 +215,20 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' + - '\n ⨯ ../../test/integration/server-side-dev-errors/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)' + - // TODO(veil): Should be sourcemapped - '\n at resolver (webpack' + // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. + '\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 ignore-listed + '\n at async' ) } else { expect(stderrOutput).toStartWith( '⨯ 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 resolver (webpack' + // TODO(veil): Should be ignore-listed + '\n at async' ) } expect(stderrOutput).toContain( @@ -283,14 +266,9 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() // FIXME(veil): error repeated if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' + - '\n ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' - ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + - // TODO(veil): Should not include `turbopack://[project]` + // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. '\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' @@ -348,8 +326,6 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js (7:20) @ Timeout._onTimeout - ⨯ ../../test/integration/server-side-dev-errors/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/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { @@ -429,8 +405,6 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js (7:20) @ Timeout._onTimeout - ⨯ ../../test/integration/server-side-dev-errors/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/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) 5 | export async function getServerSideProps() { @@ -509,8 +483,6 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js (7:11) @ Timeout._onTimeout - ⨯ ../../test/integration/server-side-dev-errors/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/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { @@ -589,8 +561,6 @@ describe('server-side dev errors', () => { 8 | }, 10) 9 | return { 10 | props: {}, - ⨯ ../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js (7:11) @ Timeout._onTimeout - ⨯ ../../test/integration/server-side-dev-errors/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/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) 5 | export async function getServerSideProps() {