From 595c340d6f5a05bf44d38d5268f539d2d56192b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 22 Feb 2024 14:33:16 +0100 Subject: [PATCH 01/39] refactor(error-overlay): improve server code for webpack/turbopack middleware --- .../parseNotFoundError.ts | 15 +- .../server/middleware-turbopack.ts | 36 +- .../react-dev-overlay/server/middleware.ts | 316 ++++++------------ .../react-dev-overlay/server/shared.ts | 53 ++- .../lib/router-utils/setup-dev-bundler.ts | 10 +- 5 files changed, 178 insertions(+), 252 deletions(-) diff --git a/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseNotFoundError.ts b/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseNotFoundError.ts index 5d50db00c18cc..2e299d6a14978 100644 --- a/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseNotFoundError.ts +++ b/packages/next/src/build/webpack/plugins/wellknown-errors-plugin/parseNotFoundError.ts @@ -49,18 +49,21 @@ async function getSourceFrame( compilation: any ): Promise<{ frame: string; lineNumber: string; column: string }> { try { - const loc = input.loc - ? input.loc - : input.dependencies.map((d: any) => d.loc).filter(Boolean)[0] + const loc = + input.loc || input.dependencies.map((d: any) => d.loc).filter(Boolean)[0] const originalSource = input.module.originalSource() const result = await createOriginalStackFrame({ - line: loc.start.line, - column: loc.start.column, source: originalSource, rootDirectory: compilation.options.context!, modulePath: fileName, - frame: {}, + frame: { + arguments: [], + file: fileName, + methodName: '', + lineNumber: loc.start.line, + column: loc.start.column, + }, }) return { 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 911e1e3536c42..c03f8f42a6a2f 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 @@ -1,8 +1,11 @@ import type { IncomingMessage, ServerResponse } from 'http' -import { findSourcePackage, type OriginalStackFrameResponse } from './shared' +import { + findSourcePackage, + getOriginalCodeFrame, + type OriginalStackFrameResponse, +} from './shared' import fs, { constants as FS } from 'fs/promises' -import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' import { launchEditor } from '../internal/helpers/launchEditor' interface Project { @@ -15,9 +18,9 @@ interface Project { interface TurbopackStackFrame { // 1-based column: number | null - // 1-based file: string isServer: boolean + // 1-based line: number | null methodName: string | null isInternal?: boolean @@ -70,33 +73,20 @@ async function batchedTraceSource( export async function createOriginalStackFrame( project: Project, - frame: TurbopackStackFrame + _frame: TurbopackStackFrame ): Promise { - const traced = await batchedTraceSource(project, frame) + const traced = await batchedTraceSource(project, _frame) if (!traced) { - const sourcePackage = findSourcePackage(frame.file) + const sourcePackage = findSourcePackage(_frame.file, _frame.methodName) if (sourcePackage) return { sourcePackage } return null } + const { frame, source } = traced return { - originalStackFrame: traced.frame, - originalCodeFrame: - traced.source === null - ? null - : codeFrameColumns( - traced.source, - { - start: { - // 1-based, but -1 means start line without highlighting - line: traced.frame.lineNumber ?? -1, - // 1-based, but 0 means whole line without column highlighting - column: traced.frame.column ?? 0, - }, - }, - { forceColor: true } - ), - sourcePackage: findSourcePackage(traced.frame.file), + originalStackFrame: frame, + originalCodeFrame: getOriginalCodeFrame(frame, source), + sourcePackage: findSourcePackage(frame.file, frame.methodName), } } 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 9b593fae18d94..3a0a5a2430e1a 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 @@ -1,22 +1,25 @@ -import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' import { constants as FS, promises as fs } from 'fs' -import type { IncomingMessage, ServerResponse } from 'http' import path from 'path' import { SourceMapConsumer } from 'next/dist/compiled/source-map08' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' -import url from 'url' -import type webpack from 'webpack' import { getRawSourceMap } from '../internal/helpers/getRawSourceMap' import { launchEditor } from '../internal/helpers/launchEditor' -import { findSourcePackage, type OriginalStackFrameResponse } from './shared' +import { + findSourcePackage, + getOriginalCodeFrame, + type OriginalStackFrameResponse, +} from './shared' export { getServerError } from '../internal/helpers/nodeStackFrames' export { parseStack } from '../internal/helpers/parseStack' -export type OverlayMiddlewareOptions = { - rootDirectory: string - stats(): webpack.Stats | null - serverStats(): webpack.Stats | null - edgeServerStats(): webpack.Stats | null +import type { IncomingMessage, ServerResponse } from 'http' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' +import type webpack from 'webpack' + +interface WebpackStackFrame extends StackFrame { + isEdgeServer?: boolean + isServer?: boolean + isAppDirectory?: boolean + errorMessage?: string } type Source = { map: () => any } | null @@ -29,43 +32,25 @@ function getModuleById( id: string | undefined, compilation: webpack.Compilation ) { - return [...compilation.modules].find((searchModule) => { - const moduleId = getModuleId(compilation, searchModule) - return moduleId === id - }) + return [...compilation.modules].find( + (searchModule) => getModuleId(compilation, searchModule) === id + ) } function findModuleNotFoundFromError(errorMessage: string | undefined) { - const match = errorMessage?.match(/'([^']+)' module/) - return match && match[1] + return errorMessage?.match(/'([^']+)' module/)?.[1] } function getModuleSource(compilation: any, module: any): any { + if (!module) return null return ( - (module && - compilation.codeGenerationResults - .get(module) - ?.sources.get('javascript')) ?? + compilation.codeGenerationResults.get(module)?.sources.get('javascript') ?? null ) } function getSourcePath(source: string) { - // Webpack prefixes certain source paths with this path - if (source.startsWith('webpack:///')) { - return source.substring(11) - } - - // Make sure library name is filtered out as well - if (source.startsWith('webpack://_N_E/')) { - return source.substring(15) - } - - if (source.startsWith('webpack://')) { - return source.substring(10) - } - - return source + return source.replace(/^(webpack:\/\/\/|webpack:\/\/|webpack:\/\/_N_E\/)/, '') } async function findOriginalSourcePositionAndContent( @@ -108,81 +93,43 @@ function findOriginalSourcePositionAndContentFromCompilation( } export async function createOriginalStackFrame({ - line, - column, source, moduleId, modulePath, rootDirectory, frame, - errorMessage, - clientCompilation, - serverCompilation, - edgeCompilation, + compilation, }: { - line: number - column: number | null source: any moduleId?: string modulePath?: string rootDirectory: string - frame: any - errorMessage?: string - clientCompilation?: webpack.Compilation - serverCompilation?: webpack.Compilation - edgeCompilation?: webpack.Compilation + frame: WebpackStackFrame + compilation?: webpack.Compilation }): Promise { + const { lineNumber: line, column, errorMessage } = frame const moduleNotFound = findModuleNotFoundFromError(errorMessage) const result = await (async () => { if (moduleNotFound) { - let moduleNotFoundResult = null - - if (clientCompilation) { - moduleNotFoundResult = - findOriginalSourcePositionAndContentFromCompilation( - moduleId, - moduleNotFound, - clientCompilation - ) - } + if (!compilation) return null - if (moduleNotFoundResult === null && serverCompilation) { - moduleNotFoundResult = - findOriginalSourcePositionAndContentFromCompilation( - moduleId, - moduleNotFound, - serverCompilation - ) - } - - if (moduleNotFoundResult === null && edgeCompilation) { - moduleNotFoundResult = - findOriginalSourcePositionAndContentFromCompilation( - moduleId, - moduleNotFound, - edgeCompilation - ) - } - - return moduleNotFoundResult + return findOriginalSourcePositionAndContentFromCompilation( + moduleId, + moduleNotFound, + compilation + ) } // This returns 1-based lines and 0-based columns return await findOriginalSourcePositionAndContent(source, { - line, + line: line ?? 1, column, }) })() - if (result === null) { - return null - } + if (!result?.sourcePosition.source) return null const { sourcePosition, sourceContent } = result - if (!sourcePosition.source) { - return null - } - const filePath = path.resolve( rootDirectory, getSourcePath( @@ -193,7 +140,7 @@ export async function createOriginalStackFrame({ ) ) - const originalFrame: StackFrame = { + const traced = { file: sourceContent ? path.relative(rootDirectory, filePath) : sourcePosition.source, @@ -207,28 +154,12 @@ export async function createOriginalStackFrame({ ?.replace('__WEBPACK_DEFAULT_EXPORT__', 'default') ?.replace('__webpack_exports__.', ''), arguments: [], - } - - const originalCodeFrame: string | null = - !(originalFrame.file?.includes('node_modules') ?? true) && - sourceContent && - sourcePosition.line - ? (codeFrameColumns( - sourceContent, - { - start: { - line: sourcePosition.line, - column: (sourcePosition.column ?? 0) + 1, - }, - }, - { forceColor: true } - ) as string) - : null + } satisfies StackFrame return { - originalStackFrame: originalFrame, - originalCodeFrame, - sourcePackage: findSourcePackage(filePath) ?? null, + originalStackFrame: traced, + originalCodeFrame: getOriginalCodeFrame(traced, sourceContent), + sourcePackage: findSourcePackage(traced.file, frame.methodName), } } @@ -272,45 +203,45 @@ export async function getSourceById( } } -function getOverlayMiddleware(options: OverlayMiddlewareOptions) { +export function getOverlayMiddleware(options: { + rootDirectory: string + stats(): webpack.Stats | null + serverStats(): webpack.Stats | null + edgeServerStats(): webpack.Stats | null +}) { return async function ( req: IncomingMessage, res: ServerResponse, next: Function ) { - const { pathname, query } = url.parse(req.url!, true) + const { pathname, searchParams } = new URL(req.url!, 'http://n') + + const frame = { + file: searchParams.get('file') as string, + methodName: searchParams.get('methodName') as string, + lineNumber: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, + column: parseInt(searchParams.get('column') ?? '0', 10) || 0, + arguments: searchParams.getAll('arguments'), + isServer: searchParams.get('isServer') === 'true', + isEdgeServer: searchParams.get('isEdgeServer') === 'true', + isAppDirectory: searchParams.get('isAppDirectory') === 'true', + errorMessage: searchParams.get('errorMessage') ?? undefined, + } satisfies WebpackStackFrame if (pathname === '/__nextjs_original-stack-frame') { - const frame = query as unknown as StackFrame & { - isEdgeServer: 'true' | 'false' - isServer: 'true' | 'false' - isAppDirectory: 'true' | 'false' - errorMessage: string | undefined - } - const isAppDirectory = frame.isAppDirectory === 'true' - const isServerError = frame.isServer === 'true' - const isEdgeServerError = frame.isEdgeServer === 'true' - const isClientError = !isServerError && !isEdgeServerError + const { isAppDirectory, isEdgeServer, isServer } = frame + const isClient = !isServer && !isEdgeServer - let sourcePackage = findSourcePackage(frame.file) + let sourcePackage = findSourcePackage(frame.file, frame.methodName) if ( !( - (frame.file?.startsWith('webpack-internal:///') || - frame.file?.startsWith('file://') || - frame.file?.startsWith('webpack://')) && - Boolean(parseInt(frame.lineNumber?.toString() ?? '', 10)) + /^(webpack-internal:\/\/\/|(file|webpack):\/\/)/.test(frame.file) && + frame.lineNumber ) ) { - if (sourcePackage) { - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.write(Buffer.from(JSON.stringify({ sourcePackage }))) - return res.end() - } - res.statusCode = 400 - res.write('Bad Request') - return res.end() + if (sourcePackage) return json(res, { sourcePackage }) + return badRequest(res) } const moduleId: string = frame.file.replace( @@ -324,136 +255,109 @@ function getOverlayMiddleware(options: OverlayMiddlewareOptions) { let source: Source = null - const clientCompilation = options.stats()?.compilation - const serverCompilation = options.serverStats()?.compilation - const edgeCompilation = options.edgeServerStats()?.compilation + let compilation: webpack.Compilation | undefined + const isFile = frame.file.startsWith('file:') try { - if (isClientError || isAppDirectory) { + if (isClient || isAppDirectory) { + compilation = options.stats()?.compilation // Try Client Compilation first // In `pages` we leverage `isClientError` to check // In `app` it depends on if it's a server / client component and when the code throws. E.g. during HTML rendering it's the server/edge compilation. - source = await getSourceById(isFile, moduleId, clientCompilation) + source = await getSourceById(isFile, moduleId, compilation) } // Try Server Compilation // In `pages` this could be something imported in getServerSideProps/getStaticProps as the code for those is tree-shaken. // In `app` this finds server components and code that was imported from a server component. It also covers when client component code throws during HTML rendering. - if ((isServerError || isAppDirectory) && source === null) { - source = await getSourceById(isFile, moduleId, serverCompilation) + if ((isServer || isAppDirectory) && source === null) { + compilation = options.serverStats()?.compilation + source = await getSourceById(isFile, moduleId, compilation) } // Try Edge Server Compilation // Both cases are the same as Server Compilation, main difference is that it covers `runtime: 'edge'` pages/app routes. - if ((isEdgeServerError || isAppDirectory) && source === null) { - source = await getSourceById(isFile, moduleId, edgeCompilation) + if ((isEdgeServer || isAppDirectory) && source === null) { + compilation = options.edgeServerStats()?.compilation + source = await getSourceById(isFile, moduleId, compilation) } } catch (err) { console.log('Failed to get source map:', err) - res.statusCode = 500 - res.write('Internal Server Error') - return res.end() + return internalServerError(res) } - if (source == null) { - if (sourcePackage) { - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.write(Buffer.from(JSON.stringify({ sourcePackage }))) - return res.end() - } - res.statusCode = 204 - res.write('No Content') - return res.end() - } - - const frameLine = parseInt(frame.lineNumber?.toString() ?? '', 10) - let frameColumn: number | null = parseInt( - frame.column?.toString() ?? '', - 10 - ) - if (!frameColumn) { - frameColumn = null + if (!source) { + if (sourcePackage) return json(res, { sourcePackage }) + return noContent(res) } try { const originalStackFrameResponse = await createOriginalStackFrame({ - line: frameLine, - column: frameColumn, - source, frame, + source, moduleId, modulePath, rootDirectory: options.rootDirectory, - errorMessage: frame.errorMessage, - clientCompilation: isClientError ? clientCompilation : undefined, - serverCompilation: isServerError ? serverCompilation : undefined, - edgeCompilation: isEdgeServerError ? edgeCompilation : undefined, + compilation, }) if (originalStackFrameResponse === null) { if (sourcePackage) { - res.statusCode = 200 res.setHeader('Content-Type', 'application/json') res.write(Buffer.from(JSON.stringify({ sourcePackage }))) return res.end() } - res.statusCode = 204 - res.write('No Content') - return res.end() + return noContent(res) } - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.write(Buffer.from(JSON.stringify(originalStackFrameResponse))) - return res.end() + return json(res, originalStackFrameResponse) } catch (err) { console.log('Failed to parse source map:', err) - res.statusCode = 500 - res.write('Internal Server Error') - return res.end() + return internalServerError(res) } } else if (pathname === '/__nextjs_launch-editor') { - const frame = query as unknown as StackFrame - - const frameFile = frame.file?.toString() || null - if (frameFile == null) { - res.statusCode = 400 - res.write('Bad Request') - return res.end() - } + if (!frame.file) return badRequest(res) // frame files may start with their webpack layer, like (middleware)/middleware.js const filePath = path.resolve( options.rootDirectory, - frameFile.replace(/^\([^)]+\)\//, '') + frame.file.replace(/^\([^)]+\)\//, '') ) const fileExists = await fs.access(filePath, FS.F_OK).then( () => true, () => false ) - if (!fileExists) { - res.statusCode = 204 - res.write('No Content') - return res.end() - } - - const frameLine = parseInt(frame.lineNumber?.toString() ?? '', 10) || 1 - const frameColumn = parseInt(frame.column?.toString() ?? '', 10) || 1 + if (!fileExists) return noContent(res) try { - await launchEditor(filePath, frameLine, frameColumn) + await launchEditor(filePath, frame.lineNumber, frame.column ?? 1) } catch (err) { console.log('Failed to launch editor:', err) - res.statusCode = 500 - res.write('Internal Server Error') - return res.end() + return internalServerError(res) } - res.statusCode = 204 - return res.end() + return noContent(res) } return next() } } -export { getOverlayMiddleware } +function noContent(res: ServerResponse) { + res.statusCode = 204 + res.end('No Content') +} + +function badRequest(res: ServerResponse) { + res.statusCode = 400 + res.end('Bad Request') +} + +function internalServerError(res: ServerResponse) { + res.statusCode = 500 + res.end('Internal Server Error') +} + +function json(res: ServerResponse, data: any) { + res + .setHeader('Content-Type', 'application/json') + .end(Buffer.from(JSON.stringify(data))) +} diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 501e0bc819e9e..5aa12fd530daa 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,3 +1,4 @@ +import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' import type { StackFrame } from 'stacktrace-parser' export type SourcePackage = 'react' | 'next' @@ -17,18 +18,52 @@ const reactVendoredRe = const reactNodeModulesRe = /node_modules[\\/](react|react-dom|scheduler)[\\/]/ const nextRe = - /(node_modules[\\/]next[\\/]|[\\/].next[\\/]static[\\/]chunks[\\/]webpack\.js$)/ + /(node_modules[\\/]next[\\/]|[\\/].next[\\/]static[\\/]chunks[\\/]webpack\.js$|(edge-runtime-webpack|webpack-runtime)\.js$)/ -/** Given a potential file path, it parses which package the file belongs to. */ +const nextMethodRe = /(^__webpack_.*|node_modules[\\/]next[\\/])/ + +/** Given a potential file path or methodName, it parses which package the file/method belongs to. */ export function findSourcePackage( - file: string | null + file: string | null, + methodName: string | null ): SourcePackage | undefined { - if (!file) return + if (file) { + // matching React first since vendored would match under `next` too + if (reactVendoredRe.test(file) || reactNodeModulesRe.test(file)) { + return 'react' + } else if (nextRe.test(file)) { + return 'next' + } + } - // matching React first since vendored would match under `next` too - if (reactVendoredRe.test(file) || reactNodeModulesRe.test(file)) { - return 'react' - } else if (nextRe.test(file)) { - return 'next' + if (methodName) { + if (nextMethodRe.test(methodName)) { + return 'next' + } } } + +export function getOriginalCodeFrame( + frame: { + file: string + lineNumber: number | null + column: number | null + methodName: string + arguments: never[] + }, + source: string | null +): string | null | undefined { + if (!source) return null + return codeFrameColumns( + source, + { + start: { + // 1-based, but -1 means start line without highlighting + line: frame.lineNumber ?? -1, + // 1-based, but 0 means whole line without column highlighting + column: frame.column ?? 0, + }, + }, + { forceColor: true } + ) +} 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 ef6aca7b6d00a..08399b8c2ec6c 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 @@ -925,20 +925,14 @@ async function startWatcher(opts: SetupOpts) { try { originalFrame = await createOriginalStackFrame({ - line: frame.lineNumber, - column: frame.column, source, frame, moduleId, modulePath, rootDirectory: opts.dir, - errorMessage: err.message, - serverCompilation: isEdgeCompiler - ? undefined - : hotReloader.serverStats?.compilation, - edgeCompilation: isEdgeCompiler + compilation: isEdgeCompiler ? hotReloader.edgeServerStats?.compilation - : undefined, + : hotReloader.serverStats?.compilation, }) } catch {} } From d0ff9bf6aeacd39a54ac73c24e563f86416af002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 22 Feb 2024 15:07:54 +0100 Subject: [PATCH 02/39] update snapshots --- .../ReactRefreshLogBox-builtins.test.ts | 4 +++- .../ReactRefreshLogBox-builtins.test.ts | 22 ++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts index e22624e77b709..95e1f060bea75 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts @@ -15,7 +15,6 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { skipStart: true, }) - // Module trace is only available with webpack 5 test('Node.js builtins', async () => { const { session, cleanup } = await sandbox( next, @@ -64,6 +63,9 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { expect(await session.getRedboxSource()).toMatchInlineSnapshot(` "./node_modules/my-package/index.js:1:1 Module not found: Can't resolve 'dns' + > 1 | const dns = require('dns') + | ^ + 2 | module.exports = dns https://nextjs.org/docs/messages/module-not-found diff --git a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts index 65069a556c8cc..9add12fdbb59b 100644 --- a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts @@ -10,7 +10,6 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { skipStart: true, }) - // Module trace is only available with webpack 5 test('Node.js builtins', async () => { const { session, cleanup } = await sandbox( next, @@ -57,15 +56,18 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { https://nextjs.org/docs/messages/module-not-found" ` : ` - "./node_modules/my-package/index.js:1:1 - Module not found: Can't resolve 'dns' - - https://nextjs.org/docs/messages/module-not-found - - Import trace for requested module: - ./index.js - ./pages/index.js" -` + "./node_modules/my-package/index.js:1:1 + Module not found: Can't resolve 'dns' + > 1 | const dns = require('dns') + | ^ + 2 | module.exports = dns + + https://nextjs.org/docs/messages/module-not-found + + Import trace for requested module: + ./index.js + ./pages/index.js" + ` ) await cleanup() From 02d2624ecea291d7b50194fc33157b9b55bca917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 22 Feb 2024 15:28:26 +0100 Subject: [PATCH 03/39] tweak URL parsing --- .../client/components/react-dev-overlay/server/middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3a0a5a2430e1a..0ca436db4d265 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 @@ -214,7 +214,7 @@ export function getOverlayMiddleware(options: { res: ServerResponse, next: Function ) { - const { pathname, searchParams } = new URL(req.url!, 'http://n') + const { pathname, searchParams } = new URL(`http://n${req.url}`) const frame = { file: searchParams.get('file') as string, From 0502163a1f4b372385198b1b126ab49ae479efba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 22 Feb 2024 16:14:28 +0100 Subject: [PATCH 04/39] don't look up code frames for `node_modules` --- .../react-dev-overlay/server/middleware-turbopack.ts | 6 +++--- .../components/react-dev-overlay/server/middleware.ts | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) 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 c03f8f42a6a2f..680b3befe88c6 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 @@ -42,8 +42,8 @@ async function batchedTraceSource( return } - let source - // Don't show code frames for node_modules. These can also often be large bundled files. + let source = null + // Don't look up code frames for node_modules. These can often be large bundled files. if (!sourceFrame.file.includes('node_modules') && !sourceFrame.isInternal) { let sourcePromise = currentSourcesByFile.get(sourceFrame.file) if (!sourcePromise) { @@ -67,7 +67,7 @@ async function batchedTraceSource( methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], }, - source: source ?? null, + source, } } 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 0ca436db4d265..537cb029c12e9 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 @@ -158,7 +158,10 @@ export async function createOriginalStackFrame({ return { originalStackFrame: traced, - originalCodeFrame: getOriginalCodeFrame(traced, sourceContent), + // Don't look up code frames for node_modules. These can often be large bundled files. + originalCodeFrame: frame.file?.includes('node_modules') + ? null + : getOriginalCodeFrame(traced, sourceContent), sourcePackage: findSourcePackage(traced.file, frame.methodName), } } @@ -301,11 +304,7 @@ export function getOverlayMiddleware(options: { }) if (originalStackFrameResponse === null) { - if (sourcePackage) { - res.setHeader('Content-Type', 'application/json') - res.write(Buffer.from(JSON.stringify({ sourcePackage }))) - return res.end() - } + if (sourcePackage) return json(res, { sourcePackage }) return noContent(res) } From 24e60b14d08db73d6c05bed63d3a78bfbfb8de04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 22 Feb 2024 16:21:40 +0100 Subject: [PATCH 05/39] revert snapshots --- .../ReactRefreshLogBox-builtins.test.ts | 4 +--- .../ReactRefreshLogBox-builtins.test.ts | 22 +++++++++---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts index 95e1f060bea75..e22624e77b709 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts @@ -15,6 +15,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { skipStart: true, }) + // Module trace is only available with webpack 5 test('Node.js builtins', async () => { const { session, cleanup } = await sandbox( next, @@ -63,9 +64,6 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { expect(await session.getRedboxSource()).toMatchInlineSnapshot(` "./node_modules/my-package/index.js:1:1 Module not found: Can't resolve 'dns' - > 1 | const dns = require('dns') - | ^ - 2 | module.exports = dns https://nextjs.org/docs/messages/module-not-found diff --git a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts index 9add12fdbb59b..65069a556c8cc 100644 --- a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts @@ -10,6 +10,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { skipStart: true, }) + // Module trace is only available with webpack 5 test('Node.js builtins', async () => { const { session, cleanup } = await sandbox( next, @@ -56,18 +57,15 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { https://nextjs.org/docs/messages/module-not-found" ` : ` - "./node_modules/my-package/index.js:1:1 - Module not found: Can't resolve 'dns' - > 1 | const dns = require('dns') - | ^ - 2 | module.exports = dns - - https://nextjs.org/docs/messages/module-not-found - - Import trace for requested module: - ./index.js - ./pages/index.js" - ` + "./node_modules/my-package/index.js:1:1 + Module not found: Can't resolve 'dns' + + https://nextjs.org/docs/messages/module-not-found + + Import trace for requested module: + ./index.js + ./pages/index.js" +` ) await cleanup() From 82cbbe8557512058441b14f354ae0042528fa4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 14:43:36 +0100 Subject: [PATCH 06/39] increase color contrast for better a11y --- .../internal/container/RuntimeError/index.tsx | 6 +++--- .../components/react-dev-overlay/internal/styles/Base.tsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx index a168c5bc38888..e75e4cb954f1a 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx @@ -138,7 +138,7 @@ export const styles = css` color: #222; } [data-nextjs-call-stack-frame] > h3[data-nextjs-frame-expanded='false'] { - color: #666; + color: var(--color-stack-headline); } [data-nextjs-call-stack-frame] > div, [data-nextjs-component-stack-frame] > div { @@ -146,7 +146,7 @@ export const styles = css` align-items: center; padding-left: calc(var(--size-gap) + var(--size-gap-half)); font-size: var(--size-font-small); - color: #999; + color: var(--color-stack-subline); } [data-nextjs-call-stack-frame] > div > svg, [data-nextjs-component-stack-frame] > [role='link'] > svg { @@ -195,7 +195,7 @@ export const styles = css` } [data-nextjs-collapsed-call-stack-details] h3 { - color: #666; + color: var(--color-stack-headline); } [data-nextjs-collapsed-call-stack-details] [data-nextjs-call-stack-frame] { margin-bottom: var(--size-gap-double); diff --git a/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx b/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx index 9847e352a3020..dd742506eee90 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx @@ -18,12 +18,12 @@ export function Base() { --size-font-bigger: 24px; --color-background: white; - --color-font: #757575; + --color-font: #444; --color-backdrop: rgba(17, 17, 17, 0.2); --color-stack-h6: #222; - --color-stack-headline: #666; - --color-stack-subline: #999; + --color-stack-headline: #555; + --color-stack-subline: #585858; --color-accents-1: #808080; --color-accents-2: #222222; From ad54559663ce5c7babfc4bdd3bd36508426992a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 14:44:20 +0100 Subject: [PATCH 07/39] prefer `display: flex` + `gap` --- .../internal/components/Dialog/styles.ts | 5 ++++- .../internal/container/RuntimeError/index.tsx | 15 +++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts b/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts index ac3544e63c799..9f843bc3cfb1a 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts @@ -77,14 +77,17 @@ const styles = css` height: 100%; display: flex; flex-direction: column; + gap: var(--size-gap-double); } [data-nextjs-dialog-content] > [data-nextjs-dialog-header] { flex-shrink: 0; - margin-bottom: var(--size-gap-double); } [data-nextjs-dialog-content] > [data-nextjs-dialog-body] { position: relative; flex: 1 1 auto; + display: flex; + flex-direction: column; + gap: var(--size-gap-double); } ` diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx index e75e4cb954f1a..a7b6d18b28d4e 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx @@ -118,21 +118,16 @@ export const styles = css` button[data-nextjs-data-runtime-error-collapsed-action] { background: none; border: none; - padding: 0; font-size: var(--size-font-small); line-height: var(--size-font-bigger); color: var(--color-accents-3); - } - - [data-nextjs-call-stack-frame]:not(:last-child), - [data-nextjs-component-stack-frame]:not(:last-child) { - margin-bottom: var(--size-gap-double); + text-decoration: underline dotted; + align-self: start; } [data-nextjs-call-stack-frame] > h3, [data-nextjs-component-stack-frame] > h3 { margin-top: 0; - margin-bottom: var(--size-gap); font-family: var(--font-stack-monospace); font-size: var(--size-font); color: #222; @@ -182,12 +177,16 @@ export const styles = css` } [data-nextjs-collapsed-call-stack-details][open] [data-nextjs-call-stack-chevron-icon] { + transition: transform 0.2s ease; transform: rotate(90deg); } + [data-nextjs-collapsed-call-stack-details] > * { + padding-left: var(--size-gap-double); + } [data-nextjs-collapsed-call-stack-details] summary { display: flex; align-items: center; - margin-bottom: var(--size-gap); + padding-left: 0; list-style: none; } [data-nextjs-collapsed-call-stack-details] summary::-webkit-details-marker { From e7ef6978675b54b17e1bf46b9551c74979033a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 14:47:04 +0100 Subject: [PATCH 08/39] more refactor --- .../internal/container/RuntimeError/index.tsx | 69 ++++++++----------- .../internal/helpers/stack-frame.ts | 44 +++++------- 2 files changed, 47 insertions(+), 66 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx index a7b6d18b28d4e..45cf25e00e714 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx @@ -20,10 +20,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { ) const firstFirstPartyFrameIndex = filteredFrames.findIndex( - (entry) => - entry.expanded && - Boolean(entry.originalCodeFrame) && - Boolean(entry.originalStackFrame) + (e) => e.expanded && e.originalCodeFrame && e.originalStackFrame ) return { @@ -36,7 +33,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { } }, [error.frames]) - const [all, setAll] = React.useState(firstFrame == null) + const [all, setAll] = React.useState(firstFrame === null) const { canShowMore, @@ -51,7 +48,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { return { canShowMore: allCallStackFrames.length !== visibleCallStackFrames.length || - (all && firstFrame != null), + (all && firstFrame !== null), stackFramesGroupedByFramework: groupStackFramesByFramework(allCallStackFrames), @@ -62,10 +59,9 @@ export function RuntimeError({ error }: RuntimeErrorProps) { }, [all, allCallStackFrames, allLeadingFrames, firstFrame]) return ( - + <> {firstFrame ? ( - -

Source

+ <> -
- ) : undefined} - - {error.componentStackFrames ? ( - <> -

Component Stack

- {error.componentStackFrames.map((componentStackFrame, index) => ( - - ))} ) : null} + {error.componentStackFrames?.map((componentStackFrame, index) => ( + + ))} + {stackFramesGroupedByFramework.length ? ( - -

Call Stack

- -
- ) : undefined} + + ) : null} + {canShowMore ? ( - - - - ) : undefined} -
+ + ) : null} + ) } diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts index 3eb007ce0e7d5..bf331c5721bdb 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts @@ -16,29 +16,28 @@ function getOriginalStackFrame( errorMessage: string ): Promise { async function _getOriginalStackFrame(): Promise { - const params = new URLSearchParams() - params.append('isServer', String(type === 'server')) - params.append('isEdgeServer', String(type === 'edge-server')) - params.append('isAppDirectory', String(isAppDir)) - params.append('errorMessage', errorMessage) + const params = new URLSearchParams({ + isServer: String(type === 'server'), + isEdgeServer: String(type === 'edge-server'), + isAppDirectory: String(isAppDir), + errorMessage, + }) + for (const key in source) { params.append(key, ((source as any)[key] ?? '').toString()) } const controller = new AbortController() const tm = setTimeout(() => controller.abort(), 3000) - const res = await self - .fetch( - `${ - process.env.__NEXT_ROUTER_BASEPATH || '' - }/__nextjs_original-stack-frame?${params.toString()}`, - { - signal: controller.signal, - } - ) - .finally(() => { - clearTimeout(tm) - }) + const res = await fetch( + `${ + process.env.__NEXT_ROUTER_BASEPATH ?? '' + }/__nextjs_original-stack-frame?${params}`, + { signal: controller.signal } + ).finally(() => { + clearTimeout(tm) + }) + if (!res.ok || res.status === 204) { return Promise.reject(new Error(await res.text())) } @@ -48,16 +47,11 @@ function getOriginalStackFrame( error: false, reason: null, external: false, - expanded: !Boolean( - /* collapsed */ - (source.file?.includes('node_modules') || - body.originalStackFrame?.file?.includes('node_modules') || - body.originalStackFrame?.file?.startsWith('[turbopack]/')) ?? - true - ), + /** If we ommited originalCodeFrame, it means it was for node_modules or internals, so we can hide by default. */ + expanded: !!body.originalCodeFrame ?? true, sourceStackFrame: source, originalStackFrame: body.originalStackFrame, - originalCodeFrame: body.originalCodeFrame || null, + originalCodeFrame: body.originalCodeFrame, sourcePackage: body.sourcePackage, } } From 8c181c4378502f77e7d52383eb492089888bde45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 14:47:38 +0100 Subject: [PATCH 09/39] don't generate code frames for internals --- .../server/middleware-turbopack.ts | 4 ++-- .../react-dev-overlay/server/middleware.ts | 10 ++++++---- .../react-dev-overlay/server/shared.ts | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) 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 680b3befe88c6..61537f96ee809 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 @@ -43,8 +43,8 @@ async function batchedTraceSource( } let source = null - // Don't look up code frames for node_modules. These can often be large bundled files. - if (!sourceFrame.file.includes('node_modules') && !sourceFrame.isInternal) { + // Don't look up code frames for node_modules or internals. These can often be large bundled files. + if (sourceFrame.file.includes('node_modules') || sourceFrame.isInternal) { let sourcePromise = currentSourcesByFile.get(sourceFrame.file) if (!sourcePromise) { sourcePromise = project.getSourceForAsset(sourceFrame.file) 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 537cb029c12e9..4db0ad54a515d 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 @@ -6,6 +6,7 @@ import { launchEditor } from '../internal/helpers/launchEditor' import { findSourcePackage, getOriginalCodeFrame, + isInternal, type OriginalStackFrameResponse, } from './shared' export { getServerError } from '../internal/helpers/nodeStackFrames' @@ -158,10 +159,11 @@ export async function createOriginalStackFrame({ return { originalStackFrame: traced, - // Don't look up code frames for node_modules. These can often be large bundled files. - originalCodeFrame: frame.file?.includes('node_modules') - ? null - : getOriginalCodeFrame(traced, sourceContent), + // Don't look up code frames for node_modules or internals. These can often be large bundled files. + originalCodeFrame: + frame.file?.includes('node_modules') || !isInternal(traced.file) + ? null + : getOriginalCodeFrame(traced, sourceContent), sourcePackage: findSourcePackage(traced.file, frame.methodName), } } diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 5aa12fd530daa..29dd9c4f4604e 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -3,7 +3,7 @@ import type { StackFrame } from 'stacktrace-parser' export type SourcePackage = 'react' | 'next' -export type OriginalStackFrameResponse = { +export interface OriginalStackFrameResponse { originalStackFrame?: StackFrame | null originalCodeFrame?: string | null /** We use this to group frames in the error overlay */ @@ -17,11 +17,21 @@ const reactVendoredRe = /** React the user installed. Used by Pages Router, or user imports in App Router. */ const reactNodeModulesRe = /node_modules[\\/](react|react-dom|scheduler)[\\/]/ -const nextRe = +const nextInternalsRe = /(node_modules[\\/]next[\\/]|[\\/].next[\\/]static[\\/]chunks[\\/]webpack\.js$|(edge-runtime-webpack|webpack-runtime)\.js$)/ const nextMethodRe = /(^__webpack_.*|node_modules[\\/]next[\\/])/ +export function isInternal(file: string | null) { + if (!file) return false + + return ( + nextInternalsRe.test(file) || + reactVendoredRe.test(file) || + reactNodeModulesRe.test(file) + ) +} + /** Given a potential file path or methodName, it parses which package the file/method belongs to. */ export function findSourcePackage( file: string | null, @@ -31,7 +41,7 @@ export function findSourcePackage( // matching React first since vendored would match under `next` too if (reactVendoredRe.test(file) || reactNodeModulesRe.test(file)) { return 'react' - } else if (nextRe.test(file)) { + } else if (nextInternalsRe.test(file)) { return 'next' } } From e47e034caeadaaa8a4d9c6da6ae968548c848ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 16:15:25 +0100 Subject: [PATCH 10/39] revert client changes --- .../internal/components/Dialog/styles.ts | 5 +- .../internal/container/RuntimeError/index.tsx | 90 +++++++++++-------- .../internal/helpers/stack-frame.ts | 44 +++++---- .../internal/styles/Base.tsx | 6 +- 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts b/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts index 9f843bc3cfb1a..ac3544e63c799 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/Dialog/styles.ts @@ -77,17 +77,14 @@ const styles = css` height: 100%; display: flex; flex-direction: column; - gap: var(--size-gap-double); } [data-nextjs-dialog-content] > [data-nextjs-dialog-header] { flex-shrink: 0; + margin-bottom: var(--size-gap-double); } [data-nextjs-dialog-content] > [data-nextjs-dialog-body] { position: relative; flex: 1 1 auto; - display: flex; - flex-direction: column; - gap: var(--size-gap-double); } ` diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx index 45cf25e00e714..a168c5bc38888 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx @@ -20,7 +20,10 @@ export function RuntimeError({ error }: RuntimeErrorProps) { ) const firstFirstPartyFrameIndex = filteredFrames.findIndex( - (e) => e.expanded && e.originalCodeFrame && e.originalStackFrame + (entry) => + entry.expanded && + Boolean(entry.originalCodeFrame) && + Boolean(entry.originalStackFrame) ) return { @@ -33,7 +36,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { } }, [error.frames]) - const [all, setAll] = React.useState(firstFrame === null) + const [all, setAll] = React.useState(firstFrame == null) const { canShowMore, @@ -48,7 +51,7 @@ export function RuntimeError({ error }: RuntimeErrorProps) { return { canShowMore: allCallStackFrames.length !== visibleCallStackFrames.length || - (all && firstFrame !== null), + (all && firstFrame != null), stackFramesGroupedByFramework: groupStackFramesByFramework(allCallStackFrames), @@ -59,9 +62,10 @@ export function RuntimeError({ error }: RuntimeErrorProps) { }, [all, allCallStackFrames, allLeadingFrames, firstFrame]) return ( - <> + {firstFrame ? ( - <> + +

Source

+
+ ) : undefined} + + {error.componentStackFrames ? ( + <> +

Component Stack

+ {error.componentStackFrames.map((componentStackFrame, index) => ( + + ))} ) : null} - {error.componentStackFrames?.map((componentStackFrame, index) => ( - - ))} - {stackFramesGroupedByFramework.length ? ( - - ) : null} - + +

Call Stack

+ +
+ ) : undefined} {canShowMore ? ( - - ) : null} - + + + + ) : undefined} +
) } @@ -105,22 +118,27 @@ export const styles = css` button[data-nextjs-data-runtime-error-collapsed-action] { background: none; border: none; + padding: 0; font-size: var(--size-font-small); line-height: var(--size-font-bigger); color: var(--color-accents-3); - text-decoration: underline dotted; - align-self: start; + } + + [data-nextjs-call-stack-frame]:not(:last-child), + [data-nextjs-component-stack-frame]:not(:last-child) { + margin-bottom: var(--size-gap-double); } [data-nextjs-call-stack-frame] > h3, [data-nextjs-component-stack-frame] > h3 { margin-top: 0; + margin-bottom: var(--size-gap); font-family: var(--font-stack-monospace); font-size: var(--size-font); color: #222; } [data-nextjs-call-stack-frame] > h3[data-nextjs-frame-expanded='false'] { - color: var(--color-stack-headline); + color: #666; } [data-nextjs-call-stack-frame] > div, [data-nextjs-component-stack-frame] > div { @@ -128,7 +146,7 @@ export const styles = css` align-items: center; padding-left: calc(var(--size-gap) + var(--size-gap-half)); font-size: var(--size-font-small); - color: var(--color-stack-subline); + color: #999; } [data-nextjs-call-stack-frame] > div > svg, [data-nextjs-component-stack-frame] > [role='link'] > svg { @@ -164,16 +182,12 @@ export const styles = css` } [data-nextjs-collapsed-call-stack-details][open] [data-nextjs-call-stack-chevron-icon] { - transition: transform 0.2s ease; transform: rotate(90deg); } - [data-nextjs-collapsed-call-stack-details] > * { - padding-left: var(--size-gap-double); - } [data-nextjs-collapsed-call-stack-details] summary { display: flex; align-items: center; - padding-left: 0; + margin-bottom: var(--size-gap); list-style: none; } [data-nextjs-collapsed-call-stack-details] summary::-webkit-details-marker { @@ -181,7 +195,7 @@ export const styles = css` } [data-nextjs-collapsed-call-stack-details] h3 { - color: var(--color-stack-headline); + color: #666; } [data-nextjs-collapsed-call-stack-details] [data-nextjs-call-stack-frame] { margin-bottom: var(--size-gap-double); diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts index bf331c5721bdb..3eb007ce0e7d5 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts @@ -16,28 +16,29 @@ function getOriginalStackFrame( errorMessage: string ): Promise { async function _getOriginalStackFrame(): Promise { - const params = new URLSearchParams({ - isServer: String(type === 'server'), - isEdgeServer: String(type === 'edge-server'), - isAppDirectory: String(isAppDir), - errorMessage, - }) - + const params = new URLSearchParams() + params.append('isServer', String(type === 'server')) + params.append('isEdgeServer', String(type === 'edge-server')) + params.append('isAppDirectory', String(isAppDir)) + params.append('errorMessage', errorMessage) for (const key in source) { params.append(key, ((source as any)[key] ?? '').toString()) } const controller = new AbortController() const tm = setTimeout(() => controller.abort(), 3000) - const res = await fetch( - `${ - process.env.__NEXT_ROUTER_BASEPATH ?? '' - }/__nextjs_original-stack-frame?${params}`, - { signal: controller.signal } - ).finally(() => { - clearTimeout(tm) - }) - + const res = await self + .fetch( + `${ + process.env.__NEXT_ROUTER_BASEPATH || '' + }/__nextjs_original-stack-frame?${params.toString()}`, + { + signal: controller.signal, + } + ) + .finally(() => { + clearTimeout(tm) + }) if (!res.ok || res.status === 204) { return Promise.reject(new Error(await res.text())) } @@ -47,11 +48,16 @@ function getOriginalStackFrame( error: false, reason: null, external: false, - /** If we ommited originalCodeFrame, it means it was for node_modules or internals, so we can hide by default. */ - expanded: !!body.originalCodeFrame ?? true, + expanded: !Boolean( + /* collapsed */ + (source.file?.includes('node_modules') || + body.originalStackFrame?.file?.includes('node_modules') || + body.originalStackFrame?.file?.startsWith('[turbopack]/')) ?? + true + ), sourceStackFrame: source, originalStackFrame: body.originalStackFrame, - originalCodeFrame: body.originalCodeFrame, + originalCodeFrame: body.originalCodeFrame || null, sourcePackage: body.sourcePackage, } } diff --git a/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx b/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx index dd742506eee90..9847e352a3020 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/styles/Base.tsx @@ -18,12 +18,12 @@ export function Base() { --size-font-bigger: 24px; --color-background: white; - --color-font: #444; + --color-font: #757575; --color-backdrop: rgba(17, 17, 17, 0.2); --color-stack-h6: #222; - --color-stack-headline: #555; - --color-stack-subline: #585858; + --color-stack-headline: #666; + --color-stack-subline: #999; --color-accents-1: #808080; --color-accents-2: #222222; From 5b260262240585dfb6eddf328c6cece1a56fb8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 16:19:55 +0100 Subject: [PATCH 11/39] drop _ --- .../server/middleware-turbopack.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 61537f96ee809..a0fb43a549924 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 @@ -73,20 +73,22 @@ async function batchedTraceSource( export async function createOriginalStackFrame( project: Project, - _frame: TurbopackStackFrame + frame: TurbopackStackFrame ): Promise { - const traced = await batchedTraceSource(project, _frame) + const traced = await batchedTraceSource(project, frame) if (!traced) { - const sourcePackage = findSourcePackage(_frame.file, _frame.methodName) + const sourcePackage = findSourcePackage(frame.file, frame.methodName) if (sourcePackage) return { sourcePackage } return null } - const { frame, source } = traced return { - originalStackFrame: frame, - originalCodeFrame: getOriginalCodeFrame(frame, source), - sourcePackage: findSourcePackage(frame.file, frame.methodName), + originalStackFrame: traced.frame, + originalCodeFrame: getOriginalCodeFrame(traced.frame, traced.source), + sourcePackage: findSourcePackage( + traced.frame.file, + traced.frame.methodName + ), } } From 6fa7fdc9dd385797dddf4f581548b451c4e73fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 16:48:58 +0100 Subject: [PATCH 12/39] unify StackFrame interface --- packages/next/src/build/swc/index.ts | 17 ++----- .../server/middleware-turbopack.ts | 51 ++++++------------- .../react-dev-overlay/server/middleware.ts | 24 +++------ .../react-dev-overlay/server/shared.ts | 47 +++++++++++------ 4 files changed, 56 insertions(+), 83 deletions(-) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 3c8de1af5d98e..657411d56ccd8 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -13,6 +13,7 @@ import { isDeepStrictEqual } from 'util' import { getDefineEnv } from '../webpack/plugins/define-env-plugin' import type { DefineEnvPluginOptions } from '../webpack/plugins/define-env-plugin' import type { PageExtensions } from '../page-extensions-type' +import type { StackFrame } from '../../client/components/react-dev-overlay/server/shared' const nextVersion = process.env.__NEXT_VERSION as string @@ -594,14 +595,6 @@ export interface HmrIdentifiers { identifiers: string[] } -interface TurbopackStackFrame { - column: number | null - file: string - isServer: boolean - line: number - methodName: string | null -} - export type UpdateMessage = | { updateType: 'start' @@ -629,9 +622,7 @@ export interface Project { getSourceForAsset(filePath: string): Promise - traceSource( - stackFrame: TurbopackStackFrame - ): Promise + traceSource(stackFrame: StackFrame): Promise updateInfoSubscribe( aggregationMs: number @@ -1022,9 +1013,7 @@ function bindingToApi(binding: any, _wasm: boolean) { return subscription } - traceSource( - stackFrame: TurbopackStackFrame - ): Promise { + traceSource(stackFrame: StackFrame): Promise { return binding.projectTraceSource(this._nativeProject, stackFrame) } 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 a0fb43a549924..b58b545e31ef4 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 @@ -3,6 +3,7 @@ import { findSourcePackage, getOriginalCodeFrame, type OriginalStackFrameResponse, + type StackFrame, } from './shared' import fs, { constants as FS } from 'fs/promises' @@ -10,37 +11,16 @@ import { launchEditor } from '../internal/helpers/launchEditor' interface Project { getSourceForAsset(filePath: string): Promise - traceSource( - stackFrame: TurbopackStackFrame - ): Promise -} - -interface TurbopackStackFrame { - // 1-based - column: number | null - file: string - isServer: boolean - // 1-based - line: number | null - methodName: string | null - isInternal?: boolean + traceSource(stackFrame: StackFrame): Promise } const currentSourcesByFile: Map> = new Map() -async function batchedTraceSource( - project: Project, - frame: TurbopackStackFrame -) { +async function batchedTraceSource(project: Project, frame: StackFrame) { const file = frame.file ? decodeURIComponent(frame.file) : undefined - if (!file) { - return - } + if (!file) return const sourceFrame = await project.traceSource(frame) - - if (!sourceFrame) { - return - } + if (!sourceFrame) return let source = null // Don't look up code frames for node_modules or internals. These can often be large bundled files. @@ -62,7 +42,8 @@ async function batchedTraceSource( return { frame: { file: sourceFrame.file, - lineNumber: sourceFrame.line, + // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should align. + lineNumber: sourceFrame.lineNumber ?? sourceFrame.line, column: sourceFrame.column, methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], @@ -73,11 +54,11 @@ async function batchedTraceSource( export async function createOriginalStackFrame( project: Project, - frame: TurbopackStackFrame + frame: StackFrame ): Promise { const traced = await batchedTraceSource(project, frame) if (!traced) { - const sourcePackage = findSourcePackage(frame.file, frame.methodName) + const sourcePackage = findSourcePackage(frame) if (sourcePackage) return { sourcePackage } return null } @@ -85,10 +66,7 @@ export async function createOriginalStackFrame( return { originalStackFrame: traced.frame, originalCodeFrame: getOriginalCodeFrame(traced.frame, traced.source), - sourcePackage: findSourcePackage( - traced.frame.file, - traced.frame.methodName - ), + sourcePackage: findSourcePackage(traced.frame), } } @@ -98,11 +76,12 @@ export function getOverlayMiddleware(project: Project) { const frame = { file: searchParams.get('file') as string, - methodName: searchParams.get('methodName'), - line: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, + methodName: searchParams.get('methodName') ?? '', + lineNumber: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, isServer: searchParams.get('isServer') === 'true', - } satisfies TurbopackStackFrame + arguments: searchParams.getAll('arguments'), + } satisfies StackFrame if (pathname === '/__nextjs_original-stack-frame') { let originalStackFrame: OriginalStackFrameResponse | null @@ -147,7 +126,7 @@ export function getOverlayMiddleware(project: Project) { } try { - launchEditor(frame.file, frame.line ?? 1, frame.column ?? 1) + launchEditor(frame.file, frame.lineNumber ?? 1, frame.column ?? 1) } catch (err) { console.log('Failed to launch editor:', err) res.statusCode = 500 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 4db0ad54a515d..e958a2c880968 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 @@ -6,23 +6,15 @@ import { launchEditor } from '../internal/helpers/launchEditor' import { findSourcePackage, getOriginalCodeFrame, - isInternal, type OriginalStackFrameResponse, + type StackFrame, } from './shared' export { getServerError } from '../internal/helpers/nodeStackFrames' export { parseStack } from '../internal/helpers/parseStack' import type { IncomingMessage, ServerResponse } from 'http' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import type webpack from 'webpack' -interface WebpackStackFrame extends StackFrame { - isEdgeServer?: boolean - isServer?: boolean - isAppDirectory?: boolean - errorMessage?: string -} - type Source = { map: () => any } | null function getModuleId(compilation: any, module: any) { @@ -105,7 +97,7 @@ export async function createOriginalStackFrame({ moduleId?: string modulePath?: string rootDirectory: string - frame: WebpackStackFrame + frame: StackFrame compilation?: webpack.Compilation }): Promise { const { lineNumber: line, column, errorMessage } = frame @@ -159,12 +151,8 @@ export async function createOriginalStackFrame({ return { originalStackFrame: traced, - // Don't look up code frames for node_modules or internals. These can often be large bundled files. - originalCodeFrame: - frame.file?.includes('node_modules') || !isInternal(traced.file) - ? null - : getOriginalCodeFrame(traced, sourceContent), - sourcePackage: findSourcePackage(traced.file, frame.methodName), + originalCodeFrame: getOriginalCodeFrame(traced, sourceContent), + sourcePackage: findSourcePackage(traced), } } @@ -231,13 +219,13 @@ export function getOverlayMiddleware(options: { isEdgeServer: searchParams.get('isEdgeServer') === 'true', isAppDirectory: searchParams.get('isAppDirectory') === 'true', errorMessage: searchParams.get('errorMessage') ?? undefined, - } satisfies WebpackStackFrame + } satisfies StackFrame if (pathname === '/__nextjs_original-stack-frame') { const { isAppDirectory, isEdgeServer, isServer } = frame const isClient = !isServer && !isEdgeServer - let sourcePackage = findSourcePackage(frame.file, frame.methodName) + let sourcePackage = findSourcePackage(frame) if ( !( diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 29dd9c4f4604e..c598324e09549 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,8 +1,18 @@ import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' -import type { StackFrame } from 'stacktrace-parser' +import type { StackFrame as ParsedStackFrame } from 'stacktrace-parser' export type SourcePackage = 'react' | 'next' +export interface StackFrame extends ParsedStackFrame { + file: string + isEdgeServer?: boolean + isServer?: boolean + isAppDirectory?: boolean + errorMessage?: string + /** Specific to Turbopack */ + isInternal?: boolean +} + export interface OriginalStackFrameResponse { originalStackFrame?: StackFrame | null originalCodeFrame?: string | null @@ -22,7 +32,7 @@ const nextInternalsRe = const nextMethodRe = /(^__webpack_.*|node_modules[\\/]next[\\/])/ -export function isInternal(file: string | null) { +function isInternal(file: string | null) { if (!file) return false return ( @@ -32,11 +42,13 @@ export function isInternal(file: string | null) { ) } -/** Given a potential file path or methodName, it parses which package the file/method belongs to. */ -export function findSourcePackage( - file: string | null, - methodName: string | null -): SourcePackage | undefined { +/** Given a frame, it parses which package it belongs to. */ +export function findSourcePackage({ + file, + methodName, +}: Partial<{ file: string | null; methodName: string | null }>): + | SourcePackage + | undefined { if (file) { // matching React first since vendored would match under `next` too if (reactVendoredRe.test(file) || reactNodeModulesRe.test(file)) { @@ -53,17 +65,22 @@ export function findSourcePackage( } } +/** + * It looks up the code frame of the traced source. + * @note It ignores node_modules or Next.js/React internals, as these can often be huge budnled files. + */ export function getOriginalCodeFrame( - frame: { - file: string - lineNumber: number | null - column: number | null - methodName: string - arguments: never[] - }, + frame: StackFrame, source: string | null ): string | null | undefined { - if (!source) return null + if ( + !source || + frame.file?.includes('node_modules') || + !isInternal(frame.file) + ) { + return null + } + return codeFrameColumns( source, { From 9e06b8659fd68506293baef768d7bcda9664f9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 23 Feb 2024 16:55:51 +0100 Subject: [PATCH 13/39] fix type errors --- .../react-dev-overlay/server/middleware-turbopack.ts | 7 +++++-- .../client/components/react-dev-overlay/server/shared.ts | 1 - .../next/src/server/lib/router-utils/setup-dev-bundler.ts | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) 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 b58b545e31ef4..242802d484174 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 @@ -24,7 +24,10 @@ async function batchedTraceSource(project: Project, frame: StackFrame) { let source = null // Don't look up code frames for node_modules or internals. These can often be large bundled files. - if (sourceFrame.file.includes('node_modules') || sourceFrame.isInternal) { + if ( + sourceFrame.file && + (sourceFrame.file.includes('node_modules') || sourceFrame.isInternal) + ) { let sourcePromise = currentSourcesByFile.get(sourceFrame.file) if (!sourcePromise) { sourcePromise = project.getSourceForAsset(sourceFrame.file) @@ -32,7 +35,7 @@ async function batchedTraceSource(project: Project, frame: StackFrame) { setTimeout(() => { // Cache file reads for 100ms, as frames will often reference the same // files and can be large. - currentSourcesByFile.delete(sourceFrame.file) + currentSourcesByFile.delete(sourceFrame.file!) }, 100) } diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index c598324e09549..f6698447069db 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -4,7 +4,6 @@ import type { StackFrame as ParsedStackFrame } from 'stacktrace-parser' export type SourcePackage = 'react' | 'next' export interface StackFrame extends ParsedStackFrame { - file: string isEdgeServer?: boolean isServer?: boolean isAppDirectory?: boolean 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 08399b8c2ec6c..ece023e2c47ce 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 @@ -892,9 +892,11 @@ async function startWatcher(opts: SetupOpts) { { file: frameFile, methodName: frame.methodName, + // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should be aligned line: frame.lineNumber ?? 0, column: frame.column, isServer: true, + arguments: frame.arguments, } ) } catch {} From 8af0c1f73889792e435c1517b7cc249250264c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 26 Feb 2024 12:59:53 +0100 Subject: [PATCH 14/39] fix condition --- .../src/client/components/react-dev-overlay/server/shared.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index f6698447069db..11abd85ceb7f8 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -75,7 +75,7 @@ export function getOriginalCodeFrame( if ( !source || frame.file?.includes('node_modules') || - !isInternal(frame.file) + isInternal(frame.file) ) { return null } From 5f9ebae0bdac265cad9904dd60e76da65911073f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 26 Feb 2024 13:55:13 +0100 Subject: [PATCH 15/39] move more to shared --- .../server/middleware-turbopack.ts | 43 ++++++------------- .../react-dev-overlay/server/middleware.ts | 25 ++--------- .../react-dev-overlay/server/shared.ts | 22 ++++++++++ 3 files changed, 38 insertions(+), 52 deletions(-) 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 242802d484174..80c861b35b0f6 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 @@ -1,7 +1,11 @@ import type { IncomingMessage, ServerResponse } from 'http' import { + badRequest, findSourcePackage, getOriginalCodeFrame, + internalServerError, + json, + noContent, type OriginalStackFrameResponse, type StackFrame, } from './shared' @@ -91,55 +95,32 @@ export function getOverlayMiddleware(project: Project) { try { originalStackFrame = await createOriginalStackFrame(project, frame) } catch (e: any) { - res.statusCode = 500 - res.write(e.message) - res.end() - return + return internalServerError(res, e.message) } - if (originalStackFrame === null) { + if (!originalStackFrame) { res.statusCode = 404 - res.write('Unable to resolve sourcemap') - res.end() - return + return res.end('Unable to resolve sourcemap') } - res.statusCode = 200 - res.setHeader('Content-Type', 'application/json') - res.write(Buffer.from(JSON.stringify(originalStackFrame))) - res.end() - return + return json(res, originalStackFrame) } else if (pathname === '/__nextjs_launch-editor') { - if (!frame.file) { - res.statusCode = 400 - res.write('Bad Request') - res.end() - return - } + if (!frame.file) return badRequest(res) const fileExists = await fs.access(frame.file, FS.F_OK).then( () => true, () => false ) - if (!fileExists) { - res.statusCode = 204 - res.write('No Content') - res.end() - return - } + if (!fileExists) return noContent(res) try { launchEditor(frame.file, frame.lineNumber ?? 1, frame.column ?? 1) } catch (err) { console.log('Failed to launch editor:', err) - res.statusCode = 500 - res.write('Internal Server Error') - res.end() - return + return internalServerError(res) } - res.statusCode = 204 - res.end() + noContent(res) } } } 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 e958a2c880968..b88f770f9d89a 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 @@ -4,8 +4,12 @@ import { SourceMapConsumer } from 'next/dist/compiled/source-map08' import { getRawSourceMap } from '../internal/helpers/getRawSourceMap' import { launchEditor } from '../internal/helpers/launchEditor' import { + badRequest, findSourcePackage, getOriginalCodeFrame, + internalServerError, + json, + noContent, type OriginalStackFrameResponse, type StackFrame, } from './shared' @@ -329,24 +333,3 @@ export function getOverlayMiddleware(options: { return next() } } - -function noContent(res: ServerResponse) { - res.statusCode = 204 - res.end('No Content') -} - -function badRequest(res: ServerResponse) { - res.statusCode = 400 - res.end('Bad Request') -} - -function internalServerError(res: ServerResponse) { - res.statusCode = 500 - res.end('Internal Server Error') -} - -function json(res: ServerResponse, data: any) { - res - .setHeader('Content-Type', 'application/json') - .end(Buffer.from(JSON.stringify(data))) -} diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 11abd85ceb7f8..6f6e80415a3e3 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,3 +1,4 @@ +import type { ServerResponse } from 'http' import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' import type { StackFrame as ParsedStackFrame } from 'stacktrace-parser' @@ -93,3 +94,24 @@ export function getOriginalCodeFrame( { forceColor: true } ) } + +export function noContent(res: ServerResponse) { + res.statusCode = 204 + res.end('No Content') +} + +export function badRequest(res: ServerResponse) { + res.statusCode = 400 + res.end('Bad Request') +} + +export function internalServerError(res: ServerResponse, e?: any) { + res.statusCode = 500 + res.end(e ?? 'Internal Server Error') +} + +export function json(res: ServerResponse, data: any) { + res + .setHeader('Content-Type', 'application/json') + .end(Buffer.from(JSON.stringify(data))) +} From a76733b81da868547940935d92f9cd1b58d6d75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 26 Feb 2024 14:38:01 +0100 Subject: [PATCH 16/39] fix condition, lineNumber<>line inconsistency --- .../react-dev-overlay/server/middleware-turbopack.ts | 8 +++++--- .../client/components/react-dev-overlay/server/shared.ts | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) 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 80c861b35b0f6..0f37951a858a0 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 @@ -23,14 +23,16 @@ async function batchedTraceSource(project: Project, frame: StackFrame) { const file = frame.file ? decodeURIComponent(frame.file) : undefined if (!file) return + // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should align. + frame.line ??= frame.lineNumber const sourceFrame = await project.traceSource(frame) if (!sourceFrame) return let source = null - // Don't look up code frames for node_modules or internals. These can often be large bundled files. + // Don't look up source for node_modules or internals. These can often be large bundled files. if ( sourceFrame.file && - (sourceFrame.file.includes('node_modules') || sourceFrame.isInternal) + !(sourceFrame.file.includes('node_modules') || sourceFrame.isInternal) ) { let sourcePromise = currentSourcesByFile.get(sourceFrame.file) if (!sourcePromise) { @@ -87,7 +89,7 @@ export function getOverlayMiddleware(project: Project) { lineNumber: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, isServer: searchParams.get('isServer') === 'true', - arguments: searchParams.getAll('arguments'), + arguments: searchParams.getAll('arguments').filter(Boolean), } satisfies StackFrame if (pathname === '/__nextjs_original-stack-frame') { diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 6f6e80415a3e3..83aa9a39ef21d 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -86,7 +86,8 @@ export function getOriginalCodeFrame( { start: { // 1-based, but -1 means start line without highlighting - line: frame.lineNumber ?? -1, + // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should align. + line: frame.lineNumber ?? frame.line ?? -1, // 1-based, but 0 means whole line without column highlighting column: frame.column ?? 0, }, From ceeb547b12bd7dbc1381db5bba70ef47a259dc97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 26 Feb 2024 15:24:15 +0100 Subject: [PATCH 17/39] filter `''` from arguments --- .../client/components/react-dev-overlay/server/middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b88f770f9d89a..01de617b71fc1 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 @@ -218,7 +218,7 @@ export function getOverlayMiddleware(options: { methodName: searchParams.get('methodName') as string, lineNumber: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, - arguments: searchParams.getAll('arguments'), + arguments: searchParams.getAll('arguments').filter(Boolean), isServer: searchParams.get('isServer') === 'true', isEdgeServer: searchParams.get('isEdgeServer') === 'true', isAppDirectory: searchParams.get('isAppDirectory') === 'true', From eae8cf7b812416bf8ede6f20fa3d9712c48c7071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 26 Feb 2024 16:57:11 +0100 Subject: [PATCH 18/39] line -> lineNumber --- packages/next/src/server/lib/router-utils/setup-dev-bundler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ece023e2c47ce..6dabc3fae5a6f 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 @@ -892,8 +892,7 @@ async function startWatcher(opts: SetupOpts) { { file: frameFile, methodName: frame.methodName, - // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should be aligned - line: frame.lineNumber ?? 0, + lineNumber: frame.lineNumber ?? 0, column: frame.column, isServer: true, arguments: frame.arguments, From be853e2e8eb7ec785810d60035801ad211c13a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 27 Feb 2024 01:10:16 +0100 Subject: [PATCH 19/39] `lineNumber` --- .../client/components/react-dev-overlay/server/middleware.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 01de617b71fc1..cb27a7885fff5 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 @@ -104,7 +104,7 @@ export async function createOriginalStackFrame({ frame: StackFrame compilation?: webpack.Compilation }): Promise { - const { lineNumber: line, column, errorMessage } = frame + const { lineNumber, column, errorMessage } = frame const moduleNotFound = findModuleNotFoundFromError(errorMessage) const result = await (async () => { if (moduleNotFound) { @@ -118,7 +118,7 @@ export async function createOriginalStackFrame({ } // This returns 1-based lines and 0-based columns return await findOriginalSourcePositionAndContent(source, { - line: line ?? 1, + line: lineNumber ?? 1, column, }) })() From 941ae2fc9d9c4a9a3560d0acb8b0bb066cd6afa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 27 Feb 2024 01:34:39 +0100 Subject: [PATCH 20/39] correctly propagate error message from dev bundler --- packages/next/src/server/lib/router-utils/setup-dev-bundler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6dabc3fae5a6f..4e125506e9e01 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 @@ -927,7 +927,7 @@ async function startWatcher(opts: SetupOpts) { try { originalFrame = await createOriginalStackFrame({ source, - frame, + frame: { ...frame, errorMessage: err.message }, moduleId, modulePath, rootDirectory: opts.dir, From c0896f36691371ef69bad1a8fe0c5e3450fd8f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 27 Feb 2024 01:48:58 +0100 Subject: [PATCH 21/39] fix import --- .../src/client/components/react-dev-overlay/server/shared.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 83aa9a39ef21d..11a80dbe5d8df 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,6 +1,6 @@ import type { ServerResponse } from 'http' import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' -import type { StackFrame as ParsedStackFrame } from 'stacktrace-parser' +import type { StackFrame as ParsedStackFrame } from 'next/dist/compiled/stacktrace-parser' export type SourcePackage = 'react' | 'next' From f73a987ddb9efe2b075841c353abb76ea4daf880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 27 Feb 2024 02:10:27 +0100 Subject: [PATCH 22/39] fix type lint error --- .../src/client/components/react-dev-overlay/server/shared.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 11a80dbe5d8df..f124257ca04a1 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -5,10 +5,12 @@ import type { StackFrame as ParsedStackFrame } from 'next/dist/compiled/stacktra export type SourcePackage = 'react' | 'next' export interface StackFrame extends ParsedStackFrame { + file: string | null isEdgeServer?: boolean isServer?: boolean isAppDirectory?: boolean errorMessage?: string + column: number | null /** Specific to Turbopack */ isInternal?: boolean } From 43c1c52e5441e8e41b4272a273c1eb796fceea84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 27 Feb 2024 02:43:58 +0100 Subject: [PATCH 23/39] fix interface --- .../client/components/react-dev-overlay/server/shared.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index f124257ca04a1..666161ac274de 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,16 +1,18 @@ import type { ServerResponse } from 'http' import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' -import type { StackFrame as ParsedStackFrame } from 'next/dist/compiled/stacktrace-parser' export type SourcePackage = 'react' | 'next' -export interface StackFrame extends ParsedStackFrame { +export interface StackFrame { file: string | null + methodName: '' | string + arguments: string[] + lineNumber: number | null + column: number | null isEdgeServer?: boolean isServer?: boolean isAppDirectory?: boolean errorMessage?: string - column: number | null /** Specific to Turbopack */ isInternal?: boolean } From 5f74cf51014244c2f1ce2232f5eb96ef19973362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Mon, 4 Mar 2024 11:51:42 +0100 Subject: [PATCH 24/39] fix type and import --- .../react-dev-overlay/server/middleware-turbopack.ts | 2 +- packages/next/src/server/lib/router-utils/setup-dev-bundler.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 0f37951a858a0..dbb4996205e6a 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 @@ -19,7 +19,7 @@ interface Project { } const currentSourcesByFile: Map> = new Map() -async function batchedTraceSource(project: Project, frame: StackFrame) { +export async function batchedTraceSource(project: Project, frame: StackFrame) { const file = frame.file ? decodeURIComponent(frame.file) : undefined if (!file) return 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 347d4f1ec4cba..d4a95c0de5db3 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 @@ -1064,7 +1064,8 @@ async function traceTurbopackErrorStack( const traced = await batchedTraceSource(project, { file: f.file!, methodName: f.methodName, - line: f.lineNumber ?? 0, + lineNumber: f.lineNumber ?? 0, + arguments: f.arguments, column: f.column, isServer: true, }) From ba63cc17a911f31338dbedad04fbc7eb7b28893f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 5 Mar 2024 13:50:00 +0100 Subject: [PATCH 25/39] separate turbopack stackframe interface --- packages/next/src/build/swc/index.ts | 10 ++++--- .../server/middleware-turbopack.ts | 27 ++++++++++++------- .../react-dev-overlay/server/shared.ts | 8 +++--- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index fbfc934705342..393933e930b49 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -13,7 +13,7 @@ import { isDeepStrictEqual } from 'util' import type { DefineEnvPluginOptions } from '../webpack/plugins/define-env-plugin' import { getDefineEnv } from '../webpack/plugins/define-env-plugin' import type { PageExtensions } from '../page-extensions-type' -import type { StackFrame } from '../../client/components/react-dev-overlay/server/shared' +import type { TurbopackStackFrame } from '../../client/components/react-dev-overlay/server/middleware-turbopack' const nextVersion = process.env.__NEXT_VERSION as string @@ -628,7 +628,9 @@ export interface Project { getSourceForAsset(filePath: string): Promise - traceSource(stackFrame: StackFrame): Promise + traceSource( + stackFrame: TurbopackStackFrame + ): Promise updateInfoSubscribe( aggregationMs: number @@ -1019,7 +1021,9 @@ function bindingToApi(binding: any, _wasm: boolean) { ) } - traceSource(stackFrame: StackFrame): Promise { + traceSource( + stackFrame: TurbopackStackFrame + ): Promise { return binding.projectTraceSource(this._nativeProject, stackFrame) } 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 dbb4996205e6a..4b1720d033412 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 @@ -13,18 +13,26 @@ import { import fs, { constants as FS } from 'fs/promises' import { launchEditor } from '../internal/helpers/launchEditor' +export interface TurbopackStackFrame extends StackFrame { + isInternal?: boolean + line: number | null +} + interface Project { getSourceForAsset(filePath: string): Promise - traceSource(stackFrame: StackFrame): Promise + traceSource( + stackFrame: TurbopackStackFrame + ): Promise } const currentSourcesByFile: Map> = new Map() -export async function batchedTraceSource(project: Project, frame: StackFrame) { +export async function batchedTraceSource( + project: Project, + frame: TurbopackStackFrame +) { const file = frame.file ? decodeURIComponent(frame.file) : undefined if (!file) return - // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should align. - frame.line ??= frame.lineNumber const sourceFrame = await project.traceSource(frame) if (!sourceFrame) return @@ -51,8 +59,7 @@ export async function batchedTraceSource(project: Project, frame: StackFrame) { return { frame: { file: sourceFrame.file, - // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should align. - lineNumber: sourceFrame.lineNumber ?? sourceFrame.line, + line: sourceFrame.line, column: sourceFrame.column, methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], @@ -63,7 +70,7 @@ export async function batchedTraceSource(project: Project, frame: StackFrame) { export async function createOriginalStackFrame( project: Project, - frame: StackFrame + frame: TurbopackStackFrame ): Promise { const traced = await batchedTraceSource(project, frame) if (!traced) { @@ -86,11 +93,11 @@ export function getOverlayMiddleware(project: Project) { const frame = { file: searchParams.get('file') as string, methodName: searchParams.get('methodName') ?? '', - lineNumber: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, + line: parseInt(searchParams.get('line') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, isServer: searchParams.get('isServer') === 'true', arguments: searchParams.getAll('arguments').filter(Boolean), - } satisfies StackFrame + } satisfies TurbopackStackFrame if (pathname === '/__nextjs_original-stack-frame') { let originalStackFrame: OriginalStackFrameResponse | null @@ -116,7 +123,7 @@ export function getOverlayMiddleware(project: Project) { if (!fileExists) return noContent(res) try { - launchEditor(frame.file, frame.lineNumber ?? 1, frame.column ?? 1) + launchEditor(frame.file, frame.line ?? 1, frame.column ?? 1) } catch (err) { console.log('Failed to launch editor:', err) return internalServerError(res) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 666161ac274de..f03df5d886490 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -7,14 +7,16 @@ export interface StackFrame { file: string | null methodName: '' | string arguments: string[] - lineNumber: number | null + /** + * Specific to Webpack. Turbopack uses `line` instead. + * TODO: Try to align these. + */ + lineNumber?: number | null column: number | null isEdgeServer?: boolean isServer?: boolean isAppDirectory?: boolean errorMessage?: string - /** Specific to Turbopack */ - isInternal?: boolean } export interface OriginalStackFrameResponse { From 91857b9575a27c0e86d2ab92851e5f1c15adca81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 5 Mar 2024 14:39:50 +0100 Subject: [PATCH 26/39] fix `line`<>`lineNumber` inconsistencies --- .../react-dev-overlay/server/middleware-turbopack.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 4b1720d033412..34ef6ae967aaf 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 @@ -29,7 +29,7 @@ const currentSourcesByFile: Map> = new Map() export async function batchedTraceSource( project: Project, frame: TurbopackStackFrame -) { +): Promise<{ frame: StackFrame; source: string | null } | undefined> { const file = frame.file ? decodeURIComponent(frame.file) : undefined if (!file) return @@ -59,7 +59,7 @@ export async function batchedTraceSource( return { frame: { file: sourceFrame.file, - line: sourceFrame.line, + lineNumber: sourceFrame.line, column: sourceFrame.column, methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], @@ -93,7 +93,7 @@ export function getOverlayMiddleware(project: Project) { const frame = { file: searchParams.get('file') as string, methodName: searchParams.get('methodName') ?? '', - line: parseInt(searchParams.get('line') ?? '0', 10) || 0, + line: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, isServer: searchParams.get('isServer') === 'true', arguments: searchParams.getAll('arguments').filter(Boolean), From 153de03eeda245c320a660f9d9f13a7baf89c77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 5 Mar 2024 14:47:16 +0100 Subject: [PATCH 27/39] fix build --- .../next/src/server/lib/router-utils/setup-dev-bundler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d4a95c0de5db3..13843f14d79f2 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 @@ -896,7 +896,7 @@ async function startWatcher(opts: SetupOpts) { { file: frameFile, methodName: frame.methodName, - lineNumber: frame.lineNumber ?? 0, + line: frame.lineNumber ?? 0, column: frame.column, isServer: true, arguments: frame.arguments, @@ -1064,7 +1064,7 @@ async function traceTurbopackErrorStack( const traced = await batchedTraceSource(project, { file: f.file!, methodName: f.methodName, - lineNumber: f.lineNumber ?? 0, + line: f.lineNumber ?? 0, arguments: f.arguments, column: f.column, isServer: true, From d14fd7a915d2135c8e257bcc70850ffcb32aafad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 5 Mar 2024 14:53:11 +0100 Subject: [PATCH 28/39] fix types --- .../internal/components/CodeFrame/CodeFrame.tsx | 2 +- .../react-dev-overlay/internal/helpers/stack-frame.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx index ae511a1cb73cd..27a247ddba192 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx @@ -1,6 +1,6 @@ import Anser from 'next/dist/compiled/anser' import * as React from 'react' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' +import type { StackFrame } from '../../../server/shared' import stripAnsi from 'next/dist/compiled/strip-ansi' import { getFrameSource } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts index 3eb007ce0e7d5..6d78e0e751f00 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts @@ -1,5 +1,7 @@ -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' -import type { OriginalStackFrameResponse } from '../../server/shared' +import type { + OriginalStackFrameResponse, + StackFrame, +} from '../../server/shared' export interface OriginalStackFrame extends OriginalStackFrameResponse { error: boolean From d6afaa01fa6fa548e26663b233c3a100f30d5d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Tue, 5 Mar 2024 14:55:15 +0100 Subject: [PATCH 29/39] more type fix --- .../internal/container/RuntimeError/CallStackFrame.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx index 8b5613229f79b..3f4b947957841 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx @@ -1,11 +1,11 @@ import React from 'react' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import { getFrameSource, type OriginalStackFrame, } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' import { HotlinkedText } from '../../components/hot-linked-text' +import type { StackFrame } from '../../../server/shared' export const CallStackFrame: React.FC<{ frame: OriginalStackFrame From 3bd359594ec73833220c2782fd28a0cb54e82730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 11:48:10 +0100 Subject: [PATCH 30/39] address review --- .../src/client/components/react-dev-overlay/server/shared.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index f03df5d886490..459e1e3f31db5 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -92,8 +92,7 @@ export function getOriginalCodeFrame( { start: { // 1-based, but -1 means start line without highlighting - // @ts-expect-error Turbopack uses `line` instead of `lineNumber`, should align. - line: frame.lineNumber ?? frame.line ?? -1, + line: frame.lineNumber ?? -1, // 1-based, but 0 means whole line without column highlighting column: frame.column ?? 0, }, From b50405f9289761a0fe086a864ee09abcbcaba18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 12:14:47 +0100 Subject: [PATCH 31/39] address review --- .../server/middleware-turbopack.ts | 13 +++++++++---- .../react-dev-overlay/server/middleware.ts | 13 +++++++------ .../react-dev-overlay/server/shared.ts | 18 ++---------------- .../lib/router-utils/setup-dev-bundler.ts | 5 ++--- 4 files changed, 20 insertions(+), 29 deletions(-) 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 34ef6ae967aaf..c1a92a7fdbd75 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 @@ -13,9 +13,15 @@ import { import fs, { constants as FS } from 'fs/promises' import { launchEditor } from '../internal/helpers/launchEditor' -export interface TurbopackStackFrame extends StackFrame { +/** @see https://github.com/vercel/next.js/blob/415cd74b9a220b6f50da64da68c13043e9b02995/packages/next-swc/crates/napi/src/next_api/project.rs#L824-L833 */ +export interface TurbopackStackFrame + extends Pick { + isServer: boolean isInternal?: boolean + /** 1-indexed, unlike source map tokens */ line: number | null + /** 1-indexed, unlike source map tokens */ + column: number | null } interface Project { @@ -59,8 +65,8 @@ export async function batchedTraceSource( return { frame: { file: sourceFrame.file, - lineNumber: sourceFrame.line, - column: sourceFrame.column, + lineNumber: sourceFrame.line ?? 0, + column: sourceFrame.column ?? 0, methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], }, @@ -96,7 +102,6 @@ export function getOverlayMiddleware(project: Project) { line: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, isServer: searchParams.get('isServer') === 'true', - arguments: searchParams.getAll('arguments').filter(Boolean), } satisfies TurbopackStackFrame if (pathname === '/__nextjs_original-stack-frame') { 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 cb27a7885fff5..9097eda23263c 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 @@ -95,6 +95,7 @@ export async function createOriginalStackFrame({ modulePath, rootDirectory, frame, + errorMessage, compilation, }: { source: any @@ -102,9 +103,10 @@ export async function createOriginalStackFrame({ modulePath?: string rootDirectory: string frame: StackFrame + errorMessage?: string compilation?: webpack.Compilation }): Promise { - const { lineNumber, column, errorMessage } = frame + const { lineNumber, column } = frame const moduleNotFound = findModuleNotFoundFromError(errorMessage) const result = await (async () => { if (moduleNotFound) { @@ -219,14 +221,13 @@ export function getOverlayMiddleware(options: { lineNumber: parseInt(searchParams.get('lineNumber') ?? '0', 10) || 0, column: parseInt(searchParams.get('column') ?? '0', 10) || 0, arguments: searchParams.getAll('arguments').filter(Boolean), - isServer: searchParams.get('isServer') === 'true', - isEdgeServer: searchParams.get('isEdgeServer') === 'true', - isAppDirectory: searchParams.get('isAppDirectory') === 'true', - errorMessage: searchParams.get('errorMessage') ?? undefined, } satisfies StackFrame + const isServer = searchParams.get('isServer') === 'true' + const isEdgeServer = searchParams.get('isEdgeServer') === 'true' + const isAppDirectory = searchParams.get('isAppDirectory') === 'true' + if (pathname === '/__nextjs_original-stack-frame') { - const { isAppDirectory, isEdgeServer, isServer } = frame const isClient = !isServer && !isEdgeServer let sourcePackage = findSourcePackage(frame) diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 459e1e3f31db5..62bda5dbc4492 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,24 +1,10 @@ import type { ServerResponse } from 'http' import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' +export type { StackFrame } export type SourcePackage = 'react' | 'next' -export interface StackFrame { - file: string | null - methodName: '' | string - arguments: string[] - /** - * Specific to Webpack. Turbopack uses `line` instead. - * TODO: Try to align these. - */ - lineNumber?: number | null - column: number | null - isEdgeServer?: boolean - isServer?: boolean - isAppDirectory?: boolean - errorMessage?: string -} - export interface OriginalStackFrameResponse { originalStackFrame?: StackFrame | null originalCodeFrame?: string | null 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 13843f14d79f2..db55ebf1a3b64 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 @@ -899,7 +899,6 @@ async function startWatcher(opts: SetupOpts) { line: frame.lineNumber ?? 0, column: frame.column, isServer: true, - arguments: frame.arguments, } ) } catch {} @@ -931,7 +930,8 @@ async function startWatcher(opts: SetupOpts) { try { originalFrame = await createOriginalStackFrame({ source, - frame: { ...frame, errorMessage: err.message }, + frame, + errorMessage: err.message, moduleId, modulePath, rootDirectory: opts.dir, @@ -1065,7 +1065,6 @@ async function traceTurbopackErrorStack( file: f.file!, methodName: f.methodName, line: f.lineNumber ?? 0, - arguments: f.arguments, column: f.column, isServer: true, }) From fb1ac00fc6de461502337ab2208e228f264df3d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 12:17:44 +0100 Subject: [PATCH 32/39] revert --- .../react-dev-overlay/server/middleware-turbopack.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c1a92a7fdbd75..02d4c8b0823cb 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 @@ -65,8 +65,8 @@ export async function batchedTraceSource( return { frame: { file: sourceFrame.file, - lineNumber: sourceFrame.line ?? 0, - column: sourceFrame.column ?? 0, + lineNumber: sourceFrame.line, + column: sourceFrame.column, methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], }, From 2d5fc7922a369dd20f356ccd9e212c7f0465b467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 12:18:19 +0100 Subject: [PATCH 33/39] revert --- packages/next/src/server/lib/router-utils/setup-dev-bundler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 db55ebf1a3b64..950cf44284370 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 @@ -931,10 +931,10 @@ async function startWatcher(opts: SetupOpts) { originalFrame = await createOriginalStackFrame({ source, frame, - errorMessage: err.message, moduleId, modulePath, rootDirectory: opts.dir, + errorMessage: err.message, compilation: isEdgeCompiler ? hotReloader.edgeServerStats?.compilation : hotReloader.serverStats?.compilation, From 128a5b343263c51bb3ff18ba691f354d7af6b7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 12:53:52 +0100 Subject: [PATCH 34/39] import `StackFrame` from directly from `next/dist/compiled/stacktrace-parser` --- packages/next/src/build/swc/index.ts | 13 +++++++++++- .../components/CodeFrame/CodeFrame.tsx | 2 +- .../container/RuntimeError/CallStackFrame.tsx | 2 +- .../server/middleware-turbopack.ts | 21 ++----------------- .../react-dev-overlay/server/middleware.ts | 2 +- .../react-dev-overlay/server/shared.ts | 1 - .../lib/router-utils/setup-dev-bundler.ts | 2 +- 7 files changed, 18 insertions(+), 25 deletions(-) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 393933e930b49..262fb012d947d 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -13,7 +13,6 @@ import { isDeepStrictEqual } from 'util' import type { DefineEnvPluginOptions } from '../webpack/plugins/define-env-plugin' import { getDefineEnv } from '../webpack/plugins/define-env-plugin' import type { PageExtensions } from '../page-extensions-type' -import type { TurbopackStackFrame } from '../../client/components/react-dev-overlay/server/middleware-turbopack' const nextVersion = process.env.__NEXT_VERSION as string @@ -615,6 +614,18 @@ export interface UpdateInfo { tasks: number } +/** @see https://github.com/vercel/next.js/blob/415cd74b9a220b6f50da64da68c13043e9b02995/packages/next-swc/crates/napi/src/next_api/project.rs#L824-L833 */ +export interface TurbopackStackFrame { + isServer: boolean + isInternal?: boolean + file: string + /** 1-indexed, unlike source map tokens */ + line: number | null + /** 1-indexed, unlike source map tokens */ + column: number | null + methodName?: string +} + export interface Project { update(options: Partial): Promise diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx index 27a247ddba192..ae511a1cb73cd 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/components/CodeFrame/CodeFrame.tsx @@ -1,6 +1,6 @@ import Anser from 'next/dist/compiled/anser' import * as React from 'react' -import type { StackFrame } from '../../../server/shared' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import stripAnsi from 'next/dist/compiled/strip-ansi' import { getFrameSource } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx index 3f4b947957841..5272a3b47295f 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx @@ -5,7 +5,7 @@ import { } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' import { HotlinkedText } from '../../components/hot-linked-text' -import type { StackFrame } from '../../../server/shared' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' export const CallStackFrame: React.FC<{ frame: OriginalStackFrame 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 02d4c8b0823cb..0d7ef9922d44a 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 @@ -7,29 +7,12 @@ import { json, noContent, type OriginalStackFrameResponse, - type StackFrame, } from './shared' import fs, { constants as FS } from 'fs/promises' import { launchEditor } from '../internal/helpers/launchEditor' - -/** @see https://github.com/vercel/next.js/blob/415cd74b9a220b6f50da64da68c13043e9b02995/packages/next-swc/crates/napi/src/next_api/project.rs#L824-L833 */ -export interface TurbopackStackFrame - extends Pick { - isServer: boolean - isInternal?: boolean - /** 1-indexed, unlike source map tokens */ - line: number | null - /** 1-indexed, unlike source map tokens */ - column: number | null -} - -interface Project { - getSourceForAsset(filePath: string): Promise - traceSource( - stackFrame: TurbopackStackFrame - ): Promise -} +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' +import type { Project, TurbopackStackFrame } from '../../../../build/swc' const currentSourcesByFile: Map> = new Map() export async function batchedTraceSource( 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 9097eda23263c..66ed55511bff9 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 @@ -1,6 +1,7 @@ import { constants as FS, promises as fs } from 'fs' import path from 'path' import { SourceMapConsumer } from 'next/dist/compiled/source-map08' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import { getRawSourceMap } from '../internal/helpers/getRawSourceMap' import { launchEditor } from '../internal/helpers/launchEditor' import { @@ -11,7 +12,6 @@ import { json, noContent, type OriginalStackFrameResponse, - type StackFrame, } from './shared' export { getServerError } from '../internal/helpers/nodeStackFrames' export { parseStack } from '../internal/helpers/parseStack' diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 62bda5dbc4492..143a5a78fddf0 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -2,7 +2,6 @@ import type { ServerResponse } from 'http' import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' -export type { StackFrame } export type SourcePackage = 'react' | 'next' export interface OriginalStackFrameResponse { 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 950cf44284370..9f5ffc4bc0b7f 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 @@ -74,7 +74,7 @@ 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 'stacktrace-parser' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' export type SetupOpts = { renderServer: LazyRenderServerInstance From 292962f2aa01877de3ed4289ebd36a256b8b98db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 12:59:05 +0100 Subject: [PATCH 35/39] move --- packages/next/src/build/swc/index.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 262fb012d947d..97aaf78f5b3e8 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -600,6 +600,18 @@ export interface HmrIdentifiers { identifiers: string[] } +/** @see https://github.com/vercel/next.js/blob/415cd74b9a220b6f50da64da68c13043e9b02995/packages/next-swc/crates/napi/src/next_api/project.rs#L824-L833 */ +export interface TurbopackStackFrame { + isServer: boolean + isInternal?: boolean + file: string + /** 1-indexed, unlike source map tokens */ + line: number | null + /** 1-indexed, unlike source map tokens */ + column: number | null + methodName?: string +} + export type UpdateMessage = | { updateType: 'start' @@ -614,18 +626,6 @@ export interface UpdateInfo { tasks: number } -/** @see https://github.com/vercel/next.js/blob/415cd74b9a220b6f50da64da68c13043e9b02995/packages/next-swc/crates/napi/src/next_api/project.rs#L824-L833 */ -export interface TurbopackStackFrame { - isServer: boolean - isInternal?: boolean - file: string - /** 1-indexed, unlike source map tokens */ - line: number | null - /** 1-indexed, unlike source map tokens */ - column: number | null - methodName?: string -} - export interface Project { update(options: Partial): Promise From 1ead12939e0139bb6417034b2de7a46686b65856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 13:00:38 +0100 Subject: [PATCH 36/39] better match rust struct --- packages/next/src/build/swc/index.ts | 4 ++-- .../react-dev-overlay/server/middleware-turbopack.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 97aaf78f5b3e8..6884b6950b957 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -606,9 +606,9 @@ export interface TurbopackStackFrame { isInternal?: boolean file: string /** 1-indexed, unlike source map tokens */ - line: number | null + line?: number /** 1-indexed, unlike source map tokens */ - column: number | null + column?: number methodName?: string } 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 0d7ef9922d44a..5768b17925cab 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 @@ -48,8 +48,8 @@ export async function batchedTraceSource( return { frame: { file: sourceFrame.file, - lineNumber: sourceFrame.line, - column: sourceFrame.column, + lineNumber: sourceFrame.line ?? 0, + column: sourceFrame.column ?? 0, methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], }, From 7d5c12151a8ed690084a0f19a1af24e16fb146fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 13:01:36 +0100 Subject: [PATCH 37/39] move import --- .../internal/container/RuntimeError/CallStackFrame.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx index 5272a3b47295f..5c796f32abb03 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/CallStackFrame.tsx @@ -1,11 +1,10 @@ -import React from 'react' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' import { getFrameSource, type OriginalStackFrame, } from '../../helpers/stack-frame' import { useOpenInEditor } from '../../helpers/use-open-in-editor' import { HotlinkedText } from '../../components/hot-linked-text' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' export const CallStackFrame: React.FC<{ frame: OriginalStackFrame From ad2fee7b550d99c1254d47961735ce83f2ad0fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 13:02:27 +0100 Subject: [PATCH 38/39] fix import --- .../react-dev-overlay/internal/helpers/stack-frame.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts index 6d78e0e751f00..3eb007ce0e7d5 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts @@ -1,7 +1,5 @@ -import type { - OriginalStackFrameResponse, - StackFrame, -} from '../../server/shared' +import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' +import type { OriginalStackFrameResponse } from '../../server/shared' export interface OriginalStackFrame extends OriginalStackFrameResponse { error: boolean From a9d7101a597634103fe72d6dd888962bf5aa5e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Wed, 6 Mar 2024 13:07:11 +0100 Subject: [PATCH 39/39] fix build --- packages/next/src/build/swc/index.ts | 2 +- .../src/client/components/react-dev-overlay/server/shared.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 6884b6950b957..7c185a7bf756e 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -608,7 +608,7 @@ export interface TurbopackStackFrame { /** 1-indexed, unlike source map tokens */ line?: number /** 1-indexed, unlike source map tokens */ - column?: number + column?: number | null methodName?: string } diff --git a/packages/next/src/client/components/react-dev-overlay/server/shared.ts b/packages/next/src/client/components/react-dev-overlay/server/shared.ts index 143a5a78fddf0..a9f098a5703ad 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/shared.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/shared.ts @@ -1,6 +1,6 @@ +import type { StackFrame } from 'stacktrace-parser' import type { ServerResponse } from 'http' import { codeFrameColumns } from 'next/dist/compiled/babel/code-frame' -import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' export type SourcePackage = 'react' | 'next'