Skip to content

Commit

Permalink
fix(error-logging): rollup errors weren't displaying id and codeframe
Browse files Browse the repository at this point in the history
The code which modified the error message to have additional context about the module id
and code frame where the error originated was relying on the error's stack having never
been accessed prior to modifying the error's message. At some point, this no longer was
true for Rollup errors, and no context information was being displayed as a result other
than the stack trace within the rollup library that doesn't make it clear what or where
the problem is.

This PR changes the code that add's the id and code-frame context to the error message
to also modify the error's stack. To do so, it attempts to extract the original stack
trace by removing the existing error message from error.stack if it matches, and
otherwise falling back to looking for the stack trace portion by searching for indented
lines that look something like:
    at (file://path/to/file.js
or
    at <annonymout>

Some additional normalization was done to the error.frame field if it exists to ensure
that consistent padding is used in the error output.

Fixes vitejs#16539
  • Loading branch information
thebanjomatic committed Apr 26, 2024
1 parent 6c323d5 commit b20d142
Showing 1 changed file with 45 additions and 1 deletion.
46 changes: 45 additions & 1 deletion packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,16 +541,60 @@ export async function build(
},
}

/**
* The stack string usually contains a copy of the message at the start of the stack.
* This function tries to extract the stack trace portion and returns it so that we
* can avoid the same message being logged twice. If we fail to find the stack portion,
* then the full error.stack is returned instead.
*/
function extractStack(e: RollupError) {
const originalStack = e.stack ?? ''
if (originalStack.startsWith(e.message)) {
return originalStack.slice(e.message.length)
}

// If the stack doesn't start with the error message, it's likely one or the other was modified.
// In this case, we'll make a best guess by trimming until the first line that looks like:
// " at someFunction (file:///path/to/file.js) or " at <anonymous>"
const match = /\n \s+at \S+ [(<] /.exec(originalStack)
if (!match) {
return originalStack
}

return originalStack.slice(0, match.index)
}

/**
* Esbuild code frames have newlines at the start and end of the frame, rollup doesn't
* This function normalizes the frame to match the esbuild format which has more pleasing padding
*/
const normalizeCodeFrame = (frame: string) => {
const match = /^\r?\n?(.*)\r?\n?$/.exec(frame)
if (!match) return frame
return `\n${match[1]}\n`
}

const mergeRollupError = (e: RollupError) => {
const stackOnly = extractStack(e)

let msg = colors.red((e.plugin ? `[${e.plugin}] ` : '') + e.message)
if (e.id) {
msg += `\nfile: ${colors.cyan(
e.id + (e.loc ? `:${e.loc.line}:${e.loc.column}` : ''),
)}`
}
if (e.frame) {
msg += `\n` + colors.yellow(e.frame)
msg += `\n` + colors.yellow(normalizeCodeFrame(e.frame))
}

e.message = msg

// We are rebuilding the stack trace to include the more detailed message at the top.
// Previously this code was relying on mutating e.message changing the generated stack
// when it was accessed, but we don't have any guarantees that the error we are working
// with hasn't already had its stack accessed before we get here.
e.stack = `${e.message}${stackOnly}`

return msg
}

Expand Down

0 comments on commit b20d142

Please sign in to comment.