wooorm / xdm

Just a *really* good MDX compiler. No runtime. With esbuild, Rollup, and webpack plugins
http://wooorm.com/xdm/
MIT License
595 stars 18 forks source link

Clarify hast properties to jsx props processing #65

Closed wlib closed 3 years ago

wlib commented 3 years ago

I am trying to use the @mapbox/rehype-prism library to apply syntax highlighting within my mdx files. It uses refractor (prism) to create hast output that later gets turned into jsx by xdm. My code looks roughly like this:

example.mdx

# Some code
```javascript
const someCode = () => {}

`compile.mjs`
````javascript
import fs from "fs/promises"

import { compile } from "xdm"
import rehypePrism from "@mapbox/rehype-prism"

const mdxContent = await fs.readFile(new URL("./example.mdx", import.meta.url))
const xdmOptions = {
  rehypePlugins: [rehypePrism],
  jsxImportSource: "bruh"
}

const result = await compile(mdxContent, xdmOptions)
console.log(result)

My library, bruh, uses jsx props as html attributes directly, meaning no className or htmlFor (just like html). When I write jsx within mdx files, I noticed that xdm correctly will turn input jsx props directly into output jsx props. But, with hast, className (and similar) properties will stay in the output. Even when I wrote an ad hoc rehype plugin to rename the className array into a class string, xdm still output className jsx props.

My understanding is that the hast result is being transformed somewhere down the plugin pipeline with xdm, and presumably hast properties are renamed for compatibility with react.

I do not know how to make xdm treat hast properties such that the output jsx props are identical to what the attributes would be if the hast was instead serialized into html.

When I read through the README, I also noticed a mention that vue, like bruh, also expects class instead of className. I would then assume that my issue with hast property name translation would also affect vue, meaning that vue jsx users would have problems with @mapbox/rehype-prism (and any other rehype plugins producing className's and related properties).

wooorm commented 3 years ago

Could you link to bruh's implementation?

wooorm commented 3 years ago

I do not know how to make xdm treat hast properties such that the output jsx props are identical to what the attributes would be if the hast was instead serialized into html.

The problem is that none of them do that, and thus that hast-to-hyperscript in infers which one of a few common used ones, is used, and goes from there

wlib commented 3 years ago

Could you link to bruh's implementation?

I can't for the relevant part here, because I only have the jsx classic runtime implemented, but it doesn't matter, really. If you are curious, the classic JSX implementation is here

I made a little glitch.me thing to show my problem: https://glitch.com/edit/#!/rogue-big-pull?path=hast.json%3A1%3A0

It basically just shows the fact that even with my classNameToClassPlugin, the jsx output in xdm.jsx still has className props (probably because of hast-to-hyperscript assuming react compatibility, if I read your comment correctly).

wooorm commented 3 years ago

I can't for the relevant part here

That should indeed not matter. xdm supports both the classic and the automatic runtime.


Your classNameToClassPlugin is running too early — you can’t solve this problem inside the hast space. hast reflects the dom and is supposed to use className. See Architecture for more information.

The architecture section also explains how we get from an HTML/DOM AST to a JavaScript AST: hast-util-to-estree. At that point, input JSX but also input markdown constructs or generated markdown/html constructs are all turned into ESTree JSX elements.

I think this is the point where you should transform JSX. A recma plugin that walks the tree, either with estree-util-visit or estree-walker, looks for JSX elements, and changes className to class.

wooorm commented 3 years ago

Closing this. You have a custom JSX implementation that is not compatible with React. That is your responsibility. xdm lets you do that, but you will need to write a JSX compiler for that yourself