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

fix: use react-jsx transform and support ES2021 types #12

Merged
merged 3 commits into from
Aug 8, 2024

Conversation

adamstankiewicz
Copy link
Member

Part 1.

Problem

I introduced a .tsx file that renders JSX in an MFE that uses the latest @openedx/frontend-build. This file does not include an import React from "react";, given the MFE runs React 17 where the explicit React import is no longer required (article).

Without including the explicit React import, the following error appears:

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.

However, given in React 17 the React import is not required in .jsx files, .tsx files ideally also should be able to withhold the React import, too.

Discovery

The jsx option in tsconfig.json currently relies on a legacy runtime react, which emits JS using React.createElement. Since React 17, a new JSX transform was introduced that allows using React without explicitly importing React.

However, for usage with TypeScript, the tsconfig.json file needs to opt into using the new transform via the jsx option (documentation).

Options:

  • react-jsx: Emit .js files with the JSX changed to _jsx calls optimized for production
  • react-jsxdev: Emit .js files with the JSX changed to _jsx calls for development only
  • preserve: Emit .jsx files with the JSX unchanged
  • react-native: Emit .js files with the JSX unchanged
  • react: Emit .js files with JSX changed to the equivalent React.createElement calls

For example, this sample code gets transformed as follows:

export const HelloWorld = () => <h1>Hello world</h1>;

Legacy React runtime: "react"

import React from 'react';
export const HelloWorld = () => React.createElement("h1", null, "Hello world");

React with new JSX transform: "react-jsx"

import { jsx as _jsx } from "react/jsx-runtime";
export const HelloWorld = () => _jsx("h1", { children: "Hello world" });

Solution

Extending this base config's jsx option in a consuming MFE's tsconfig.json to use the new react-jsx resolves the error around the React global.

Part 2

The other change in this PR is to add support for "ES2021" types, to enable support for string functions like .replaceAll() in TypeScript files by extending the lib option in tsconfig.json.

Doing so in a consuming MFE's tsconfig.json resolves the TypeScript error about .replaceAll(), but this feels like a fairly common syntax consuming repos may use so makes sense to update the base config directly.

tsconfig.json Outdated
"jsx": "react",
"lib": ["dom", "es6", "dom.iterable"],
"jsx": "react-jsx",
"lib": ["DOM", "ES6", "DOM.Iterable", "ES2021"],
Copy link

@davidjoy davidjoy Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, here's where I've ended up in frontend-base:

"jsx": "react-jsx",
"lib": [
  "dom",
  "dom.iterable",
  "esnext"
],

Admittedly I don't 100% comprehend exactly including things here gets you. Do you really need ES6 and ES2021?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admittedly I don't 100% comprehend exactly including things here gets you. Do you really need ES6 and ES2021?

On a quick test locally, it does appear that specifying a later spec, you don't need the older ones. E.g., ESNext also resolves the .replaceAll() type issue I'm seeing, without explicitly needing to specify ES2021.

So yeah, probably don't need "ES6" and "ES2021". Any objection if I change to "esnext" to match what you have in frontend-base? I don't think it has too much downside? 🤔

Copy link
Member Author

@adamstankiewicz adamstankiewicz Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related, I also wonder if we even need "DOM.Iterable" if we're including all of "DOM" already?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I'm reading, adding stuff to lib just changes what type definitions are available in your source code, so a more permissive list here is kind of okay. The 'target' determines what features get transpiled down ('down-leveled' in the TS documentation). So since our target is ES6, we should be good.

I wasn't quite clear on DOM/DOM.Iterable. I do know that create-react-app generates a tsconfig.json with both. :) (In fact, it matches what I put in frontend-base, which is why I did it...)

Copy link

@davidjoy davidjoy Aug 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference, this is a create-react-app tsconfig.json file:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[inform] I've updated to match the lib as frontend-base / CRA configures it (leaving in the DOM and DOM.iterable).

@adamstankiewicz adamstankiewicz merged commit adfa4d3 into master Aug 8, 2024
4 checks passed
@adamstankiewicz adamstankiewicz deleted the ags/react-jsx-transform branch August 8, 2024 14:27
@edx-semantic-release
Copy link

🎉 This PR is included in version 1.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants