facebook / react

The library for web and native user interfaces.

Home Page:https://react.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

nstepien opened this issue · comments

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:

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'
}

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

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",
    "./": "./"
  },
}

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.

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.

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.

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.

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

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.

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.

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.

@eps1lon Could you update the labels on this issue?

@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"
  }
}

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

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.

commented

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.

@roikoren755

Any chance you could add your whole webpack config?

I have this for my next config and it doesn't appear to work still:

    if (!isServer) {
      config.resolve.fallback.fs = false;
    }
    config.resolve.alias = {
      ...config.resolve.alias,
      "react/jsx-dev-runtime": "react/jsx-dev-runtime.js",
      "react/jsx-runtime": "react/jsx-runtime.js",
    };

    return config;
  }

@roikoren755

Any chance you could add your whole webpack config?

I have this for my next config and it doesn't appear to work still:

    if (!isServer) {
      config.resolve.fallback.fs = false;
    }
    config.resolve.alias = {
      ...config.resolve.alias,
      "react/jsx-dev-runtime": "react/jsx-dev-runtime.js",
      "react/jsx-runtime": "react/jsx-runtime.js",
    };

    return config;
  }

Keep in mind I didn't use next.js, but webpack@4 directly. Try

    if (!isServer) {
      config.resolve.fallback.fs = false;
    }
    config.resolve.alias = {
      ...config.resolve.alias,
      "react/jsx-dev-runtime": require.resolve("react/jsx-dev-runtime"),
      "react/jsx-runtime": require.resolve("react/jsx-runtime"),
    };

    return config;
  }

@nstepien do you know if there are any pull-requests open for backporting the fix to v17 & v16?

Not that I know of.

Following changes that worked for me, I modified the module Jsk-runtime import statement and restarted the server.

Before : import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
After : import { jsxs, jsx, Fragment } from 'react/jsx-runtime.js';

I am running into the following issue at our company due to the file resolution pointing to a file using require statement when attempting to resolve as an esm module. The problem is specific to yarn 3 I believe but their solution is simply trying to resolve files based on module support. I expect this will present itself beyond yarn 3 (or may have already). Also complicated because the jsx-runtime import statement is actually coming through @emotion/core so in this case it's a transitive dependency for the projects of concern.

Is this a problem already solved in react 17?
image

commented

For people using the recently releasedcreate-react-app@5.0.0, there's currently an ongoing pull request #11797, expected for the 5.0.1 release.

Until then, here's a solution:

  1. Install patch-package, which allows you to modify node_modules dependency directly in the code - and keep the modification available even after running a fresh npm install. yarn add patch-package postinstall-postinstall
  2. Add a postinstall hook to your projects package.json scripts "postinstall": "patch-package"
  3. Open the node_modules/react-scripts/config/webpack.config.js in your code editor, scroll to about line 320 with the alias and add:
      alias: {
+      "react/jsx-dev-runtime": "react/jsx-dev-runtime.js",
+      "react/jsx-runtime": "react/jsx-runtime.js",
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
        'react-native': 'react-native-web',
        // Allows for better profiling with ReactDevTools
        ...(isEnvProductionProfile && {
          'react-dom$': 'react-dom/profiling',
          'scheduler/tracing': 'scheduler/tracing-profiling',
        }),
        ...(modules.webpackAliases || {}),
      },
  1. Apply the patch with yarn patch-package react-scripts. This will now create a patch directory in your project root, with the patch file.

Now the patch is available even after a fresh installation.
Also, set a reminder to check for pull request #11797 release to remove the patch. Removing the patch is pretty straightforward. Remove patch-package postinstall-postinstall, remove the package.json script & delete the /patch dir (if you don't use patch-package for anything else).

In addition to #20235 (comment), I suggest not to use alias, this will be breaking again when 7.0.3 is published, use fallback instead:

resolve: {
    fallback: {
        'react/jsx-runtime': 'react/jsx-runtime.js',
        'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
    },
},

This will successfully resolve in 7.0.3 and 18.x and fallbacks to *.js when they are not importable.

commented

In addition to #20235 (comment), I suggest not to use alias, this will be breaking again when 7.0.3 is published, use fallback instead:

resolve: {
    fallback: {
        'react/jsx-runtime': 'react/jsx-runtime.js',
        'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
    },
},

This will successfully resolve in 7.0.3 and 18.x and fallbacks to *.js when they are not importable.

Shouldn't this then be mentioned/fixed in CRA #11797?

And any source?

I'm using craco and the problem was solved when I added in craco.config.js

webpack: {
    alias: {
      'react/jsx-runtime': 'react/jsx-runtime.js',
      'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
    },
 },

I'm using react-scripts v5, react 17 with react-data-grid: "7.0.0-beta.8"

I'm using craco and the problem was solved when I added in craco.config.js

webpack: {
    alias: {
      'react/jsx-runtime': 'react/jsx-runtime.js',
      'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
    },
 },

I'm using react-scripts v5, react 17 with react-data-grid: "7.0.0-beta.8"

I thought CRACO did not support react-scripts 5 yet?

In addition to #20235 (comment), I suggest not to use alias, this will be breaking again when 7.0.3 is published, use fallback instead:

resolve: {
    fallback: {
        'react/jsx-runtime': 'react/jsx-runtime.js',
        'react/jsx-dev-runtime': 'react/jsx-dev-runtime.js',
    },
},

This will successfully resolve in 7.0.3 and 18.x and fallbacks to *.js when they are not importable.

Using the alias doesn't work for me.
But this one works. Thanks dude you saved me a lot of time.

commented

This is fixed in React 18.

@gaearon Are React 17 and 16 not supported anymore?

commented

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.

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