Skip to content

[TS 4.1] react-jsx breaks a type:module package #41623

Closed

Description

TypeScript Version: 4.1.2 AND next

Search Terms: jsx react-jsx jsxImportSource react/jsx-runtime extension

When using the new jsx: react-jsx flag, the emitted files should point to react/jsx-runtime.js instead of react/jsx-runtime. Otherwise Webpack fails.

Code

// my-lib/package.json
{
    "type": "module",
}
// my-lib/tsconfig.json
{
    "compilerOptions": {
        "jsx": "react-jsx",
    },
}
// my-lib/my-component.tsx

function MyComponent() {
    return (
        <div>Hello World<div/>
    )
}

Expected behavior:

The output of the build should be

// my-lib/my-component.js
import { jsx as _jsx } from "react/jsx-runtime.js"

// ...

The react/jsx-runtime should have the .js extension.

Actual behavior:

The output of the build is

// my-lib/my-component.js
import { jsx as _jsx } from "react/jsx-runtime"

// ...

The react/jsx-runtime has no extension. Which breaks Webpack 5 (5.6).

Module not found: Error: Can't resolve 'react/jsx-runtime' in 'project'
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.

For reference:
react/react#20235
babel/babel#12210

Activity

  1. daniele-orlando commented on Nov 21, 2020

    @daniele-orlando
    Author

    For others utility, this is my fix at the moment:

    # fix-build.sh
    
    for dir; do
            find "$dir" -type f -iname '*.js' -not -ipath '*/node_modules/*' -print0 \
            | while read -r -d '' file; do
                    sed -i '' -E 's|(import *.+from *['\''"]react/jsx-runtime)(['\''"])|\1.js\2|g' "$file"
            done
    done

    Quite ugly. 😢

  2. changed the title [-][TS 4.1] Using jsx:react-jsx breaks a type:module package[/-] [+][TS 4.1] Using react-jsx breaks a type:module package[/+] on Nov 21, 2020
  3. changed the title [-][TS 4.1] Using react-jsx breaks a type:module package[/-] [+][TS 4.1] react-jsx breaks a type:module package[/+] on Nov 21, 2020
  4. weswigham commented on Nov 26, 2020

    @weswigham
    Member

    Nope! The current behavior is expected - You're using a version of a runtime (well, webpack) that doesn't (fully?) support exports maps (or your specific version of the react package is missing them), which is what allows the extensionless imports to function - the transform, canonically, produces imports without extensions. There was a big todo about this over on the babel repo a few months ago, where the behavior got flip-flopped across the ecosystem (we tried adding extensions, this broke preact and friends who'd already adopted exports maps successfully, then had to go back to not adding extensions) (and if you disagree, you should probably open an issue on the babel/react issue trackers, since we're just trying to match them).

  5. daniele-orlando commented on Nov 26, 2020

    @daniele-orlando
    Author

    Thank you very much Wesley Wigham (@weswigham) for the extensive explanation.

    I understand the point, but using the exports map seams like a last resort and of course it is an implementation detail of the consumed package on which the consumer has no control.
    For example:
    I'm consuming React, I can't force the React team to map an import to react/jsx-runtime into react/jsx-runtime.js in their package.json file.
    They could simply argue, "well man, just import the right file" and they would be right.
    As you can see here, they do not use the exports map.
    https://github.com/facebook/react/blob/v17.0.1/packages/react/package.json

    Technically speaking, a configuration flag/option should configure something.
    I'm not saying that using by default the '.js' extension is right or a mistake, but that the tsc compiler, given an already present jsxImportSource option, should be configurable by the developer to emit what the developer wants.

    If for some reasons it is not possible to exploit the already present jsxImportSource, it would just easy as introducing an option like jsxImportPath where the developer can instruct the tsc compiler to emit what the developer needs.
    Example:

    jsxImportPath: "react/jsx-runtime.js"
    

    It does not break anything, it works with "legacy" packages without an export map and does not need a runtime able to remap the imports.

  6. nicolo-ribaudo commented on Nov 27, 2020

    @nicolo-ribaudo
    Contributor

    Reposting from the issue on the Babel side: soon React will only support the version without the extension: https://github.com/facebook/react/pull/20304/files#diff-1f344ac391eeecc21ec0f01fb07430a47f4b80d20485c125447d54c33c4bbfc4R34-R35

  7. dtrelogan commented on Apr 15, 2021

    @dtrelogan

    I'm trying to transpile React 17.0.2 with TypeScript configured to use "module": "ES6" (rather than "CommonJS").

    To get the output to run in Node, for now seems you have to manually update node_modules/react/package.json and add this configuration, reproduced here:

    "exports": {
        ".": "./index.js",
        "./jsx-dev-runtime": "./jsx-dev-runtime.js",
        "./jsx-runtime": "./jsx-runtime.js",
        "./": "./"
      },
  8. daniele-orlando commented on Apr 19, 2021

    @daniele-orlando
    Author

    It will be fixed in React 17.0.3.

  9. jeswin commented on Aug 20, 2021

    @jeswin

    I think it's incorrect for typescript to be emitting CJS code when tsconfig.json instructs it to emit ES modules compatible code.

    It can be solved in bundlers with various configuration options, but it creates problems in various server-side rendering usecases. I needed the following hack while invoking node:

    # Affects all imports, so not ideal.
    node --experimental-specifier-resolution=node yourscript.js

    When I look at the corresponding babel thread, it seems they did the right thing initially and then reverted because it broke preact. IMHO typescript should emit clean output, or at least make it configurable.

    Right now, it looks like there is no way to get clean ES modules compatible output while using tsx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions