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

Source Map V3 Support in Node.js #40

Closed
bcoe opened this issue Sep 13, 2019 · 9 comments
Closed

Source Map V3 Support in Node.js #40

bcoe opened this issue Sep 13, 2019 · 9 comments

Comments

@bcoe
Copy link

bcoe commented Sep 13, 2019

There's an open PR to start caching source-maps when scripts are loaded in Node.js; It piggybacks off the NODE_V8_COVERAGE implementation, and is an attempt to solve problems presented by tools like ts-node which dynamically insert source-maps at runtime.

I've been scratching my head and trying to think of what a more generic solution looks like for the community...

Is the additional functionality added to NODE_V8_COVERAGE too much of a hack?

This would allow upstream tooling to start working for tools like esm, ts-node, ts-mocha, without any additional work, e.g., hooking into require.extensions, application exit events, but, is it adding something into Node.js we'll regret?

Is it useful for Node.js to maintain and expose a cache of loaded source-maps

An alternative would be the folks override require.extensions and eventually loader-hooks for ESM, and detect source-maps themselves in userland ... this is how things work today.

If we do maintain a cache, what should an API for interacting with it look like?

I was picturing something like this:

const {fetchSourceMap} = require('source_map');
const sm1 = fetchSourceMap(require.resolve('./my-script'));

Should Node.js rewrite stack traces, taking into account source-maps?

Today there's a popular tool node-source-map-support that does this, if Node.js was now maintaining a cache of source-maps, should we make this a first-class-citizen of Node.

Alternatively, we could try to upstream this behavior into V8, but this might be a fairly long-term project.

Should Node.js also provide an API for applying with source-maps?

If Node.js were to apply source-maps to stack-traces, we would already need to ship something like source-map for parsing and applying the maps, should this API be exposed?

What's the MVP functionality that would be useful to folks in the community?

tldr; I've looped in a few folks who work on Source Map adjacent projects, and would love to know people's opinions ... what's an MVP that would make people's lives easier?

CC: @evanw, @LinusU, @cpojer, @SimenB, @joyeecheung, @boneskull, @iansu, @jdalton.

@SimenB
Copy link
Member

SimenB commented Sep 13, 2019

This is super exciting, thank you so much for working on it! 🎉

(For background on my thoughts below, I'm one of the maintainers of Jest)

Is the additional functionality added to NODE_V8_COVERAGE too much of a hack?

I'm not in a position to have an opinion here. Only comment is that in my (WIP) implementation of V8 coverage in Jest, that env variable is not really useful. However, if it's a stepping stone for getting support for source maps natively into Node it seems pretty natural to group it under the thing that prompted its inclusion until source maps potentially are regarded as stable?

Is it useful for Node.js to maintain and expose a cache of loaded source-maps

I haven't really thought it through yet, but the main thing coming to mind is that a single file might be transformed in different ways within the same process (e.g. with and without coverage, or both for es6 and es5), so a cache based on file paths wouldn't necessarily work. If the fetchSourceMap you mention above just takes a unique string though (and not just a file path), that's not an issue. Also, it would need to be accessible to child processes and worker threads, which might pose an issue if the cache is kept in memory?

If we do maintain a cache, what should an API for interacting with it look like?

I touched on it above, but (at least for Jest's case) a file path is not necessarily unique enough, but if the argument is just a string I think that API can work a treat.

Should Node.js rewrite stack traces, taking into account source-maps?

Yes please, that would be amazing! We've had memory issues in Jest due to source maps, and I imagine a solution within node itself could help mitigate this (e.g. by being able to use the wasm implementation of source-map in a synchronous matter via loading it earlier/in native code)

Should Node.js also provide an API for applying with source-maps?

I'm not sure, but doing so would probably greatly simplify the work Istanbul does today, right?

what's an MVP that would make people's lives easier?

(This is again from Jest's perspective)

I think just caching it doesn't really change anything as that's the simple part (or rather, the heavy lifting is already done in a few different modules). Actually applying a source map to some transpiled code is harder, so I think some way of rewriting stack traces (like the linked node-source-map-support does) would be a great start that is relatively small in scope while still needing real source map support.


Like I've said a few times before, thank you so much for driving this. 👏 It's a super exciting development that I think can really make creating great DX easier

@cb1kenobi
Copy link

As I mentioned in nodejs/user-feedback#59 (comment), I'm a huge +1 on this feature!

All of our packages that have transpilation use source-map-support. We inline source maps and we call register from the index.js. It works, but I wish Node had this built-in which would A) allow us to no longer depend on a ~900KB dependency and B) likely give us a tiny performance boost.

I'm not sure why source map support would depend on NODE_V8_COVERAGE. I would prefer Node always detects the existence of a source map and use it in stack traces automatically.

As far as caching is concerned, our source maps are inlined and I presume the caching you're talking about is when the maps are external. We use inlined source maps because there was some issue with external source maps like 4 years ago with I first tinkered with Babel and NYC. I'd be curious what the benefits of an external source map would be. Better performance?

@coreyfarrell
Copy link
Member

I'm not sure why source map support would depend on NODE_V8_COVERAGE. I would prefer Node always detects the existence of a source map and use it in stack traces automatically.

I worry about the memory usage and startup/module load delay for 'always on' source maps. I'm not sure it should ultimately depend on NODE_V8_COVERAGE but I think it should somehow be opt-in.

To be clear I'm 👍 on node.js having the ability to handle source-maps but I would only want it enabled during testing.

@bcoe
Copy link
Author

bcoe commented Sep 14, 2019

It would need to be accessible to child processes and worker threads, which might pose an issue if the cache is kept in memory?

@SimenB the solution with NODE_V8_COVERAGE was to output coverage information to disk (this is similar to what nyc does as well); this puts us in the position to reassemble information from a variety of threads and workers.

I thought your thinking with Jest, was that you would collect coverage information through a long-lived inspector session? wouldn't this be isolated in one Node process where an in memory cache of source-maps could be useful?

Short of a cache on disk, I'm trying to picture what you're thinking? (is there a similar Node.js API that's analogous?).

Yes please, that would be amazing! We've had memory issues in Jest due to source maps, and I imagine a solution within node itself could help mitigate this

@SimenB interesting, would love to know the specific memory issues, sounds like something we should mindful of (if we're taking the naive approach of just shipping source-map).

I was actually concerned about the wasm implementation, because I believe it's an async API, and process.exit needs to happen in one tick of the event loop (we can't really have the source-map API be async). Curious, @loganfsmyth, do you have any thoughts on this topic; If we were going to add source-map support into Node.js, what library should we ship?

I'm not sure why source map support would depend on NODE_V8_COVERAGE. I would prefer Node always detects the existence of a source map and use it in stack traces automatically.

@cb1kenobi, I think we're thinking of two different problems here; I'd like to solve the use-case of upstream tooling (I specifically work on source-maps) wanting to apply source-maps, so that they can display reports that reference the original source -- I'm 100% in agreement that we should do something automatically with stack traces.

@cb1kenobi, you mention in your other thread that source-map-support uses prepareStackTrace; I noticed that @devsnek did some work to move this API into Node.js last year (@devsnek if --collect-source-map was enabled, could we just override prepareStackTrace in Node.js providing better context for transpiled code?).

@bcoe
Copy link
Author

bcoe commented Sep 15, 2019

I did a bit of programming today, based on conversations so far; curious if folks think this is a step in the right direction:

  1. we introduce the flag --experimental-source-maps, which if flipped on starts collecting source-maps in a cache (the cache design can be seen here).
  2. if an error occurs, and --experimental-source-maps is set, stack traces are annotated as follows:
Error: goodbye
    at  /Users/bencoe/oss/node-1/test/fixtures/source-map/inline-throw.min.js:1:43
         -> /Users/bencoe/oss/node-1/test/fixtures/source-map/throw.js:8:2
    at  Immediate.<anonymous> (/Users/bencoe/oss/node-1/test/fixtures/source-map/inline-throw.min.js:1:60)
         -> /Users/bencoe/oss/node-1/test/fixtures/source-map/throw.js:8:2
    at  processImmediate (internal/timers.js:439:21)

I have a working proof of concept of this here.

@cb1kenobi, @SimenB, does this seem like a step in the right direction?

@SimenB in your case it doesn't give you an API for Jest as of yet, but I think perhaps this is a step in the right direction (I'm excited that it ports SourceMap.js from V8, which is a lightweight implementation of the Source Map V3 API, we could expose this eventually).

@cb1kenobi
Copy link

@bcoe, I understand what you're trying to do now.

Yes, prepareStackTrace sounds like a reasonable place to apply source maps to a stack trace.

In regards to your last comment, for what I work on, I don't see the benefit in knowing the line number in the minified source file. I'd rather have a concise stack trace that has the source mapped line numbers, but I'm fine with either way. I appreciate you spearheading this effort!

@ljharb
Copy link
Member

ljharb commented Sep 16, 2019

Note, though, that the stack trace language proposal will eventually require that stack traces be observably “prepared” at error creation time, and no “prepareStackTrace” method can exist for that purpose - it would be ideal for node to avoid attaching to it if possible.

@devsnek
Copy link
Member

devsnek commented Sep 16, 2019

oopsie I missed this ping. node internals use a special c++ api I added to V8, not the "public" prepareStackTrace function (in fact, we have to polyfill prepareStackTrace since the c++ api replaces it).

@bcoe
Copy link
Author

bcoe commented Sep 17, 2019

node internals use a special c++ api I added to V8, not the "public" prepareStackTrace function

@devsnek, so with regards to @ljharb's comments; it sounds like we're relatively future-proof for the time being? I can imagine eventually moving this behavior into V8, when we have a design document, etc., (but I'm guessing it's a ways out before their team has cycles, based on talks with Yang).

In regards to your last comment, for what I work on, I don't see the benefit in knowing the line number in the minified source file. I'd rather have a concise stack trace that has the source mapped line numbers

@cb1kenobi my perspective is you want both to be observable:

  • some folks ship source-maps without the original source-code, wo you could end up with stack-traces in node_modules/ that don't map back to an actual file.
  • there's always a chance that an exception occurs in the transpiled code but not the original.

@bcoe bcoe closed this as completed Oct 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants