Skip to content

Commit

Permalink
Cleanup webpack sourcemap middleware source URL handling
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon committed Feb 12, 2025
1 parent 57c8d04 commit 8e06e42
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async function getSourceFrame(
ignoredSources: getIgnoredSources(sourceMap),
compilation,
moduleId,
modulePath: fileName,
moduleURL: fileName,
},
rootDirectory: compilation.options.context!,
frame: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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:')
)
}

Expand All @@ -57,15 +57,15 @@ type Source =
type: 'file'
sourceMap: RawSourceMap
ignoredSources: IgnoredSources
modulePath: string
moduleURL: string
}
| {
type: 'bundle'
sourceMap: RawSourceMap
ignoredSources: IgnoredSources
compilation: webpack.Compilation
moduleId: string
modulePath: string
moduleURL: string
}

function getModuleById(
Expand All @@ -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\/)/, '')
}

Expand Down Expand Up @@ -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)
}
}
Expand Down Expand Up @@ -186,7 +189,7 @@ export async function createOriginalStackFrame({
}): Promise<OriginalStackFrameResponse | null> {
const { lineNumber, column } = frame
const moduleNotFound = findModuleNotFoundFromError(errorMessage)
const result = await (async () => {
const result = await (() => {
if (moduleNotFound) {
if (source.type === 'file') {
return undefined
Expand All @@ -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,
})
Expand All @@ -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,
Expand Down Expand Up @@ -281,33 +281,33 @@ async function getSourceMapFromCompilation(
}

async function getSource(
filename: string,
sourceURL: string,
options: {
getCompilations: () => webpack.Compilation[]
}
): Promise<Source | undefined> {
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
}

// 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\/)?)/,
''
Expand All @@ -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)
Expand All @@ -339,7 +327,7 @@ async function getSource(
sourceMap,
compilation,
moduleId,
modulePath,
moduleURL: modulePath,
ignoredSources,
}
}
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 8e06e42

Please sign in to comment.