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

Support "exports" field in package.json #208

Closed
tetsuharuohzeki opened this issue Feb 13, 2020 · 16 comments
Closed

Support "exports" field in package.json #208

tetsuharuohzeki opened this issue Feb 13, 2020 · 16 comments

Comments

@tetsuharuohzeki
Copy link

  • Rollup Plugin Name: @rollup/plugin-node-resolve
  • Rollup Plugin Version: 7.1.1

Feature Use Case

Bundle a package seamlessly which provides exports` field as an entry point.

Feature Proposal

The exports field has been added to package.json to define an encapsulating entry points for Node.js ES Module supports.
https://nodejs.org/api/esm.html#esm_package_entry_points

This feature has been unflagged from Node.js v13.7. and I seem that this was backported to v12.16.

@shellscape
Copy link
Collaborator

Thanks for opening a new feature request. This looks like it would be a significant amount of work to add support for.

I'd like to get a link added to a specification for this issue, but I cannot find any.

@lukastaegert this is a new development

My vote is to hold off until all LTS Node versions support this. That is not currently the case.

@lukastaegert
Copy link
Member

I agree. Nevertheless I recently stumbled across this myself, and I think we should start putting some thought into this soon, because it does not only have implications for node-resolve but also for commonjs. Basically this enables specifying different entry points for ESM imports and CJS requires. It would however be unclear, if and how the commonjs plugin would pick this up as well.

@tetsuharuohzeki
Copy link
Author

I'd like to get a link added to a specification for this issue, but I cannot find any.

I seem this specification is defined in Resolver Algorithm section.

@mattpilott
Copy link

I just ran into a scenario where i'd need this, good to see it's already on the cards

@SupremeTechnopriest
Copy link

SupremeTechnopriest commented Apr 11, 2020

Node 13 no longer requires a flag to use these features. Can we unfreeze this issue and start work on adding support? Node 14 (next LTS) is due to hit at the end of the month. It would be nice to get ahead of this. The workaround is really sloppy.

@shellscape
Copy link
Collaborator

Possibly behind a feature flag. But I don't think we can roll out full support until Node 10 and 12 fade away. And the current Maintenance period for Node 12 is bananas long.

@SupremeTechnopriest
Copy link

Possibly behind a feature flag. But I don't think we can roll out full support until Node 10 and 12 fade away. And the current Maintenance period for Node 12 is bananas long.

Sounds good to me!

@jkrems
Copy link

jkrems commented Apr 15, 2020

If nodejs/node#32869 lands, this likely should include support for NODE_ENV based resolution ("production" and "development").

@MylesBorins
Copy link

@shellscape curious why you think that we would need to wait until Node.js 12 EOL. We do plan to remove both the flag and the warning before it moves into maintenance mode.

@lukastaegert
Copy link
Member

lukastaegert commented Apr 15, 2020

Personally I would also like to see movement here once this is unflagged, but there ARE some open questions to answer:

If a package uses conditional exports, such as Rollup itself, then currently resolution can be VERY different between Node 12 and Node 13+. So how should we play this?

  • ignore them for Node <= 12 but observe them for Node 13+? This would make builds non-portable between those Node versions.
  • Add a flag to configure if they are obeyed. I think considering the previous point, we should DEFINITELY put this behind a flag until Node 12 is EOL, at which point we can consider deprecating the old mode.

Moreover, how should this interact with our current "special" resolution aka "mainFields"? What should the precedence order be, or rather, how do we want to configure it? This would require some serious thought to get right.

And last note that this will be one of the biggest additions to this plugin ever. For our current logic, we heavily rely on https://github.com/browserify/resolve. The question is if we roll the conditional exports logic on our own on top of that (I fear we must if we want to be able to configure priorities) or if there is a way to reduce maintenance overhead as we try to stay abreast of extensions to this feature.

So understand if we tread cautiously here.

@MylesBorins
Copy link

The way that Node.js will work internally is to support exports on versions of exports that support it, and fall back to main for versions that do not. Module authors can use the main field as a fallback for versions that don't support exports.

You can see an example here https://github.com/MylesBorins/node-osc/blob/next/package.json#L5-L9

In this module I am using rollup to generate CJS output prior to publish and exposing it both from a conditional export and a main. If I were using node.js features that were unsupported in 10 I could also transpile and have a separate entry point in main than exports. To me the important bit here seems to be the target... although I have to admit I need to read up a bit more on rollup internals and think deeply about how this feature integration would work.

I think updates to resolve, or creating a new similar module that can offer pre / post exports resolve algorithm would be extremely useful as well.

This is something I would personally be willing to invest resources in, this could be with my own time or potentially rallying other colleagues at google to work on. So consider me interested in available to help find a path forward

@lukastaegert
Copy link
Member

That is great to hear! So the thing here is, basically during bundling we take a snapshot of how all imports are resolved to put the right files into the bundle. And one goal should be IMO that if I check out a repository that has some dependencies and builds with rollup and rollup-plugin-node-resolve, then it should produce the exact same bundle no matter if I build on Node 10 or Node 14. So this means that we cannot rely e.g. on Node itself to resolve the conditional exports for use, we need to do it manually just like the resolve package manually replicates Node's old resolution algorithm (with great hooks for caching by the way that makes builds faster if you use this plugin vs. vanilla Rollup that uses just path.resolve...).

At the moment, we heavily rely on resolve's packageFilter option to rewrite the main field of the package.json file in-memory according to our settings. Not sure if this can be easily extended to conditional exports though.

One big problem is that conditional exports can depend on how a file was imported, require or import. But this information is just not available, and is not even available in Rollup core—it would somehow need to come from rollup-plugin-commonjs through some non-existing API. Unless for the first version we just always assume it is an import statement, which is not really wrong for Rollup, but might cause unintended problems.

@jkrems
Copy link

jkrems commented Apr 15, 2020

it should produce the exact same bundle no matter if I build on Node 10 or Node 14.

Agreed! I don't think there should be any expectation that rollup cares about exports support in the version of node that happens to do the bundling. IIRC the resolve package will be updated in the near future to support exports and I assume that's how rollup would access the feature. It should work the same in all versions of node, no matter what node's own resolver does.

yhatt added a commit to marp-team/marp-vscode that referenced this issue Apr 18, 2020
CommonJS build of yaml v1.9.0 throws error `Duplicate export 'YAML'`
while building via rollup.

yaml v1.9.0 has supported ES modules through "exports" field.
(eemeli/yaml#145) However, rollup bundler cannot
recognize it in any plugins.
(rollup/plugins#208)
@lukastaegert
Copy link
Member

Also worth considering: rollup/rollup#3514 Basically I think module will need to remain a very important field and should by default take precedence over exports to have a way to create stateful dual mode packages without the need for a CJS plugin.

@shellscape
Copy link
Collaborator

Closing citing rollup/rollup#3514. We may revisit this issue again in the future. Please feel free to continue discussion.

@eKoopmans
Copy link

eKoopmans commented Jun 21, 2022

Anyone landing here in 2022, I can confirm that the latest @rollup/plugin-node-resolve (13.3.0) does indeed support package.json exports - at least for subpaths, I haven't tested conditional exports.

I can't confirm when support was added, but if you find it not working, make sure to try the latest version!

Edit: Adding a bit more context, to say that the exports field is also currently recommended over main:

  • Node - Main entry point

    All currently supported versions of Node.js and modern build tools support the "exports" field

  • Node - Package entry points

    The "exports" provides a modern alternative to "main"
    For new packages targeting the currently supported versions of Node.js, the "exports" field is recommended

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

8 participants