From 8e06e42ae784d548ec61b7d2cfdfe39fc9c8abfe Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Mon, 10 Feb 2025 17:41:24 +0100 Subject: [PATCH] Cleanup webpack sourcemap middleware source URL handling --- .../parseNotFoundError.ts | 2 +- .../internal/helpers/webpack-module-path.ts | 6 +- .../server/middleware-webpack.ts | 68 ++++++++----------- 3 files changed, 32 insertions(+), 44 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 6addcca0768127..508b370673b57a 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 @@ -68,7 +68,7 @@ async function getSourceFrame( ignoredSources: getIgnoredSources(sourceMap), compilation, moduleId, - modulePath: fileName, + moduleURL: fileName, }, rootDirectory: compilation.options.context!, frame: { diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/webpack-module-path.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/webpack-module-path.ts index 683a2fbc13e6fc..c42c4196646dac 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/webpack-module-path.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/webpack-module-path.ts @@ -26,10 +26,10 @@ export function isWebpackInternalResource(file: string) { * webpack://./src/hello.tsx => ./src/hello.tsx * webpack:///./src/hello.tsx => ./src/hello.tsx */ -export function formatFrameSourceFile(file: string) { +export function formatFrameSourceFile(sourceURL: string) { for (const regex of replacementRegExes) { - file = file.replace(regex, '') + sourceURL = sourceURL.replace(regex, '') } - return file + return sourceURL } diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-webpack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-webpack.ts index 0babc74125f4ed..6420ef3c3fe92d 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-webpack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-webpack.ts @@ -1,6 +1,6 @@ import { constants as FS, promises as fs } from 'fs' import path from 'path' -import url from 'url' +import { fileURLToPath, pathToFileURL } from 'url' import { SourceMapConsumer, type BasicSourceMapConsumer, @@ -32,12 +32,12 @@ import { formatFrameSourceFile } from '../internal/helpers/webpack-module-path' import type { MappedPosition } from 'source-map' import { inspect } from 'util' -function shouldIgnorePath(modulePath: string): boolean { +function shouldIgnoreSource(sourceURL: string): boolean { return ( - modulePath.includes('node_modules') || + sourceURL.includes('node_modules') || // Only relevant for when Next.js is symlinked e.g. in the Next.js monorepo - modulePath.includes('next/dist') || - modulePath.startsWith('node:') + sourceURL.includes('next/dist') || + sourceURL.startsWith('node:') ) } @@ -57,7 +57,7 @@ type Source = type: 'file' sourceMap: RawSourceMap ignoredSources: IgnoredSources - modulePath: string + moduleURL: string } | { type: 'bundle' @@ -65,7 +65,7 @@ type Source = ignoredSources: IgnoredSources compilation: webpack.Compilation moduleId: string - modulePath: string + moduleURL: string } function getModuleById( @@ -82,6 +82,9 @@ function findModuleNotFoundFromError(errorMessage: string | undefined) { } function getSourcePath(source: string) { + if (source.startsWith('file://')) { + return fileURLToPath(source) + } return source.replace(/^(webpack:\/\/\/|webpack:\/\/|webpack:\/\/_N_E\/)/, '') } @@ -130,10 +133,10 @@ export function getIgnoredSources(sourceMap: RawSourceMap): IgnoredSources { for (let index = 0; index < moduleFilenames.length; index++) { // bundlerFilePath case: webpack://./app/page.tsx - const bundlerFilePath = moduleFilenames[index] + const webpackSourceURL = moduleFilenames[index] // Format the path to the normal file path - const formattedFilePath = formatFrameSourceFile(bundlerFilePath) - if (shouldIgnorePath(formattedFilePath)) { + const formattedFilePath = formatFrameSourceFile(webpackSourceURL) + if (shouldIgnoreSource(formattedFilePath)) { ignoreList.add(index) } } @@ -186,7 +189,7 @@ export async function createOriginalStackFrame({ }): Promise { const { lineNumber, column } = frame const moduleNotFound = findModuleNotFoundFromError(errorMessage) - const result = await (async () => { + const result = await (() => { if (moduleNotFound) { if (source.type === 'file') { return undefined @@ -199,7 +202,7 @@ export async function createOriginalStackFrame({ ) } // This returns 1-based lines and 0-based columns - return await findOriginalSourcePositionAndContent(source.sourceMap, { + return findOriginalSourcePositionAndContent(source.sourceMap, { line: lineNumber ?? 1, column, }) @@ -218,19 +221,16 @@ export async function createOriginalStackFrame({ isIgnoredSource(source, sourcePosition) || // If the source file is externals, should be excluded even it's not ignored source. // e.g. webpack://next/dist/.. needs to be ignored - shouldIgnorePath(source.modulePath) + shouldIgnoreSource(source.moduleURL) const sourcePath = getSourcePath( // When sourcePosition.source is the loader path the modulePath is generally better. (sourcePosition.source!.includes('|') - ? source.modulePath - : sourcePosition.source) || source.modulePath + ? source.moduleURL + : sourcePosition.source) || source.moduleURL ) const filePath = path.resolve(rootDirectory, sourcePath) - - const resolvedFilePath = sourceContent - ? path.relative(rootDirectory, filePath) - : sourcePosition.source + const resolvedFilePath = path.relative(rootDirectory, filePath) const traced: IgnorableStackFrame = { file: resolvedFilePath, @@ -281,25 +281,25 @@ async function getSourceMapFromCompilation( } async function getSource( - filename: string, + sourceURL: string, options: { getCompilations: () => webpack.Compilation[] } ): Promise { const { getCompilations } = options - if (path.isAbsolute(filename)) { - filename = url.pathToFileURL(filename).href + if (path.isAbsolute(sourceURL)) { + sourceURL = pathToFileURL(sourceURL).href } - if (filename.startsWith('file:')) { - const sourceMap = await getSourceMapFromFile(filename) + if (sourceURL.startsWith('file:')) { + const sourceMap = await getSourceMapFromFile(sourceURL) return sourceMap ? { type: 'file', sourceMap, ignoredSources: getIgnoredSources(sourceMap), - modulePath: filename.replace(/^file:\/\//, ''), + moduleURL: sourceURL, } : undefined } @@ -307,7 +307,7 @@ async function getSource( // webpack-internal:///./src/hello.tsx => ./src/hello.tsx // rsc://React/Server/webpack-internal:///(rsc)/./src/hello.tsx?42 => (rsc)/./src/hello.tsx // webpack://_N_E/./src/hello.tsx => ./src/hello.tsx - const moduleId = filename + const moduleId = sourceURL .replace( /^(rsc:\/\/React\/[^/]+\/)?(webpack-internal:\/\/\/|webpack:\/\/(_N_E\/)?)/, '' @@ -319,18 +319,6 @@ async function getSource( for (const compilation of getCompilations()) { const sourceMap = await getSourceMapFromCompilation(moduleId, compilation) - const ignoreList = [] - const moduleFilenames = sourceMap?.sources ?? [] - - for (let index = 0; index < moduleFilenames.length; index++) { - // bundlerFilePath case: webpack://./app/page.tsx - const bundlerFilePath = moduleFilenames[index] - // Format the path to the normal file path - const formattedFilePath = formatFrameSourceFile(bundlerFilePath) - if (shouldIgnorePath(formattedFilePath)) { - ignoreList.push(index) - } - } if (sourceMap) { const ignoredSources = getIgnoredSources(sourceMap) @@ -339,7 +327,7 @@ async function getSource( sourceMap, compilation, moduleId, - modulePath, + moduleURL: modulePath, ignoredSources, } } @@ -467,7 +455,7 @@ async function getOriginalStackFrame({ lineNumber: frame.lineNumber, column: frame.column ?? 1, methodName: frame.methodName, - ignored: shouldIgnorePath(filename), + ignored: shouldIgnoreSource(filename), arguments: [], } if (!source) {