sucrase
sucrase copied to clipboard
Add support for new React 17 JSX transform
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
orjsxs
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
andcreateElement
) so they can be separately handled to account for import renaming.
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.
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 usejsxDEV
orjsx
/jsxs
. My current leaning is to makeproduction: 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.