mdx icon indicating copy to clipboard operation
mdx copied to clipboard

Support React `__source` prop

Open remcohaszing opened this issue 3 years ago • 8 comments

Initial checklist

Problem

React uses the __source prop display where components originate in the React devtools.

Babel supports this in @babel/babel-plugin-transform-react-jsx-source.

Solution

Inject this prop in a similar way as @babel/babel-plugin-transform-react-jsx-source. This should only happen in development and only for React, but optionally supporting alternative runtimes.

Also I’m willing to implement this.

Alternatives

It could be an external recma plugin, but I think it’s useful to include in core.

remcohaszing avatar May 12 '22 14:05 remcohaszing

  • What about __self?
  • I think this at least makes sense as a utility similar to estree-util-build-jsx, which is then used here
  • Preact has some support in their automatic JSX runtime: https://github.com/preactjs/preact/blob/6a4b346615105fe0b2d8591736ac75ceb7e75890/jsx-runtime/src/index.js#L26. I’m guessing that all automatic runtimes receive this as a separate prop, but I can’t find the “source” in Babel. If that’s the case, estree-util-build-jsx should move the prop to a separate argument, and this option can be turned on in development

wooorm avatar May 12 '22 14:05 wooorm

I found a bit more info in the React RFC for the automatic runtime.

So basically in the classic runtime, we need to compile to:

React.createElement(
  Component,
  {
    ...props,
    __source={{ fileName, lineNumber, columnNumber }}
    __self={this}
  },
  ...children
)

And in the automatic runtime, we need to compile to the following, where isStaticChildren is a boolean indicating if a production runtime would use jsxs instead of jsx:

import { jsxDEV as _jsxDEV } from 'react/jsx-runtime'

_jsxDEV(
  Component,
  props,
  key,
  isStaticChildren,
  { fileName, lineNumber, columnNumber },
  this
)

I still need to figure out when the __self prop or last argument of _jsxDEV needs to be added. It could be added later as well, so I can focus on supporting __source first.

I also believe this shows this functionality belongs in core, especially for the automatic runtime.

remcohaszing avatar May 13 '22 12:05 remcohaszing

We generate JSX. Because otherwise each step needs to know which runtime the user wants, and sometimes they don‘t want to compile it away. Much easier to use JSX as the lingua franca, and then at the end optionally transform JSX into function calls.

I think we need to do the same here: add that prop to JSX.

On the receiving end, we can see from Preact and React, that they optionally expect __source. So, if there’s an automatic runtime, we can define __source on JSX. Though estree-util-build-jsx has to move it from props to the 5th argument.

For the classic runtime, as you linked before, as __source is passed in props, and some h functions don’t handle __source meaning it ends up in HTML, we can’t just define and later pass it.

I don’t support _jsxDEV in estree-util-build-jsx yet. Is that required, because otherwise __source is not used in production? We can, if needed, add support for a development option to estree-util-build-jsx.

wooorm avatar May 14 '22 14:05 wooorm

Maybe we should only focus on the automatic runtime. The main difference is the automatic runtime actually supports a specific dev transform, whereas in the classic runtime some React specific props are used.

estree-util-build-jsx will receive 2 new options, which are only used in development:

options.development

If the automatic runtime is used, this compiles JSX into automatic runtime development mode. (boolean, default: false)

options.file

If the automatic runtime development mode is used, this option is used to provide a file to resolve the JSX node’s file name and position. (VFile)

@mdx-js/mdx will just have to pass through these options. Although maybe there should also be an option to explicitly opt out of this behaviour.

For comparison, TypeScript basically supports 3 types of JSX transforms:

  • react (classic runtime)
  • react-jsx (automatic runtime)
  • react-jsxdev (automatic runtime with dev mode (documented incorrectly))

Babel supports 2 runtime types in their JSX plugin: classic and automatic.

It uses a development option to toggle between the JSX or JSX development plugin in @babel/preset-react.

remcohaszing avatar May 16 '22 15:05 remcohaszing

options.file

Maybe filePath? I don’t see the rest of file being used, likely. It also helps that file.path does not mean file.history[0] (the original file).

I don’t see a need for an extra @mdx-js/mdx option, to opt out. If we run into trouble, we can add it later.

I’m under the impression that TS is a bit messy with everything it supports for JSX, compared to the comments that we, Babel, and SWC support. I’d focus on modelling Babel!


Sounds like we have a plan?!

wooorm avatar May 16 '22 16:05 wooorm

I believe we need the file contents, because estree uses offsets which we need to map to lines and columns. Depending on that we need either a vfile or just the file path. I'll figure that out on the go. Apart from that, we have a plan! 👍

remcohaszing avatar May 16 '22 16:05 remcohaszing

Aren't the positions on nodes enough? 🤔

wooorm avatar May 16 '22 16:05 wooorm

AST explorer only attaches start and end offsets, but apparently the estree format produced by MDX attaches loc as well. So you’re right, we don’t need any additional information, at least not for MDX.

remcohaszing avatar May 16 '22 17:05 remcohaszing