-
Notifications
You must be signed in to change notification settings - Fork 43
Unify exports implementation across CJS and ESM #358
Comments
Step 1: Unify the test suite so that the entire behavior (and the differences) are covered. |
@guybedford @bmeck While working on the test PR (nodejs/node#28831) I ran into the |
It was not intentional on my behalf |
Created a dedicated issue in case somebody else beats me to it: #360 |
Refs: nodejs/modules#358 PR-URL: nodejs#28831 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
Refs: nodejs/modules#358 PR-URL: #28831 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
I'm starting to lean towards closing this issue as wont-fix. I haven't found a good way to do this and as long as the tests are shared, it feels like we can live with two implementations of exports, just like we live with two implementations of resolution in general. |
Wasn’t there an issue for unifying resolution, at least as much as is reasonable? Can unifying exports be part of that? |
Maybe..? Don't recall such an issue right now. But I'm not sure if it would realistically happen before unflagging (read: ever?) since there seem to be more urgent gaps that still need closing. I'm not sure if unifying the resolution code would actually get us enough value to be worth it. |
One interesting argument for merging is that this question of virtualizing the file system keeps coming up for tools like Yarn. Currently they can achieve this by hacking If we were to move the module resolver back into JS in a shared way, we'd have the ability to easily share both the cache and fs virtualization. |
I don't think allowing "hacking the fs" is a good reason to move something. That sounds like opening cans of worms. |
The can of worms is not allowing virtualization, because the result would be resource hooks within the C++ resolver and JS codebase. Resolution is a hot path... the more hooks the slower it gets, the slower Node gets. |
That is, unless there's a clever way to virtualize the lib_uv fs implementation itself. |
@guybedford |
Fwiw, that’s an issue I’ve had with the modules implementation the entire time it existed – it should not be concerned with file system implementation details at all, that’s just a sign of a design that lacks orthogonality. This also affects embedders like Electron, who do support loading modules from files that do not exist on-disk, and would affect us if we start supporting e.g. loading code from tarballs. |
@addaleax some things like |
One way to work around that is to write shared libraries to a temporary path on disk and open that file instead. |
@addaleax yes, this was the design I used back in 2014 for Noda but cleanup is unreliable and a lot of extra copies of libraries were written to disk. |
It is common for a resolver implementation to be based on a virtualized file interface, which would be easier to do in JS. The standard model is to expose a
|
@guybedford
What |
The cache is that we don't share the package.json cache between CJS and ESM currently to avoid crossing boundaries. The fs virtualization is the standard Yarn pnp / Tink model, which Yarn and others will ask for. And with no ESM solution it's not good for us to stifle innovation. Loaders cannot solve virtualization because |
I don't think we should be working out the perfect hooks model for them here, that is their job. The But I'm just noting that a JS resolver unification would avoid these problems, which should not be so easily dismissed. Perfect is the enemy of good and all. |
I don't think this is true? What requires this? I know on the web
I think thats fine, but I don't think this should be hooking |
We cannot guarantee that packages on npm will not use Consider for example a library that loads custom templates from a local folder. It's very common. And users will write code that uses Tink and Yarn pnp have to be backwards compatible, and if they try to use a different scheme, then Schemes might work if they support those new schemes in I am not arguing for anything strong here, just really noting two things:
I hope we can bear the above in mind as these virtualization arguments play out. |
I feel that way about most of the last comment, lots of these problems seem to be scoped to getting the resolver working. If you look from a different angle, things like spawning child processes, dlopen, passing/saving paths between processes, things like using
I agree we need to find a solution to hooking CJS/ESM both. I don't think we have to be as restrictive as we are, but using
I don't see how an alternative would be able to avoid the same kind of interception. How would userland solve performance or reduce complexity for this that the runtime cannot? Hooking the entire |
I agree our role shouldn't be one to solve, and for that reason we should avoid prematurely trying to push APIs and abstractions that solve these problems before we know what the correct solution is. Rather, we should be aware of our role in the muddy problem space. And a unified CJS / ESM resolver makes the mud slightly easier to work with. |
I am unclear what this means, CJS/ESM currently have differing resolution algorithms and potentially timings (CJS enforces sync/blocking, ESM does not). With those continuing to differ I doubt there is actual utility in trying to share the same resolver functions. I think a shared way of handling resources and cache-ing results would be enough. I think porting ESM's resolver to JS would help and is more realistic now than when it was originally wrote thanks to |
I think this is effectively a larger discussion around sharing logic between the two resolvers and goes beyond |
Right now we have two copies of the exports logic. One in CJS and one in ESM. We should try to reuse at least some of the implementation so we can spend less energy trying to sync the two.
The text was updated successfully, but these errors were encountered: