rfcs icon indicating copy to clipboard operation
rfcs copied to clipboard

Support named exports in Node.js native ES2015 modules implementation

Open dantman opened this issue 7 years ago • 18 comments

View formatted RFC

I would appreciate it if we kept the merits of .mjs and how Node.js chose to implement ES2015 modules out of the discussion here. We should just accept the fact that ES2015 modules are getting a native implementation in Node.js, this is what we have to work with, and focus on fixing React so that it works with the implementation we are getting.

dantman avatar Mar 31 '18 06:03 dantman

It is unknown when a node will support ESM and what will be the form. https://github.com/nodejs/modules Modules team in node did not publish timeline and previous PRs in the node were postponed. This is already the third attempt of ESM in the node. IMO focus should be on browser ESM implementation that works now.

chyzwar avatar Jul 22 '18 13:07 chyzwar

We also need to consider what to do with react-test-renderer/shallow entry point which currently exports a function in CommonJS.

gaearon avatar Oct 19 '18 04:10 gaearon

We can do the same or just change exports to

import { TestRenderer, Shallow } from 'react-test-renderer';

TrySound avatar Oct 19 '18 07:10 TrySound

I think the general thinking here is that we want to do something like this but it'd be a breaking change and only worth doing along with other breaking changes...?

sebmarkbage avatar Mar 02 '19 06:03 sebmarkbage

I think the general thinking here is that we want to do something like this but it'd be a breaking change and only worth doing along with other breaking changes...?

The Node.js ESM implementation has been pushed back to October and has changed a bit in implementation. So the RFC is kind of on hold.

Theoretically this change should not be breaking since it only affects people writing .mjs files. Additionally Node has even changed the behaviour in a way that adding an index.mjs won't break current users using .mjs. That said there's a chance WebPack could completely screw up that plan. So perhaps it should be bundled with breaking changes anyways.

It's tangentially related. But I wonder if we should also revisit TypeScript. It's really annoying having to use import * as React from 'react'; instead of import React, {useState} from 'react';. Which could easily be solved with essentially a const React = {...}; modules.export = {...React, default: React};.

dantman avatar Mar 02 '19 07:03 dantman

@dantman Default export is not the right way to import from react. React is a namespace. Logically it should not be default export. Default export also requires hacks like babel plugin to transform

React.createElement()
// to
const { createElement } = React
createElement()

When bundler may do this for free and even better will not generate const { createElement } = React in every file.

Treeshaking will not be significant with react but it still can be considered as a feature.

TrySound avatar Mar 02 '19 08:03 TrySound

React should be a namespace. It currently is not.

Jessidhia avatar Mar 02 '19 08:03 Jessidhia

Default export is not the right way to import from react. React is a namespace.

You're going to have to rally for JSX to be changed then. Because the default is React.createElement and React must be defined in context.

Treeshaking will not be significant with react but it still can be considered as a feature.

Treeshaking will be completely nonexistent. React is bundled into a single file and uses an object export.

dantman avatar Mar 02 '19 08:03 dantman

You're going to have to rally for JSX to be changed then. Because the default is React.createElement and React must be defined in context.

Not at all. import * as React from 'react'; is universal solution. Default export can be provided as a temporary fix.

Treeshaking will be completely nonexistent. React is bundled into a single file and uses an object export.

I'm talking about the time when react will have esm and named exports.

TrySound avatar Mar 02 '19 08:03 TrySound

I'm talking about the time when react will have esm and named exports.

React's single file bundles were an intentional choice, even if React ever gets ESM it will likely still use single file bundles.

dantman avatar Mar 02 '19 08:03 dantman

@dantman Wait, I didn't say anything about react bundles. I like them.

My point was that react should not have default export because it increases user bundle a lot or requires babel plugins to solve the problem and still with trade offs.

TrySound avatar Mar 02 '19 09:03 TrySound

How does this proposal hold up against the latest shipped Node implementation?

gaearon avatar Feb 10 '20 23:02 gaearon

How does this proposal hold up against the latest shipped Node implementation?

There seem to be some changes to the ESM handling. The general idea of the RFC seems to still be valid, but about 10% needs a few tweaks for the ESM changes.

Scanning the new https://nodejs.org/api/esm.html docs here are the things that stand out:

  • File extensions are now mandatory, so the wrapper would need to explicitly use .mjs and the suggestion of ./index for the main field would be removed.
  • .js files in a package can also be ESM if you include "type": "module" in the package.json but we're not changing the default (unless you want to switch to ESM code for React 17 with optionally a CommonJS .cjs fallback for compatibility with older node) so we'd do the opposite and explicitly declare "type": "commonjs".
  • Instead of placing an index.js and index.mjs next to each other, package.json has a new exports field. It supports conditional exports to direct require and import to different files, so this is how you'd setup that instead of an extension-less main.
    • It's not relevant here, but if exports gains wider adoption it could mean the disappearance of needing to include lib/dist/es/module folders in imports.
  • The proposal I made of using an .mjs wrapper file to export named identifiers appears to be the recommended approach for packages vulnerable to the dual package hazard (where one dep doing import and another doing require would result in two instances of React being imported). So the general idea of the RFC seems to be validated by this.
  • The ESM docs propose a browser conditional export variant (like the variants for import/require/node). If tooling adopts this (whatever tooling people would be using to generate browser native type=module packages) then this could possibly be a solution in the future for supporting browser native module packages without dropping the compatibility with CommonJS-only versions of node. (Basically export a full module version of React and only target it to browsers. This avoids the dual package hazard because Node still uses the wrapper not vulnerable and a browser that only supports ESM won't import the CJS version).

@gaearon Is there any desire to implement this anytime soon? If so I can revisit this now and update the RFC. Otherwise I can revisit it the next time there is new news on Node's ESM implementation (changes from experimental to stable, finishes the loader API, other toolkits adopt the ESM behaviours, or a number of React users indicate they wish to use ESM).

dantman avatar Feb 11 '20 01:02 dantman

How does this proposal hold up against the latest shipped Node implementation?

The import specifiers in ESM .mjs files must contain the full filenames, including extensions (except when using a bare specifier to import from main package exports).

E.g. when importing ESM into ESM:

- import { Foo } from './Foo'
+ import { Foo } from './Foo.mjs'

E.g. when importing CJS into ESM:

- import Foo from './Foo'
+ import Foo from './Foo.js'

jaydenseric avatar Feb 11 '20 01:02 jaydenseric

@gaearon Is there any desire to implement this anytime soon?

"Soon" is relative but it's always helpful to have an up-to-date plan in case it seems like the right moment to start the work. In particular, I'm worry about how to be compatible with Node, bundlers, Flow/TS, and everything else.

gaearon avatar Feb 11 '20 16:02 gaearon

This RFC seems to be getting some attention at the moment. I’d like to direct any folks who are interested in it to the Node.js documentation, and in particular the “Dual CommonJS/ES module packages” section which provides a more complete and up-to-date picture: https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_dual_commonjs_es_module_packages

There is also an ongoing discussion in https://github.com/facebook/react/issues/11503.

stefee avatar Jul 25 '20 11:07 stefee

Our latest thinking was that we'd drop default exports altogether. Named only.

gaearon avatar Aug 18 '21 19:08 gaearon

Our latest thinking was that we'd drop default exports altogether. Named only.

Perhaps this should be deprecated starting React 19?

jonkoops avatar Aug 20 '24 12:08 jonkoops