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

Don’t patch Error.prepareStackTrace if --enable-source-maps is used. #5403

Merged
merged 38 commits into from
Apr 19, 2022

Conversation

STRd6
Copy link
Contributor

@STRd6 STRd6 commented Apr 1, 2022

Fixes #5382 Don't monkey patch Error.prepareStackTrace if --enable-source-maps is used.

This ended up being kind of a chonker but I'm pretty happy with the overall structure. Also a little terrified with how much this touches, as well as a little relieved that there are tons of existing tests to catch regressions 😅

In summary:

  • Calling require('coffeescript') no longer patches Error.prepareStackTrace. People must require('coffeescript/register') to opt-in. CoffeeScript.run already does this which will maintain the existing coffee cli experience.
  • Error.prepareStackTrace is not patched if another library has already patched it even when calling require('coffeescript/register').
  • Move Error.prepareStackTrace patching in coffeescript.coffee is now exported rather than applied automatically. register.coffee imports it. The browser docs also call it directly.
  • Moved caching of source maps into sourcemap.coffee (not sure if this is the ideal place).
  • Tracking anonymously compiled modules by unique number rather than the brittle array walking of all files under the same <anonymous> name.
    • This makes getSourceMap and registerCompiled much simpler.
    • Still exporting registerCompiled so other tools can cache source maps and hook into CoffeeScript stack remapping (CoffeeCoverage, etc.)
    • Needed to change helpers.syntaxErrorToString to print [stdin] for <anonymous.* files as well as files with undefined file name to match the existing tests.
  • In index.coffee CoffeeScript.run set inlineMap to be true and set the base option.filename to match the mainModule filename.

TODO

  • CoffeeScript.compile always caches source maps if present. This makes the call to registerCompiled in register.coffee redundant. Need to do some thinking about how to untangle this.
  • Test case for ./bin/coffee --nodejs --enable-source-maps test/importing/error.coffee (Looks like file path is currently incorrect: C:\Users\duder\Documents\GitHub\coffeescript\test\..test\importing\error.coffee:3:9)
  • Browser testing. I believe that modern browsers correctly handle inline source maps but it would be a good idea to have tests for the specific cases and to make sure we're adding them where appropriate. We should always add inline source maps when running / executing scripts and should respect the options given when used as a tool for compiling.

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 1, 2022

It looks like node 16.14.x breaks the dynamic import test which worked in 16.13.x. Not sure what the correct resolution for that is.

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 1, 2022

Tests also pass on node 17.8.0

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 1, 2022

Node 16.14.x tests are fixed and updated here: #5404

@GeoffreyBooth GeoffreyBooth changed the title Fixes #5382 Don’t patch Error.prepareStackTrace if --enable-source-maps is used. Apr 3, 2022
@GeoffreyBooth
Copy link
Collaborator

Node 16.14.x tests are fixed and updated here: #5404

If you don’t mind updating with the latest main, this should be good to land now.

STRd6 added 2 commits April 3, 2022 10:25
- Added check to not patch `Error.prepareStackTrace` if it has already
been patched by another library.
- Always attach inline source maps if `--enable-source-map` is set.
- Added some basic tests for stack trace.
@STRd6 STRd6 force-pushed the 5382 branch 2 times, most recently from 50dfa8e to 85cc164 Compare April 3, 2022 22:10
test/sourcemap.coffee Outdated Show resolved Hide resolved
if process.version.split(".")[0] != "v12"
test "native source maps", ->
new Promise (resolve, reject) ->
proc = spawn "node", [
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason you used fork in the above test but spawn here?

test/sourcemap.coffee Outdated Show resolved Hide resolved
test/sourcemap.coffee Outdated Show resolved Hide resolved
STRd6 and others added 2 commits April 3, 2022 18:41
Co-authored-by: Geoffrey Booth <456802+GeoffreyBooth@users.noreply.github.com>
@STRd6
Copy link
Contributor Author

STRd6 commented Apr 4, 2022

I'm pretty happy with how this is functionally. There's some more clean up and testing that can be followed up with but I'm fairly confident that this will maintain the existing functionality while fixing the two cases where patching Error.prepareStackTrace breaks things. We now will no longer patch if Error.prepareStackTrace has been changed by another tool (such as source-map-support) and we also no longer patch if --enable-source-maps is set since that would apply an additional remapping making the stack trace incorrect there as well. The default behavior is still to patch Error.prepareStackTrace so that the basic 'out of the box' experience for people using CoffeeScript will still be correct stack traces.

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 4, 2022

There's actually one other case that this doesn't address correctly yet: when --enable-source-maps is present in code that calls CoffeeScript.compile (think webpack or a similar bundler) then as it stands right now it will always add an inline map even if that option wasn't specified to the compile function.

The fix is to move the logic into register and pass the correct options to compile based on the environment settings. I'll continue working in that direction. The downside is that it is a larger change overall but it should end up with simpler code that is clearer and more direct which will also clean up some of the historical weirdness around source maps.

@GeoffreyBooth
Copy link
Collaborator

Please let me know when you’re done working on this branch and I’ll review it again. Please don’t forget the style notes (single-quoted strings unless interpolating, avoid single-character variables).

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 8, 2022

I'll get a few more updates in and summarize the changes as well over the next few days.

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 11, 2022

@GeoffreyBooth this is ready for review now. It's a bit larger than I originally anticipated. Please ask any questions you have so I can better document exactly what changed and how things work.

I'm not sure why the CI is flaky on windows but I should look into that before this is merged.

Copy link
Collaborator

@GeoffreyBooth GeoffreyBooth left a comment

Choose a reason for hiding this comment

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

This is very good! A few notes here and there and this should be ready to land.

Does this fix #5216 by chance? From a quick search of open issues, that appears to be the only other open issue related to source maps.

exports.anonymousFileName = do ->
n = 0
->
"<anonymous-#{n++}>"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this use square brackets (like [anonymous-0]) to match [stdin]?

Copy link
Contributor Author

@STRd6 STRd6 Apr 17, 2022

Choose a reason for hiding this comment

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

It was previously <anonymous> which matches the "filename" of Node's eval.

Copy link
Collaborator

Choose a reason for hiding this comment

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

which matches the “filename” of Node’s eval.

That’s fine; do you mind sharing where you see that in Node?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

node
Welcome to Node.js v17.9.0.
Type ".help" for more information.
> eval("throw new Error")
Uncaught Error
    at eval (eval at <anonymous> (REPL1:1:1), <anonymous>:1:7)
>

src/coffeescript.coffee Outdated Show resolved Hide resolved
src/coffeescript.coffee Show resolved Hide resolved
src/coffeescript.coffee Outdated Show resolved Hide resolved
src/coffeescript.coffee Show resolved Hide resolved
test/sourcemap.coffee Outdated Show resolved Hide resolved
test/sourcemap.coffee Outdated Show resolved Hide resolved
if process.version.split(".")[0] != "v12"
test "native source maps", ->
new Promise (resolve, reject) ->
proc = spawn "node", [
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason you used fork in the above test but spawn here?

test/sourcemap.coffee Outdated Show resolved Hide resolved
test/sourcemap.coffee Outdated Show resolved Hide resolved
@STRd6
Copy link
Contributor Author

STRd6 commented Apr 17, 2022

Does this fix #5216 by chance? From a quick search of open issues, that appears to be the only other open issue related to source maps.

It doesn't fix #5216 but I think it lays some groundwork that could lead to a fix.

test/sourcemap.coffee Outdated Show resolved Hide resolved
test/sourcemap.coffee Show resolved Hide resolved
test/sourcemap.coffee Show resolved Hide resolved
@STRd6
Copy link
Contributor Author

STRd6 commented Apr 18, 2022

One thing to note is that though this does fix a bug/unexpected behavior it could be considered a breaking change to how require('coffeescript') works. The good news is that it is very simple to restore the previous behavior if people want by calling patchStackTrace explicitly.

CoffeeScript = require('coffeescript')
CoffeeScript.patchStackTrace()

@GeoffreyBooth
Copy link
Collaborator

The good news is that it is very simple to restore the previous behavior if people want by calling patchStackTrace explicitly.

I thought the intent was to keep the current behavior unless --enable-source-maps was passed (or another plugin already called Error.prepareStackTrace).

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 18, 2022

I think that the correct long term solution is to not do anything drastic to people's environments when require('coffeescript') is called, similarly to how require.extensions patching was moved to /register. Requiring coffeescript/register still patches as before (excepting the cases when --enable-source-maps is provided or Error.prepareStackTrace has already been set) but requiring coffeescript does not as currently implemented in this PR.

If that is too breaking a change to go out soon then I can update the PR to also have coffeescript continue to patch until the next breaking release for compatibility.

@GeoffreyBooth
Copy link
Collaborator

I thought the current behavior was not to patch unless register was called; are we patching when just requireing?

If so, yeah, we should restrict the patching to register (and within register, don’t patch if --enable-source-maps is set).

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 18, 2022

👍

The current behavior in 2.6.1 patches no matter what when required:

Error.prepareStackTrace = (err, stack) ->

This PR restricts the patching to register and doesn't patch in those cases mentioned. People can use the CoffeeScript.patchStackTrace() if they need to for compatibility reasons.

Comment on lines +168 to 169
sourceURL = "//# sourceURL=#{filename}"
js = "#{js}\n#{sourceMapDataURI}\n#{sourceURL}"
Copy link
Collaborator

Choose a reason for hiding this comment

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

FYI, not as part of this PR but related: #5237

@GeoffreyBooth GeoffreyBooth merged commit 8d32c71 into jashkenas:main Apr 19, 2022
@GeoffreyBooth
Copy link
Collaborator

Thanks @STRd6 and congrats! We’re overdue for a release, are there any other PRs you’d like to land soon or should I prepare a release for later this week?

@STRd6
Copy link
Contributor Author

STRd6 commented Apr 19, 2022

@GeoffreyBooth thanks for the review! I don't have anything else ready to land so release when ready 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bug: Requiring "coffeescript" breaks Node.js built-in source map support globally
2 participants