jkrems/proposal-pkg-exports

Support CommonJS?

GeoffreyBooth opened this issue · 9 comments

I wanted to capture @ljharb’s suggestion from the meeting that this “map /foo to /dist/foo.mjs“ functionality might be useful to CommonJS as well. When we wrote this proposal we were assuming that CommonJS’ design space was basically frozen and would essentially be deprecated once Node starts supporting ES modules, but if that’s not the case then we should consider whether this proposal or parts of it should apply to CommonJS.

If "exports" is no longer a signifier that a package exports ESM (or exclusively ESM) then the file specifier proposal would need another field to signify as such. That could be "mode", which would then need to have three potential values (CommonJS, ESM or dual). As we talk about potentially multiple new package.json fields I’m also thinking we should consider a nodejs namespace in package.json. (nodejs/modules#214).

If the CJS design space is frozen then I think much of ESM should be considered frozen as well - CJS is not "legacy" or "deprecated" and certainly not "going away any time soon, if ever", so we should strive to keep them in sync - which will minimize the reasons to stay on CJS, or to avoid migrating to ESM.

I'd expect "mode" to have many future values, considering there's not just CJS and ESM, there's also WASM, HTML, and infinite potential future parse goals.

I think we can support exports in CommonJS, even without affecting exports as a content type map signal. But it would mean that we make it harder to safely import those libraries because we're mixing different loading mechanisms inside of the same object literal.

One answer is to make exports more complicated by forcing additional nesting which I think would just make it harder to use.

I'd expect "mode" to have many future values, considering there's not just CJS and ESM, there's also WASM, HTML, and infinite potential future parse goals.

For me "mode" is just making a subtle switch in the file extension to content type mapping for loading from the disk. I would expect that all other entries in the map aren't affected (JSON, WASM, HTML, etc. would be mapped the same in all modes).

Making a first-class way to switch between two of the infinite potential entries in the mapping seems short-sighted. I'm all for having a way to override the parse goal of a given extension (or mime type, i suppose), but it would have to be generic across all of them - not privilege CJS and ESM JS code.

Re the title here... can I ask why we would want to port to CommonJS?

I feel that this adds complexity to the proposal and brings in a whole lot of new concerns that can be avoided if we keep it to ESM.

For example, dual mode packages can no longer just rely on "exports" being ESM-only... we now want exports to apply differently between CJS and ESM packages. How could we solve this?

We need a good reason to dive into that complexity, that I don't feel has been justified, and it runs the risk of taking the entire proposal down this rabbit hole now.

Yes, I’m inclined to not backport this to CommonJS. If the point is to give people reasons to upgrade to ESM, we shouldn’t be adding features to CommonJS.

People won’t upgrade if the friction is too high - features in ESM need to exist in CJS for that to be the case.

People won’t upgrade if the friction is too high - features in ESM need to exist in CJS for that to be the case.

I don’t follow; why do features new to ESM need to exist in CJS for people to upgrade to ESM?

I can see the logic of CJS features not existing in ESM being a barrier to upgrading, like if ESM didn’t support deep imports; but I don’t see how the lack of some feature in CJS would discourage anyone from upgrading to ESM. They aren’t using feature X now, and they can upgrade to ESM and . . . still not use feature X. If at some later point they decide to start using feature X, they can.

As @bmeck pointed out, I think it’s worth being mindful of the potential desire to backport the mapping feature to CommonJS, as we design the ESM version, so that we don’t foreclose on that possibility. Part of that is ensuring that we don’t treat the exports key as an ESM signifier, and choose or create something else instead. But I don’t think we need to add this feature to CommonJS as part of our ES modules implementation, and certainly not as part of Phase 2. I think it would be a fine feature to add to CommonJS, but that can be a wholly separate effort as a PR against Node core after the ES modules implementation is done.

I think ESM shouldn’t have features that CJS doesn’t - we’re not adding a replacement module system, we’re adding a second module system.

Support CommonJS?

Yes.