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

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

Closed
daniele-orlando opened this issue Nov 21, 2020 · 7 comments
Closed

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

daniele-orlando opened this issue Nov 21, 2020 · 7 comments
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@daniele-orlando
Copy link

daniele-orlando commented Nov 21, 2020

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:
facebook/react#20235
babel/babel#12210

@daniele-orlando
Copy link
Author

daniele-orlando commented Nov 21, 2020

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. 😢

@daniele-orlando daniele-orlando 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 Nov 21, 2020
@daniele-orlando daniele-orlando changed the title [TS 4.1] Using react-jsx breaks a type:module package [TS 4.1] react-jsx breaks a type:module package Nov 21, 2020
@andrewbranch andrewbranch added the Needs Investigation This issue needs a team member to investigate its status. label Nov 25, 2020
@andrewbranch andrewbranch added this to the TypeScript 4.2.0 milestone Nov 25, 2020
@weswigham
Copy link
Member

weswigham commented Nov 26, 2020

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).

@daniele-orlando
Copy link
Author

daniele-orlando commented Nov 26, 2020

Thank you very much @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.

@nicolo-ribaudo
Copy link

nicolo-ribaudo commented Nov 27, 2020

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

@dtrelogan
Copy link

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

@daniele-orlando
Copy link
Author

It will be fixed in React 17.0.3.

@jeswin
Copy link

jeswin commented Aug 20, 2021

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
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

6 participants