-
Notifications
You must be signed in to change notification settings - Fork 43
Revisit dot-main in exports field #368
Comments
i'm not aware of any proposal that would ever alter how "main" works, including adding array support. What I'd expect is something like this: {
"main": "./path",
"exports": {
"./path": ["std:x", "./lib/x-polyfill.cjs"]
}
} with a |
Supporting relative The concern with these approaches is that you can import an absolute file path, but not necessarily actually get it when you load it! So there is a lack of transparency to the user. Contrast this with exports, where the package name boundary naturally forms the encapsulation, instead of it applying to all types of importing. Users already know there is some indirection with package imports as The point being, if we want to do "internal rewrites" that is a decision I think we should make very carefully thinking about the usability implications, and justified based on its unique merits. |
@ljharb Your solution seems to leak the existence of the polyfill onto the exports while also assuming that exports are something happening after main, adding one more level of indirection. I think both are surprising. Exports, unlike |
Right - i see it as more of a virtual filesystem, which to me “main” sits on top of - iow i do not see main and exports as siblings, but rather “main” as a high level thing and “exports” as a low level thing. |
I'm not sure I follow how If we start asking people to add intermediate junk paths to |
Maybe I’m not clear on what you want either. I don’t think the format of “main” should ever change. What exact goal do you want to achieve that couldn’t be done with an unchanging main format? |
The use case is in the original issue: I want a simple poly fill package that falls back to a native module if it exists. The package has a single entry point, so I don’t need to (and don’t want to) expose additional paths. The same idea applies for something like React where I might want to declaratively expose a dev and production build, depending on the env. It’s seems weird if that works for subpaths but not for the package name itself. |
then why specify main at all? You can have an exports with just “index.js” in it. |
Right, that's what this is suggesting. Maybe the confusion is that in the current implementation, |
It would certainly allow packages that worked on both old and new node, but not a package that was both CJS and ESM (which is what some have expressed reservations about). I don't recall any concern about allowing a package to have different entry points for entirely different versions of node. |
That’s the hope! But there are some concerns that it may be confusing to
have one main for old node, one implicit main for old node (index), and one
main for new node in exports. It’s possibly confusing but I’m not sure if
we have a better option.
…On Tue, Aug 6, 2019 at 12:57 PM Jordan Harband ***@***.***> wrote:
We explicitly removed that feature to prevent dual-mode packages.
It would certainly allow packages that worked on both old and new node,
but not a package that was both CJS and ESM (which is what some have
expressed reservations about). I don't recall any concern about allowing a
package to have different entry points for entirely different versions of
node.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#368?email_source=notifications&email_token=AAEKR5EPM4PWZI7UTGN5Z2LQDHJRVA5CNFSM4IJSAPTKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3WJBVQ#issuecomment-518820054>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAEKR5HN6NS72L2FJSV4KM3QDHJRVANCNFSM4IJSAPTA>
.
|
If Dual-mode packages would still not be enabled; dual-node packages would; "main" wouldn't have to change (ie, break) its format; etc. The only downside is that you might have to repeat the RHS of "main", in the LHS of "exports" - but that explicitness kind of seems like a pro, not a con? |
I think that's a reasonable outcome. Right now we have an explicit check that prevents |
An overwrite makes much more sense to me than an extends. In other words, if |
@GeoffreyBooth Can you elaborate on the differences you're seeing? Is this agreement with the overall direction of this thread? |
I was referring to the example above (simplified here): "main": "./path",
"exports": {
"./path": "./pkg.js" The intended result in this example is what you might call a “double alias”: I understand that the intent of the double alias is to avoid the situation where we have two ways to define the same thing: "main": "./entry-point-for-legacy-node-where-exports-is-unsupported.js",
"exports": {
".": "./entry-point-for-modern-node.js" |
Gotcha. Yes, I think that's where the discussion ended up. So it sounds like everybody agrees. :) |
Almost; I’d prefer the double aliasing. It seems we all agree on permitted use cases, just not on that one impl detail. |
Yes. One thing I think we should discuss is if we want “dual-across-Node-versions” packages, that is, packages that export their main entry point as CommonJS for old versions of Node and as ESM for modern versions. Such packages are currently impossible, but become no longer so via any version of specifying the main entry point through a method other than The dual package singleton hazard that @MylesBorins brought up wouldn’t apply to such packages, as by definition no single Node runtime would have two versions of the same package loaded via the same specifier. But there is at least one other hazard that I can think of, and that’s dependencies. Say there’s a CommonJS package like This is a story of user error, like a lot of the discussions we’ve had regarding dual packages and |
I think such a user error would be surfaced quickly and fixed; i don’t think we have to worry about that. Whether we support dual mode packages or not, I’d we don’t support dual-node packages, imo we’ve killed ESM before it starts. Without this feature, i don’t think it’s worth unflagging ESM ever. |
Added the agenda label. I assume the open questions are:
Anything else? It feels like other than the above there's general agreement on this problem and that we'd need some sort of solution. |
Not being an active LTS version doesn't mean it's not in use; 2 wouldn't change my answer to 1. |
Seems like we missed discussing this agenda item yesterday, so we should keep it around for next time. |
Option:
|
Sorry i had to drop off for the last 15 minutes; can you elaborate further? |
@ljharb Sure, let me try to sum the two things up.
|
I don't consider a flag a reasonable workaround; I could be relying on exports working, and then suddenly add a dependency that forces me to use |
If you just add a dependency that uses |
The goal is to increase compatibility, not create a forking point for the ecosystem. |
I don't think anybody disagrees there. Is your vote that we go back to "add array syntax to |
No; that’s much less backwards compatible. I think this overlaps heavily with the use case that motivates dual packages - it’s critical to be able to have a package that works on both node 10 and 14 with the same specifiers, and ideal that it can be imported and required with the same specifiers on node 14. I suspect any solution to this will address the concern, making a no-exports flag useless. Additionally, since specifying exports is meant to prevent import/require of certain files, I’d not want a trivial node flag to be able to bypass that. |
I'm not sure what alternatives you see that I'm missing. From what I can tell, when introducing
Any attempt I made in trying to figure out a 3rd way failed. Even when not accounting for overhead/cost. Please also note that this is about packages that explicitly made the choice to have inconsistent behavior between |
I think if a package has decided to be user-hostile, and we have no way to prevent it, then we have to just allow it. Our mandate should be to make user-friendly ways easy and ergonomic, and failing that, at least possible. |
Is it fair to say that using the terms above you'd favor |
Yes, I think that's fair to say. In other words, I actively don't want a flag to bypass the dual-mode/dual-node hazard, I want to (without blocking exports) move forward looking for a solution to the wider problem of that hazard. |
I don't believe that the I don't think this has to do with user hostile... Folks may choose to offer legacy support via main so that they can continue to support, for example, Node.js 10 while it is still supported in LTS for another 20 months. I'm having a hard time grasping why this pattern would be problematic. The non-exports variant would be backwards compatible... it would also make sense to be a wholesale change. You are either running in the old mode or the new mode. We have lots of prior art in node of offering this type of opt-out. |
The problem is when An example of a "good" use of {
"main": "./lib/uuid-full.js",
"exports": [
"std:uuid",
{ "onlyIfWebCrypto": "./lib/uuid-small.js" },
"./lib/uuid-full.js"
]
} The major point in that example is: This would be the "user-hostile" version: {
"main": "./lib/uuid-full.js",
// no need to care about a complete polyfill, surely environments that need one
// would use main and not exports!
"exports": "./lib/uuid-minimal.js"
} Having |
Closed via nodejs/node#29494 |
Originally the exports proposal had a way to specify the main field as well. The following was the equivalent of setting
main
to'./lib/entry.mjs'
:We discarded this for two reasons:
But with fallbacks and/or differential serving, we run into a new reason for wanting this second key: Backwards compatibility.
The problem is that if I'm maintaining a package
std-x-polyfill
and I want to fall back to the nativestd:x
module where it's available, I could express it like this:The problem is now: This package cannot be used in anything but the latest version of node. Older versions will not recognize this kind of
main
field. If we'd supportexports[.]
as an alternative with higher priority, the problem can be solved (if a bit verbose):The text was updated successfully, but these errors were encountered: