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

Bug: Cannot import 'react/jsx-runtime' from esm node/webpack 5 #20235

Closed
nstepien opened this issue Nov 12, 2020 · 31 comments
Closed

Bug: Cannot import 'react/jsx-runtime' from esm node/webpack 5 #20235

nstepien opened this issue Nov 12, 2020 · 31 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@nstepien
Copy link

nstepien commented Nov 12, 2020

React version: 17.0.1

Steps To Reproduce

  1. Create a new directory, cd to it.
  2. Run npm i react@17 webpack@5 webpack-cli@4
  3. Create index.mjs with the following content:
    import * as jsx from 'react/jsx-runtime';
    console.log(jsx);
  4. Run node index.mjs
  5. Run npx webpack-cli path/to/index.mjs
    • I had to use an absolute path on my machine or webpack-cli wouldn't find index.mjs, don't know why.

Link to code example: --

The current behavior

> node index.mjs
node:internal/process/esm_loader:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'D:\repos\jsx\node_modules\react\jsx-runtime' imported from D:\repos\jsx\index.mjs
Did you mean to import react/jsx-runtime.js?
    at new NodeError (node:internal/errors:277:15)
    at finalizeResolution (node:internal/modules/esm/resolve:307:11)
    at moduleResolve (node:internal/modules/esm/resolve:742:10)
    at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
    at Loader.resolve (node:internal/modules/esm/loader:85:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:229:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:51:40)
    at link (node:internal/modules/esm/module_job:50:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}
> npx webpack-cli D:\repos\jsx\index.mjs
[webpack-cli] Compilation finished
assets by status 264 bytes [cached] 1 asset
./index.mjs 64 bytes [built] [code generated]

ERROR in ./index.mjs 1:0-41
Module not found: Error: Can't resolve 'react/jsx-runtime' in 'D:\repos\jsx'
Did you mean 'jsx-runtime.js'?
BREAKING CHANGE: The request 'react/jsx-runtime' failed to resolve only because it was resolved as fully specified
(probably because the origin is a '*.mjs' file or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.

webpack 5.4.0 compiled with 1 error in 150 ms
npm ERR! code 1

The expected behavior

No issues importing react/jsx-runtime with no file extensions.

I can think of two solutions:

@nstepien nstepien added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Nov 12, 2020
@nstepien
Copy link
Author

Same thing with react-dom/server:

> node .\index.mjs
node:internal/process/esm_loader:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'D:\repos\jsx\node_modules\react-dom\server' imported from D:\repos\jsx\index.mjs
Did you mean to import react-dom/server.js?
    at new NodeError (node:internal/errors:277:15)
    at finalizeResolution (node:internal/modules/esm/resolve:307:11)
    at moduleResolve (node:internal/modules/esm/resolve:742:10)
    at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
    at Loader.resolve (node:internal/modules/esm/loader:85:40)
    at Loader.getModuleJob (node:internal/modules/esm/loader:229:28)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:51:40)
    at link (node:internal/modules/esm/module_job:50:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

@eps1lon
Copy link
Collaborator

eps1lon commented Nov 17, 2020

Is this code created by the babel transform or did you manually add it?

The new JSX transform is not supposed to be used manually:

The functions inside react/jsx-runtime and react/jsx-dev-runtime must only be used by the compiler transform. If you need to manually create elements in your code, you should keep using React.createElement. It will continue to work and is not going away.

-- https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#whats-different-in-the-new-transform

@nstepien
Copy link
Author

The react/jsx-runtime import is added by the babel preset, I just showed a simplified example for quick bug reproduction.

It can be easily fixed with a simple exports map in package.json:

{
  "type": "commonjs",
  "exports": {
    ".": "./index.js",
    "./jsx-dev-runtime": "./jsx-dev-runtime.js",
    "./jsx-runtime": "./jsx-runtime.js",
    "./": "./"
  },
}

@eps1lon
Copy link
Collaborator

eps1lon commented Nov 17, 2020

I just showed a simplified example for quick bug reproduction.

Could you show an example that uses the jsx-runtime as it's intended? That would help identify the solution better. For example, a fix could be applied to the transformer instead.

@nstepien
Copy link
Author

In the repo I work on, we've started publishing the library using the new runtime, using rollup+babel.
If you install it (npm install react-data-grid), and check node_modules/react-data-grid/lib/bundle.js, you'll see the import:

import { jsxs, jsx, Fragment } from 'react/jsx-runtime';

Some bundlers like webpack 4 or rollup are fine with it, but webpack 5 adopted the newer Node semantics regarding esm, so it expects either a full path with file extension, or react/jsx-runtime to be matched in an exports map.
It's also an issue in Node if you try to do server-side rendering from an esm file.

IMO adding an exports map should be a quick and safe enough fix.
There are multiple transformers that would need fixing otherwise: Babel, TypeScript 4.1, more?
I can go back to the classic runtime so it's not a major issue, but it needs to be fixed upstream one way or the other eventually.

@eps1lon
Copy link
Collaborator

eps1lon commented Nov 17, 2020

you'll see the import

Could you share a minimal reproduction that produced this code?

I understand that this might seem frustrating since you've already identified a solution that works for you. But the code you're proposing has to be maintained by other people as well so they need to understand what problem it tried to solve. If that problem contains code that is not supposed to be used in that way, then it becomes harder to reason about the fix.

@nstepien
Copy link
Author

I've set up a minimal repo that'll generate similar bundles:
https://github.com/nstepien/react-20235
Let me know if this is good enough.

@chuckWu1989
Copy link

I faced the same problem by using creat-react-app.

@nstepien
Copy link
Author

Looks like the exports field got added in #20304, which should resolve this issue.
@sebmarkbage When can we expect a patch release for react 17? It would be nice to backport the exports field to react 16 as well.

@roikoren755
Copy link

For anyone running into this issue, until a fix is released, I also encountered this, and found a way to resolve it.

In my case, I have a few React components exposed from internal packages published to our npm repo. Each component is written in TypeScript, and the published package contains the transpiled files, which already contain the react/jsx-runtime import.
These components are then used in a React application, also written in typescript, which is then compiled using webpack v4.
When running webpack for the application, I got the Module not found: Error: Can't resolve 'react/jsx-runtime' in '.../app/node_modules/@components/Component/src/index' error, similar to the one in this issue.

To fix this, I added 'react/jsx-runtime': require.resolve('react/jsx-runtime'), to my webpack configuration's resolve.alias field.

@Andarist
Copy link
Contributor

I'm reposting this comment of mine from the Babel's tracker but I adjust it slightly to fit better here. You can read it here or in the linked thread.


Adding extensions in the emitted code is being problematic for a couple of reasons and I believe that it should be avoided. I understand that it's unfortunate that things break right now because of it - but node's semantics are very new. The ESM support in node has been released just this month - so it's understandable that some packages are not yet ready in full for it.

The solution for this would be indeed to add exports map like in this PR: #20304 . It was already a great gesture from the React team to ship runtimes for all~ React versions. I suppose they could add exports map in a similar fashion to all of them, just to avoid confusion etc. It makes sense given how many users they have but it's not up to me to decide about this.

As to the current webpack's situation - just don't use .mjs for now. If you rename your file to .js then webpack will gladly resolve the react/jsx-runtime entrypoint. You just don't have to opt-into the new node's semantics right away and just give time for this issue here to be resolved.

@nstepien
Copy link
Author

nstepien commented Dec 3, 2020

@eps1lon Could you update the labels on this issue?

@the-spyke
Copy link

the-spyke commented Dec 24, 2020

@Andarist The issue isn't in .mjs extension, but in a ESM package. If you read the error it says:

or a '*.js' file where the package.json contains '"type": "module"'

My files are just .js and, of course, I've got the error. All file imports of ESM packages should have extensions by the spec.


Alternative way to specified in #20235 (comment) is to just add the extension manually:

resolve: {
  alias: {
    "react/jsx-dev-runtime": "react/jsx-dev-runtime.js",
    "react/jsx-runtime": "react/jsx-runtime.js"
  }
}

@nstepien
Copy link
Author

The alphas of React 18 have the exports field, so at least this issue will be fixed when 18.0.0 is released.

@MikeMatrix
Copy link

I just ran into this issue with react not specifying exports in react@17.0.2, which caused the build to fail using next@11.1.0 in combination with react-data-grid@7.0.0-beta.2.
Issue described further in adazzle/react-data-grid#2568 (comment)

By using yarn patch to apply the fields described in #20235 (comment) fixed the issue completely.

Would be fantastic if this could be patched in for React 17.

@gaearon
Copy link
Collaborator

gaearon commented Apr 11, 2022

This is fixed in React 18.

@gaearon gaearon closed this as completed Apr 11, 2022
@nstepien
Copy link
Author

@gaearon Are React 17 and 16 not supported anymore?

@gaearon
Copy link
Collaborator

gaearon commented Apr 11, 2022

What do you mean by “supported”? If there is a security issue, we release a fix for all majors. For other cases, we don’t typically release updates to non-current majors. In this particular case, we don’t believe backporting would even be safe — ESM configurations and setups are very fragile — so we don’t think it’s safe to do in 17 even if it was actively released.

@sebmarkbage
Copy link
Collaborator

sebmarkbage commented Apr 11, 2022

"Unsafe" as in it will be a breaking change for someone else despite it being a minor release.

deminoth added a commit to lunit-io/insight-viewer that referenced this issue May 17, 2023
jwatzman pushed a commit to getcord/sdk-js that referenced this issue Jun 14, 2023
In order to increase compatibility with potential customer build
configurations, stop using the new JSX transform for React[1].

There are two issues with the new transform.  One is that it's only
supported on React 17+, as well as some backported versions of 15 and
16.  The other is that, pre-React 18, the new transform involves an
import from `react/jsx-runtime`, but that file isn't explicitly
exported by the `react` package, and as a result some build systems
(notably, Webpack 5+) will not be able to process the import [2].

[1] https://legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html

[2] facebook/react#20235 (comment)


Test Plan:

npm run build

npm run tsc-packages

Ran the testbed, all components seemed to render fine, including
ThreadedComments.

monorepo-commit: 0c6f26eb5e8bc8bc3a4dc8d6a16325081d3ec473
yen-tt added a commit to yext/chat-ui-react that referenced this issue Sep 12, 2023
…e), and CRA (webpack) (#38)

**Investigation:**
This PR attempts to fix the following error in alpha/sonic:
![Screenshot 2023-08-31 at 4 40 11 PM](https://github.com/yext/chat-ui-react/assets/36055303/5ec087ff-9e94-4637-9939-96a90cec9221)
This was a result of adding `"type": "module"` to our esm bundle for our library to be compatible with vite/pagesJS.
Vite, and bundlers such as webpack, follow node's module resolution algorithm. and NodeJS have certain rules on how it recognize files as ES modules (https://nodejs.org/api/packages.html#determining-module-system) -- which include adding `"type": "module"` as an option.

The fix is to update all of our paths to be explicit in order to include the mjs/js extensions, including  (e.g. `./components` to `./components/index.js` and `../icons/DualSync` to `../icons/DualSync.js`).

This currently can't be done by typescript compiler (tsc) as it's strictly a nodejs behavior and they don't want to support that ([long issue thread here](microsoft/TypeScript#18442 (comment))), which is frustrating. So we would have to do it manually or have a script to update import/export paths in the final bundle generated by tsc. This is not ideal and can be error prone.

**Solution:**
I decided to **replace tsc with rollup to bundle our library and append .mjs extension to our final esm bundle**. Using `mjs` extension instead of `.js`  also remove the need to do `"type": "module"` in our esm package.json, which previously introduce unnecessary caveats and one-off script to inject it into our esm bundle.

NOTE: the default **interop** behavior for rollup is not compatible with alpha/sonic. As such, the rollup config in this PR uses `auto` to follow Typescript's `esModuleInterop` behavior in how it transpile named, default, and dynamic imports of external deps (the issue with alpha is related to how react-textarea-autosize was imported into ChatInput)

NOTE: updated `tsconfig.json` to use `"jsx": "react"` instead of `"jsx": "react-jsx"` because of the way jsx-runtime is backported to react 16/17 without explicit exports field ([React github issue here](facebook/react#20235 (comment))). We would either need to export two separate bundles for latest and legacy versions in order to continue outputting the JSX syntactic sugar OR we could just output `React.createElement` directly. This is common for many React libraries that support older versions like React 16. (e.g. [ant design](https://github.com/ant-design/ant-design/blob/master/tsconfig.json#L15), [blueprint UI](https://github.com/palantir/blueprint/blob/develop/config/tsconfig.base.json#L9), [evergreen UI](https://github.com/segmentio/evergreen/blob/master/tsconfig.json#L6))

J=CLIP-520
TEST=manual

see that the new build works with:
- the local test-site of the component lib repo 
- pagejs/vite (released v0.6.0-alpha.38.6 to install and test)
- sonic/alpha (released v0.6.0-alpha.38.6 to install and test)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests