sucrase icon indicating copy to clipboard operation
sucrase copied to clipboard

Add support for new React 17 JSX transform

Open alangpierce opened this issue 3 years ago • 2 comments

React has a published a description of a new JSX transform with a corresponding runtime library available in new React versions: https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html . TypeScript (and others) have already implemented the new transform: https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#jsx-factories . Sucrase should implement some support for it, though the details are still an open question.

Unfortunately, it looks like it's unlikely to simplify the JSX transform for Sucrase, and will likely make it harder. For example:

  • For the prod transform, we'll now need to decide whether to emit jsx or jsxs based on whether the children section (later on in the code) has at least two elements explicitly specified. This sort of "lookahead" behavior isn't well-supported by Sucrase, but could probably be implemented via a special case in the parser and a new token type or a new flag on the token object.
  • We'll need to specially detect the key prop and reorder it to come after the props. Since the key could be an arbitrary expression that may contain newlines, it will be a challenge to correctly preserve line numbers like the rest of Sucrase does. If we simply move the key expression to after the props (preserving newline chars in each), then we'll end up with broken breakpoints if you try to debug within a callback prop. This issue also affected the class fields implementation before I rewrote it in https://github.com/alangpierce/sucrase/pull/313 to keep the code in order. There might be a clever alternative transform that preserves order, like using a comma expression to assign to an intermediate variable, but it certainly seems like a pain.
  • The transform now requires introducing an import and finding an unused name for each imported identifier rather than the old approach of relying on an existing imported name, which likely means more interdependency between the JSX transform and imports transform (even in the ESM case).
  • The difference between dev and prod modes is now more significant, with a different jsxDEV function that will fail if used in production (from my reading). That will likely make Sucrase's zero-configuration goal harder; if we use dev mode by default, it will completely break in production, and if we use prod mode by default, it will miss useful debugging info in dev.
  • The changes to the transform don't make it any more React-agnostic, which I was hoping to see at some point so that other libraries could be used without special configuration.
  • Simply having the old and new transforms will make Sucrase configuration a bit awkward. A possible approach is to make it opt-in in Sucrase 3, default in Sucrase 4, and drop the old transform in Sucrase 5, but there could be a more aggressive transition.

There are some positives with the new system, though:

  • Third-party library configuration will just be one string (importSource) rather than two.
  • We won't need to parse the configured pragma into a base and suffix (e.g. React and createElement) so they can be separately handled to account for import renaming.

alangpierce avatar Jan 09 '21 05:01 alangpierce

This would be awesome! I would definitely use it in vite-react-jsx, as using Babel makes build times ~40% longer for the ./demo folder. One thing to keep in mind: Modules using require should not have import {...} from "react/jsx-runtime" inserted, if possible. Otherwise, I'll have to manually rewrite them (using RegExp hacks) into require calls. Not a huge deal, but it would help if that was handled OOTB by Sucrase.

aleclarson avatar May 24 '21 16:05 aleclarson

I'm finally getting around to this one. As mentioned, there are lots of open questions and details, so I wrote up a tech plan to help gather my thoughts https://github.com/alangpierce/sucrase/wiki/JSX-Automatic-Runtime-Transform-Technical-Plan

Here's a quick summary:

  • There will be a new jsxRuntime option that can be set to "automatic" to enable the new transform. It will be enabled by default in the future.
  • The existing production flag will be used to determine whether to use jsxDEV or jsx/jsxs. My current leaning is to make production: true the default in the future (when enabling the new JSX transform by default) to avoid surprise production crashes.
  • The jsxImportSource option can be used to customize the import, just like other tools allow.
  • For now, there will be a dev usability bug when a key prop has a value that spans multiple lines. The body of that JSX element will have output line numbers that are shifted compared with the input line numbers, so stack traces and debugger breakpoints will be off. The JSX-specific line numbers will still work. Multi-line key expressions are very rare, so this likely will be a minor issue.
  • One interesting thing I found as I was investigating this is that the automatic runtime transform was actually designed as an intermediate state for a long-term transition: https://github.com/facebook/react/issues/20031#issuecomment-710346866 . Hopefully the eventual future state will allow for a simpler implementation, even if this intermediate transform adds complexity.

alangpierce avatar Aug 08 '22 20:08 alangpierce