Skip to content
This repository has been archived by the owner on Sep 2, 2023. It is now read-only.

Proposal: CommonJS named exports defined in package.json #324

Closed
GeoffreyBooth opened this issue Apr 28, 2019 · 4 comments
Closed

Proposal: CommonJS named exports defined in package.json #324

GeoffreyBooth opened this issue Apr 28, 2019 · 4 comments

Comments

@GeoffreyBooth
Copy link
Member

GeoffreyBooth commented Apr 28, 2019

User Story

I’m the publisher of the coffeescript package, which is written using CommonJS and has a Node API that includes exports compile and VERSION. Currently dependents can use my package in CoffeeScript via CommonJS code like:

const { compile } = require('coffeescript');

I would like to be able to provide named exports for dependents that are using ESM syntax, for example:

import { compile } from 'coffeescript';

Currently, the best I can offer my dependents is:

import CoffeeScript from 'coffeescript';
const { compile } = CoffeeScript;

because the --experimental-modules implementation doesn’t currently support named exports from CommonJS.

Alternatively, I could create a proxy ESM file, like this at the root of my package:

// module.mjs at package root
import CoffeeScript from './lib/coffeescript/coffeescript.js'; // CommonJS main

export default CoffeeScript;
const { VERSION, compile } = CoffeeScript;
export { VERSION, compile };

but then users would have to reference it as from 'coffeescript/module.mjs', not from 'coffeescript'. That’s not terrible, but it’s not as intuitive and familiar (and equivalent to CommonJS) as just from 'coffeescript'.

Efforts So Far

We’ve been blocked on a solution for named exports from CommonJS for a long time. I won’t get into the details, but at the moment none of the solutions currently on the table seem like they will likely be viable.

Package authors want to provide equivalent, and equally good, user experiences for both CommonJS and ESM consumers of their packages. This applies whether the packages themselves are originally written in CommonJS or ESM. Personally, I wouldn’t feel the need for a better dual packages solution if we had a way to provide named exports from the package root (the import { compile } from 'coffeescript' example above). And under the dual CommonJS/ESM package approach, the same specifier (e.g. 'coffeescript') would produce two separate instances, causing potentially unwanted results.

What all the CommonJS named exports solutions proposed so far have in common is that they dynamically attempt to figure out what the named exports from a CommonJS package should be. What if, instead, we rely on the package author to define their CommonJS named exports?

Potential Solution: Named Exports in package.json

Rather than a separate entry point for ESM, what if the package.json simply declared what the named exports are for my package? Then import { compile } from 'coffeescript' could work, because the resolver would know that compile was available as a named export for the package root.

This would nicely complement the package path maps feature we’re also working on. As paths are defined in package.json, the CommonJS named exports could be defined along with them.

If we really want to be ambitious, the named exports could be added to the package.json automatically by the package manager during installation or by npm (the company) via a scan through their registry. The named exports could be determined by running Node in a locked-down sandbox environment and using Reflect.ownKeys on the path, e.g. Reflect.ownKeys(require('coffeescript')). The resulting array could be added to package.json as the named exports for the root path of the package.

@devsnek
Copy link
Member

devsnek commented Apr 28, 2019

why not

import C from './index.cjs';

export const compile = C.compile;
...

i assume you're thinking of something like:

"exportNamesBikeshed": [
  "compile",
  ...
]

but if the first solution already exists....

i really really dislike having the export bindings out of band from the file that provides them.

oh this is proposed because we haven't figured out dual mode nvm...

i still really dislike out of band export declarations

@devsnek
Copy link
Member

devsnek commented Apr 29, 2019

does this need to be an agenda item before we solve dual mode? i wouldn't expect this to be an issue unless we determine that dual mode won't happen.

@ljharb
Copy link
Member

ljharb commented Apr 29, 2019

Extension resolution would address this, ftr, and is quite viable.

@bmeck
Copy link
Member

bmeck commented Aug 4, 2020

@GeoffreyBooth can we close this?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants