-
Notifications
You must be signed in to change notification settings - Fork 43
Transition Path Problems For Tooling #388
Comments
.js-based script config files is a really important class of bug - it could be worth having a dedicated issue for this one. |
(this will affect most tools!) |
we could also just get rid of |
BTW, this works flawlessly in 12.0. The newer (ie better) package boundary detection is throwing the error. Semantics feel off. The consumer (ie CJS) is consuming a module (ie ESM) that exists outside of it's package boundary. Shouldn't the package type constraint be based on the consumer not the consumed? Anyway, w/ a 1-line patch to
This breaks browser interop for pure ESM packages. Using |
That’s not entirely accurate; for example, in the babel ecosystem, everything is either a script or a module based on config, not file extension. Since nothing but node yet parses JS based on file extension, what .js means everywhere else is “i have no idea which parse goal to use yet”. |
I definitely didn't intend to start any arguments about extensions, I was just hoping to get in some ideas about various directions to go with ecosystem feedback. best to think of breaking changes now before we unflag. |
This isn’t really a breaking change for the tool, because the user has opted into the new mode by setting And while the user waits for the tool to be updated, they can keep using CommonJS or ESM as they do now, via There are going to be lots of tools that won’t fully support ESM mode in Node for quite awhile, beyond just |
I agree - the resolver under a |
Is nodejs/node#29732 getting fast tracked because of a resolution at the meeting that I missed? Or do some of the core devs just feel that strongly about it? |
@weswigham all discussions and decisions are as made publicly there, there have been no side agreements. If you disagree with fast tracking or merging it behind the flag we can certainly reconsider, my primary goal was just to avoid shipping frictions to the ecosystem before we are 100% certain about it, to avoid unnecessary concern / issues for users. I see it as more of a courtesy than anything else. |
(this issue was not discussed at the last meeting, because we were not yet aware of the compatibility case) |
I just wanted to know if it'd been discussed in the WG at all (and wasn't sure since I'd missed Wednesday's meeting), since it's a slight walk-back of what'd already been merged (and i thought part of the reason for merging it was to see what kinds of issues cropped up). I definitely asked if it should have been behind a flag when it was first added, and I still do agree enough with it having been unflagged, provided one believes that using |
@weswigham as I say it is as a courtesy - we want to encourage experimentation of type module, and we don't want to cause unnecessary frictions in doing that, unless we are 100% it is those frictions we want to ship. So it's not so much about whether it is breaking as whether this is the story we want users to be dealing with. Personally I think it is still debatable whether this should be an error or a warning or similar. |
I was under the impression that this was expected - when you have |
The major constraint for tools is that they often have synchronous config loading internals (I know at least Babel does /cc @loganfsmyth), so that using But yes if we can accept these breaks for tools and we are all 100% sure this is what we want to dictate that tools should have to deal with then that is fine too. As I say though I don't see why we couldn't still support these cases under a deprecation path as the major part of the hazard that concerned me previously was that the CJS loader was injecting these into the ESM loader such that depending on if you loaded CJS or ESM first you would get CJS or ESM interpretations respectively through the ESM loader itself. If we can at least avoid that hazard, what hazard is left? |
The hazard is that |
The usability issue is the lack of an API (eg, I really hope I can hammer that home to enough people that that's, like, one of the biggest non-bikeshed concerns of node's (current) |
what I'm asking is where is the actual hazard in this case? How does it bite? |
Literally, this - the same file could be interpreted twice, in two different ways (one of which may or may not error due to syntax), which is exactly what we're trying to avoid, as if the file does anything stateful (eg, set a global, muck with |
So to try flesh out the example - say you have a "type": "module" package on npm (P) and install it such that it has two dependents - one CommonJS and one ES module package that both import from it. The CommonJS package loads a file with no module syntax from P that does a global mutation, and the ES module package loads that same file from P resulting in the global mutation happening twice? Note this "hazard" is naturally guarded by the following:
|
we shouldn't ever have a case where a file will run more than once. |
My feeling is the best solution is still just to allow synchronous This means it would be possible to have something like |
I would not like to bifurcate TLA like that, if we support it we support it. |
The web is already intending to reject TLA in service workers, given that |
I disagree with the web disabling TLA in service workers. if TLA randomly doesn't work in certain contexts why would anyone feel motivated to write code with TLA in it. |
This is super snarky, but bear with me:
Because the convenience of use outweighs the inconvenience of incompatibility, ofc. That's the hope, at least. We've been making similar choices the whole time. I'm not saying I wouldn't prefer uniform TLA support (and Jordan's right, having both TLA and non-TLA modules is pretty much a tooling nightmare - different supported syntax subsets in the same runtime is not going to be fun for explaining to people in an error), but I do get the trade-off. Sort-of. It sounds like the argument is just "TLA in service workers is a footgun since they're often performance sensitive and it's easy to deadlock or wait on unneeded async resources using TLA" - which is all true in other contexts, too. It's again just a matter of weighing that possibly against the convenience of its use, and deciding differently in the SW context. If anything, that w3c feels free to make ecosystem bifrucating choices like this, should be freeing to us, as it certainly means runtimes should be considered as free to determine their own destiny when it comes to module support and use, since there are even compat concerns within the browser. |
require could never load esm. nothing was disabled. nothing was lost. esm is still working. I think i've said a lot in the past i am a fan of require(esm), and i still am, but randomly disabling language features is in the "unacceptable" category for me. Why not just disable the instantiation checks that verify that imports are wired correctly and map cjs modules at runtime? Why not just make our own module system that mirrors babel esm instead of native esm? If we randomly change the language for our own purposes we end up bifurcating the ecosystem. I am hopeful TC39 will choose to disallow this when its brought up in a few days. |
And service workers have never been able to be modules yet, so nothing was lost with no TLA in them.
I think the line between a language feature and a runtime feature is slim - especially for a module system feature like TLA. In any case, the distinction is lost on most less in-the-weeds users and I do think you're right; inconsistent is inconsistent, but I do think I can also call a spade a spade and say we've made similar choices thus far, to weight technical concerns and imagined future problems over real ecosystem and compatibility issues, and I understand theirs in that context, thus I don't feel quite right criticizing it.
I don't think they can. It's not possible. An implementor could just say "we support es2020 modules with TLA and other features in these contexts, and es2016 modules that don't have TLA in these others". If you simply rephrase the bifrucating as essentially two runtimes based on differing spec versions (and SW really are essentially their own runtime), the language spec doesn't get a word in edgewise about it. I don't think tc39 will be able to do a lick about it, other than some committee members saying "I don't like it". The committee could always say "but if you don't support it in all contexts then you're not a real es2020 runtime" (philosophically), but safari is the only es2016, runtime by that metric, as they're the only runtime that implemented tail calls, but you don't typically see people up in arms about that, do you? |
replacing words in a sentence doesn't inherently preserve the logical meaning of the sentence. your argument leans on the fallacy that esm doesn't work without require being able to load it, which is quite obviously false.
implementations do this with TCO, and therefore the ecosystem as a whole doesn't use TCO. It would really be a shame for the same to happen to TLA. If node follows web workers, we just make the problem worse because of how large a platform node is. Given that the ecosystem already gets along without TLA, why would anyone choose to use it if part of the web and node don't support it? Or, if they do use it, they can just bundle it and bypass the restriction, at the cost of a worse API surface. There is no winning if we disable it. |
This also has the classic issue we have worked hard to avoid where the importer shouldn't impose constraints on the imported. |
I suspect we and browsers have different views on this - we want the imported to be opaque, ideally, so backing implementations can be swapped out as easily as possible. Meanwhile, the browser security module is essentially "trust nothing", so they're going down the route of the importer determining how something is meant to be used and applying constraints on what can be done by the imported. |
TLA isn't a security issue though, they want to disable it because of performance concerns. If that is the case, perhaps we just shouldn't support TLA at all, since node is used for performance sensitive code. |
Which is yet another reason that it just doesn’t make sense to too closely tie browsers to node. |
I don't think that characterization of the browser approach is complete. Generally the browser has chosen to go with the importee determining how it's going to be interpreted (image mime types for |
Also allowing multiple jobs to occur synchronously within another single job seems like a much larger bifurcation given that other hosts simply can't implement that so saying "if we support it we support it." along side the above statement then I see no reconciliation that would allow for |
@Jamesernator it’s quite compatible, that’s how bundlers work. ESM is syntax, however, and can’t be forged, or otherwise intercepted the way require can. |
Bundlers work with best effort, I have never seen a bundler that works in 100% of cases, dynamic However allowing sync Ultimately one of these trade-offs has to be made if TLA is supported (and if browsers ship TLA then one of the these trade-offs is guaranteed):
My preference is strongly the last one as it allows tools to work, provides a clean upgrade path to ESM when mixing formats and doesn't disallow TLA entirely and its only downside is that sometimes |
would you publish code containing TLA knowing that require(esm) can't load it? |
Yes, for old consumers I won't (e.g. eslint config files) but in general yes. This is true for any async code, if something takes a sync function I can't just pass it one that returns a There's a lot of npm code that passes a value and expects a response synchronously. I can't just pass an async function to all of that and expect it to just work. Either I need to make my code synchronous or them make their code asynchronous, they don't just get to magically This is a large point of contention for async/await in general, basically transforming to |
So you've just walked into this point I keep making:
This seems clearly harmful to people trying to adopt the feature, so why would we do this? |
I don't think the analogy with TCO and TLA is appropriate. We don't really have consensus in TC39 about what to do with TCO. However, we recently achieved consensus on Stage 3 for TLA. In my personal opinion, TLA should be permitted wherever possible, and it would be unfortunate to grow the list of environments which have these kinds of restrictions. ServiceWorker is already an environment where people find its restrictions annoying compatibility problems (e.g., the lack of localStorage). |
@littledan the comparison is a feature not available in all implementations, not about the consensus process. I see using the [[Async]] field to block TLA in the same vein as not implementing IsInTailPosition correctly. |
@devsnek We heard strong implementation concerns about implicit tail calls, whereas implementers agreed for TLA to reach Stage 3. Checking [[Async]] is guided by a separate standards proposal, rather than a disagreement about what the standard is. They are simply different situations. |
@littledan I'm trying to say in both cases, feature X is not available in certain places because of a conscious decision for it to not be there. |
I'd like to suggest that we take a step back and get this thread on track. If folks want to discuss I do think it is a worthwhile goal and something that we should be exploring for future versions of Node.js as an iterative enhancement I think the discussion regarding W3C decisions around Service Workers and Top-Level It seems to me like perhaps we should close this thread and start a new one... or at least hide comments that are out of scope because it is honestly hard to figure out what types of solutions we are hoping to reach based on the discussion here. |
Just tracking tooling encountering issues with what we have been rolling out, please add more as they are seen for gathering overall impact:
require()
and.js
now errors fortype: "module"
The text was updated successfully, but these errors were encountered: