Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error handling improvements to esbuild plugin #2595

Merged
merged 11 commits into from
Mar 7, 2025
Merged

Conversation

egnor
Copy link
Contributor

@egnor egnor commented Mar 3, 2025

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and discussions and couldn’t find anything or linked relevant results below
  • I made sure the docs are up to date
  • I included tests (or that’s not needed)

Description of changes

This change improves(?) error conversion in the esbuild plugin, with no change to non-error function.

Previously, if a non-VFileMessage error was thrown by the MDX conversion process (eg. a simple nodejs runtime Error thrown by a plugin or the core), it would be wrapped as the cause in a VFileMessage with generic "Cannot process MDX file..." text, and re-thrown by the onload handler. This makes sense! However, when that message is converted in vfileMessageToEsbuild, the cause was completely ignored, so all information about that underlying error was lost, which makes debugging harder (especially debugging plugins).

Also previously, even when the original error thrown was a VFileMessage, if the optional place.start.offset (or place.offset) isn't defined, all place information was lost. This .offset field is optional (though set in most common paths).

This change revises things:

  • In the onload handler, always throw a new VFileMessage with the original exception attached. Hoist location information to the outer exception, if the inner exception appears to be a VFileMessage itself. Include the inner exception's text rendering in the outer's text (after a colon).

  • In vfileMessageToEsbuild, handle the case where offset information isn't present, working with whatever information is present.

The changes to the unit test are a decent way to see the effect of all this.

Copy link

vercel bot commented Mar 3, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mdx ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 5, 2025 2:07pm

@github-actions github-actions bot added 👋 phase/new Post is being triaged automatically 🤞 phase/open Post is being triaged manually and removed 👋 phase/new Post is being triaged automatically labels Mar 3, 2025
Copy link

codecov bot commented Mar 3, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (908ff45) to head (d3c901f).
Report is 86 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #2595   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           23        21    -2     
  Lines         2693      2647   -46     
  Branches         2         2           
=========================================
- Hits          2693      2647   -46     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

source: '@mdx-js/esbuild'
})
new VFileMessage(
`Cannot process MDX file with esbuild:\n ${error_}`, {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`Cannot process MDX file with esbuild:\n ${error_}`, {
'Cannot process MDX file with esbuild', {

The point of cause, I believe, is to be an alternative to copying it into the reason.
I’m fine with always making a new error, a vfile message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I changed it to not concatenate the message at this point, and instead walk the cause chain and concatenate the text when creating the esbuild message.

In theory the cause chain is available in the esbuild message via the detail field, but, that's a very generic field and basically nothing seems to look at it-- certainly esbuild itself doesn't print it out when reporting errors.

egnor and others added 3 commits March 4, 2025 12:50
as suggested

Co-authored-by: Titus <[email protected]>
Signed-off-by: Daniel Egnor <[email protected]>
As suggested

Co-authored-by: Titus <[email protected]>
Signed-off-by: Daniel Egnor <[email protected]>
As suggested

Co-authored-by: Titus <[email protected]>
Signed-off-by: Daniel Egnor <[email protected]>
@egnor
Copy link
Contributor Author

egnor commented Mar 4, 2025

Ready for another look!

while (exc.cause instanceof Error) {
exc = exc.cause
text = `${text}:\n ${exc}`
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these lines because otherwise the “cause” is lost? That ESBuild doesn’t print them?

Is that something that ESbuild should fix? Upstream?

Or, if indeed needed here, perhaps you could use vfile-reporter I linked above, or, perhaps you could use inspect from util: https://nodejs.org/api/util.html#utilinspectobject-options. I believe that it serializes recursive causes

Copy link
Contributor Author

@egnor egnor Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay yes this is the tricky bit.

In esbuild you can report errors by just throwing an exception, or by adding to a list of messages using their own Message class (which is what we're doing). That class includes a detail field which can be an exception but could also be anything else, and in any case doesn't get printed in esbuild's output, it seems to be for custom API use; it's not really comparable to cause.

Alternatives

  • use vfile-reporter -- this does a fine job and does chase cause chains, unfortunately this wants an entire VFile, I don't see a way to format a single VFileMessage (though you could imagine exposing that)?
  • use util.inspect -- this has a lovely formatter for Error objects, but even for subclasses like VFileMessage reverts to its default printer which just dumps the fields and does not print error messages or chase cause chains?
  • just let the error get thrown rather than catching it and adding it to messages -- this does get handled by esbuild gracefully (including plugin attribution), but the extra location information in VFileMessage is lost
  • add VFileMessage-shaped exceptions directly to messages (no wrapping), re-throw everything else, remove all cause-chain-processing -- this could work??

For now I simplified the cause processing to not try to walk the whole cause chain but just include the string representation of the cause if one exists.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points, I appreciate that you came up with alternatives & discussed trade offs.

I could see an reportError in vfile-reporter, which can then be used here.

The no-wrapping idea could also perhaps work.

But this is probably fine to start with!

@wooorm wooorm merged commit 84ec66e into mdx-js:main Mar 7, 2025
7 checks passed
@wooorm wooorm added the 💪 phase/solved Post is done label Mar 7, 2025

This comment has been minimized.

@github-actions github-actions bot removed the 🤞 phase/open Post is being triaged manually label Mar 7, 2025
@wooorm
Copy link
Member

wooorm commented Mar 7, 2025

Thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💪 phase/solved Post is done
Development

Successfully merging this pull request may close these issues.

2 participants