Skip to content

Commit 5c4691e

Browse files
committed
fix(error-logging): rollup errors weren't displaying id and codeframe
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 falls back to just displaying the stack trace with its own message since it varies from the error.message. Some additional normalization was done to the error.frame field if it exists to ensure that consistent padding is used in the error output. This is because the rollup code- frame doesn't have new-lines around it, but the esbuild one does. Fixes #16539
1 parent 6c323d5 commit 5c4691e

1 file changed

Lines changed: 44 additions & 1 deletion

File tree

packages/vite/src/node/build.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,16 +541,59 @@ export async function build(
541541
},
542542
}
543543

544+
/**
545+
* The stack string usually contains a copy of the message at the start of the stack.
546+
* If the stack starts with the message, we remove it and just return the stack trace
547+
* portion. Otherwise the original stack trace is used.
548+
*/
549+
function extractStack(e: RollupError) {
550+
const { stack, name = 'Error', message } = e
551+
552+
// If we don't have a stack, not much we can do.
553+
if (!stack) {
554+
return stack
555+
}
556+
557+
const expectedPrefix = `${name}: ${message}\n`
558+
if (stack.startsWith(expectedPrefix)) {
559+
return stack.slice(expectedPrefix.length)
560+
}
561+
562+
return stack
563+
}
564+
565+
/**
566+
* Esbuild code frames have newlines at the start and end of the frame, rollup doesn't
567+
* This function normalizes the frame to match the esbuild format which has more pleasing padding
568+
*/
569+
const normalizeCodeFrame = (frame: string) => {
570+
const trimmedPadding = frame.replace(/^\n|\n$/g, '')
571+
return `\n${trimmedPadding}\n`
572+
}
573+
544574
const mergeRollupError = (e: RollupError) => {
575+
const stackOnly = extractStack(e)
576+
545577
let msg = colors.red((e.plugin ? `[${e.plugin}] ` : '') + e.message)
546578
if (e.id) {
547579
msg += `\nfile: ${colors.cyan(
548580
e.id + (e.loc ? `:${e.loc.line}:${e.loc.column}` : ''),
549581
)}`
550582
}
551583
if (e.frame) {
552-
msg += `\n` + colors.yellow(e.frame)
584+
msg += `\n` + colors.yellow(normalizeCodeFrame(e.frame))
553585
}
586+
587+
e.message = msg
588+
589+
// We are rebuilding the stack trace to include the more detailed message at the top.
590+
// Previously this code was relying on mutating e.message changing the generated stack
591+
// when it was accessed, but we don't have any guarantees that the error we are working
592+
// with hasn't already had its stack accessed before we get here.
593+
if (stackOnly !== undefined) {
594+
e.stack = `${e.message}\n${stackOnly}`
595+
}
596+
554597
return msg
555598
}
556599

0 commit comments

Comments
 (0)