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

Stop using old JSX transform #12253

Merged
merged 1 commit into from Dec 8, 2020
Merged

Stop using old JSX transform #12253

merged 1 commit into from Dec 8, 2020

Conversation

Andarist
Copy link
Member

@Andarist Andarist commented Oct 25, 2020

Q                       A
Fixed Issues?
Patch: Bug Fix?
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes?
License MIT

As the new JSX transform has been shipped and is already used in production by many I think we can safely just switch to it - the new transform has been called "automatic" at some places but it actually is capable of handling the classic transform as well and I believe the plan was to just replace the old code with the new one. It just got implemented this way as at first it was, in fact, experimental.

To make this actually happen in Babel 7 I had to introduce useSpread and useBuiltIns options to the new transform but the plan is to remove them in Babel 8. I've focused right now on making tests pass so those options are only used by createElement - to avoid confusing people I would like to actually use them for new factories as well. I also need to split the code a little bit to make it easier to just remove the compatibility code created for those options later (without having to refactor stuff too much to make it happen).

I think we should just move the -experimental code to the old "builder" package and just remove the -experimental directory. Is this approach OK with you?

I will also put some additional "inline" comments near some code lines to explain a few things with a better context.

@Andarist Andarist added PR: Polish 💅 A type of pull request used for our changelog categories area: react area: jsx labels Oct 25, 2020
@@ -10,6 +10,29 @@ const DEFAULT = {
};

export function helper(babel, options) {
const { useSpread = true, useBuiltIns = false } = options;

if (typeof useSpread !== "boolean") {
Copy link
Member Author

Choose a reason for hiding this comment

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

this is an opposite check to what had to be introduced at an upper level (jsx transform) because from the perspective of this builder the useSpread: true is default but we need to keep the compat for public packages which had useSpread: false as default

state.get("@babel/plugin-react-jsx/usedFragment") &&
!state.get("@babel/plugin-react-jsx/pragmaFragSet")
) {
throw new Error(
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think this makes much sense - it's overly restrictive. The old transform had no such validation and TS also has never thrown for this situation:
https://www.typescriptlang.org/play?ts=4.1.0-beta#code/PQKhAIAECsGcA9x0SYAoNBuAPAEwJYBuAfNsASRjqcMUA

Copy link
Member

Choose a reason for hiding this comment

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

Can you just comment out this code for now?

I'm not 100% sure that we should remove this check (well, we should now for backward compat, but we might re-introduce it in Babel 8). The question is: should we prioritize for React libraries (e.g. Emotion) or for other "frameworks" (e.g. Preact)?

In the first case it's better not to throw because it should "work by default", while in the second case it's better to "prevent a mistake by default".

Copy link
Member Author

Choose a reason for hiding this comment

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

The question is: should we prioritize for React libraries (e.g. Emotion) or for other "frameworks" (e.g. Preact)?

Yeah, definitely a concern and I don't have a perfect answer for that. My main motivation factors here are that not throwing in this case is:

  • backward-compatible
  • matching how TS behaves

Using pragmas is not a very common use case so I would be inclined to favor backward compatibility, usually, people should really have those things configured globally per project in a config file. Pragmas are mostly useful in configuration-constrained tools.

@@ -804,8 +814,9 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
* breaking on spreads, we then push a new object containing
* all prior attributes to an array for later processing.
*/
function buildCreateElementOpeningElementAttributes(path, attribs) {
const props = [];
function buildCreateElementOpeningElementAttributes(file, path, attribs) {
Copy link
Member Author

Choose a reason for hiding this comment

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

changes here are based on the old transform

}

const visitor = helper(api, {
Copy link
Member Author

Choose a reason for hiding this comment

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

this is basically just "./transform-automatic" inlined here

@codesandbox-ci
Copy link

codesandbox-ci bot commented Oct 25, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 4b3c31a:

Sandbox Source
babel-repl-custom-plugin Configuration
babel-plugin-multi-config Configuration

@babel-bot
Copy link
Collaborator

babel-bot commented Oct 25, 2020

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/33065/

@kripod
Copy link
Contributor

kripod commented Oct 27, 2020

I'm not sure if this is strongly related or a new issue should be opened, but the ecosystem might not be ready for such a big change yet. Please see airbnb/babel-plugin-inline-react-svg#91 for further details.

@nicolo-ribaudo
Copy link
Member

@kripod We have two transforms: one that only supports the classic runtime, and one that supports both classic and automatic. We are getting rid of the old transform, but not of the default classic support 😉

state.get("@babel/plugin-react-jsx/usedFragment") &&
!state.get("@babel/plugin-react-jsx/pragmaFragSet")
) {
throw new Error(
Copy link
Member

Choose a reason for hiding this comment

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

Can you just comment out this code for now?

I'm not 100% sure that we should remove this check (well, we should now for backward compat, but we might re-introduce it in Babel 8). The question is: should we prioritize for React libraries (e.g. Emotion) or for other "frameworks" (e.g. Preact)?

In the first case it's better not to throw because it should "work by default", while in the second case it's better to "prevent a mistake by default".

development: false,
runtime,
useSpread:
"useSpread" in options ? options.useSpread : runtime !== "classic",
Copy link
Member

Choose a reason for hiding this comment

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

Can we throw if useSpread is set with the automatic runtime?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is somewhat tricky because the runtime option doesn't match the actual runtime that will be used when transforming.

What should happen for:

// config
const options = { runtime: 'classic', useSpread: false }

// ...
/** @jsxRuntime automatic */

<div {...props} key="foo" />

If we want to keep using extends helper etc here (which seems rather logical) then this would also imply that we should implement extends helper support for jsx, jsxs and jsxDEV outputs, right?

Copy link
Member

Choose a reason for hiding this comment

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

If we want to keep using extends helper etc here (which seems rather logical)

No, this should also throw.

I think that we can throw if:

  • runtime: "automatic" and useSpread: false are set in the options (when instantiating the plugin)
  • useSpread: false is explicitly set in the options and there is a /** @jsxRuntime automatic */ comment (when traversing) <-- In this case it's maybe also ok to just warn and ignore useSpread: false, depending on what would be a breaking change.

Copy link
Member Author

Choose a reason for hiding this comment

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

runtime: "automatic" and useSpread: false are set in the options (when instantiating the plugin)

Technically this was previously allowed, even if useSpread has been silently ignored in such a case. I'm OK with adding an error in a situation like this - I just want to ensure myself that you are as well.

useSpread: false is explicitly set in the options and there is a /** @jsxRuntime automatic */ comment (when traversing) <-- In this case it's maybe also ok to just warn and ignore useSpread: false, depending on what would be a breaking change.

Previously the /** @jsxRuntime automatic */ was completely ignored if the runtime was set to "classic" at the plugin level. Technically one could have it in a file but it would have no effect.


So what you are proposing is that whenever we use the automatic runtime we should ignore options like useSpread regardless of if the runtime has been chosen at the plugin or a file level? So to make those options only applicable when one uses the classic runtime through options (or by just using the default which is still the classic) and not changing the runtime at the file level (so when it stays to be classic). Did I get the intention right?

Copy link
Member

Choose a reason for hiding this comment

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

So what you are proposing is that whenever we use the automatic runtime we should ignore options like useSpread regardless of if the runtime has been chosen at the plugin or a file level? So to make those options only applicable when one uses the classic runtime through options (or by just using the default which is still the classic) and not changing the runtime at the file level (so when it stays to be classic). Did I get the intention right?

Yes, that's exactly what I'd like to see.

@Andarist
Copy link
Member Author

@nicolo-ribaudo friendly 🏓

@Andarist Andarist force-pushed the drop-old-jsx-transform branch 2 times, most recently from 3abc2c0 to 8618ad5 Compare November 22, 2020 13:21
// TODO (Babel 8): Remove `useBuiltIns` & `useSpread`
useBuiltIns: !!opts.useBuiltIns,
useSpread:
"useSpread" in opts ? opts.useSpread : runtime !== "classic",
Copy link
Member

Choose a reason for hiding this comment

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

This can simply be opts.useSpread, unless I'm missing something: it defaults to undefined, which is falsy with the classic runtime and ignored with the automatic one.

@Andarist Andarist force-pushed the drop-old-jsx-transform branch 5 times, most recently from b5478e4 to 6483ba2 Compare November 22, 2020 22:52
@nicolo-ribaudo nicolo-ribaudo added PR: Internal 🏠 A type of pull request used for our changelog categories and removed PR: Polish 💅 A type of pull request used for our changelog categories labels Nov 23, 2020
@jsg2021
Copy link

jsg2021 commented Dec 10, 2020

I'm now seeing this in my build logs... I'm not using those transforms directly. I'm just using the react preset.

Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.

@nicolo-ribaudo
Copy link
Member

That is probably a regression caused by accidentally mixing some logic for the classic and automatic transforms. Could you open a new issue, with your full config and an example of input code that generates that message?

@jsg2021
Copy link

jsg2021 commented Dec 10, 2020

This triggers it...

['@babel/preset-react', { development: true }]

if i set it false, its fine.

@jsg2021

This comment has been minimized.

@jsg2021

This comment has been minimized.

@jsg2021

This comment has been minimized.

@nicolo-ribaudo
Copy link
Member

babelrc: false only disable .babelrc loading. If you're ant to disable also the other config files, you'll need configFile: false.

@Andarist
Copy link
Member Author

@jsg2021 could you paste in your whole Babel config? I expect that you have either loading @babel/preset-react twice or that you include @babel/plugin-transform-react-jsx-self somewhere

@jsg2021
Copy link

jsg2021 commented Dec 10, 2020

my config was being included twice... I think this change made it obvious... I had been using babelrc: false incorrectly. I had a babel.config.js in my project root... and in my webpack and jest configs I pointed to my config as a preset, and attempting to disable inferred config lookup with babelrc: false... I see now I should be using configFile: false. Thanks!

@jsg2021
Copy link

jsg2021 commented Dec 10, 2020

@Andarist I'm not sure how to dump my config... its spread across a few parts. 😅 I'm not seeing those errors anymore and I'm still getting the __self and __source props added... so I'm pretty confident I fixed my config. My config is reused across multiple projects in a react-scripts style package. So it took inspiration for over-specifying the config... dropping that and letting babel figure it out on its own fixed it.

@nicolo-ribaudo
Copy link
Member

nicolo-ribaudo commented Dec 10, 2020

I'm not sure how to dump my config

You can do something like BABEL_SHOW_CONFIG_FOR=./a-file/containing-jsx.jsx npm run build (or with whatever command you are using to build your project).

Anyway, it you found that the problem was exactly that your config was loaded twice it's the error @Andarist is fixing in the PR 👍

@billyjanitsch
Copy link

This is an exciting change! 🥳 Unfortunately, it has at least some observable/undesirable side effects: emotion-js/emotion#2173.

@Andarist
Copy link
Member Author

Just a note - I'm looking already into this but not sure how long this one will take me

@github-actions github-actions bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Mar 14, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: jsx area: react outdated A closed issue/PR that is archived due to age. Recommended to make a new issue PR: Internal 🏠 A type of pull request used for our changelog categories
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants