I originally brought this up in https://github.com/mdx-js/mdx/issues/464#issuecomment-474018084 and again in https://github.com/system-ui/theme-ui/issues/212#issuecomment-516951612, where it was suggested that I open a new issue. Here's the pitch:
Fenced code blocks should be able to specify "first-class" JSX props that get passed down to the MDX tag. Currently, everything after the language identifier is passed along via the metastring prop, which is then passed as key/value strings, so this:
`md
js live foo=bar
````
...produces a remark `code` node with `properties: {live: true, foo: 'bar'}`, which is great! However, the key/value parser doesn't support anything with strings, such as `foo="bar baz"`. When you test this in the [MDX playground](https://mdxjs.com/playground):
````md
```jsx live foo=1 bar="baz 2"
````
...it produces a node with properties: {live: true, foo: '1', bar": '"baz', '2"': true}, which is clearly not right. Rather than inventing a new parser for this, I think it would be best to just treat the metastring as JSX props and either parse them "properly" (i.e. with Babel) at build time or inline them directly in the resulting JSX during the HAST → JSX compilation phase. For instance, I would expect the above example to compile to:
<MDXTag tag="pre" className="language-jsx" live foo={1} bar="baz 2" />
Or, to prevent breakage of existing components, pass them in via a separate prop:
<MDXTag tag="pre" className="language-jsx"
meta={{live: true, foo: '1', bar: 'baz 2'}} />
As a proof of concept, I've hacked together remark-fenced-props to show how at least the remark-level transformation of metastring → props could work. It's _very much_ not production-ready or backward compatible with the key/value pair syntax, but it could easily be made so with a replacement like:
metastring.replace(/(\w+)=([^ ]+)/g, (_, key, value) => {
return isNaN(value) ? `${key}="${value}"` : `${key}={${value}}`
})
Anyway, lmk what you think!
I just ran into this and assumed (wrongly) that this was how it worked. I'd love to see this and would also be willing to do the above PR.
đź‘‹
Would this need to support expressions? Spread assignment expressions? Such as:
~
js {...object} key={<h2>asdasd</h2>}
~
Would this need to support expressions? Spread assignment expressions? Such as:
IMO, yes. At the very least, that would mean not having to introduce some new/different syntax from the one you're using in the document already.
Right, makes sense! Having JSX attributes instead of some other form of attributes too. I am worried that we’re adding another way to do things. Yes, this is nice:
~markdown
js live foo={1} bar="baz 2" {...object} key={<h2>asdasd</h2>}
console.log(1)
~
But you can already author as (or am I missing something? Do you need to get the <pre>/<code>?):
~jsx
~
~markdown
js
console.log(1)
~
~xml
~
Meta string is a weird and intriguing thing! It’s the “hidden” place in Markdown.
@wooorm You're absolutely right: for anything more complicated than 1-2 props, just dropping into JSX makes a ton of sense. I guess I just figured that it would be _simpler_ on the MDX side to inline the metastring as props and just let the JSX parser throw syntax errors, rather than relying on a completely separate key/value parser. 🤷
rather than relying on a completely separate key/value parser. 🤷
It’s indeed a pretty weird key/value parser: splitting on spaces, then splitting on equals—not supporting quotes. Probably not how people expect it to be parsed.
This is needed.
For example, I am trying to make the same 'live' preview, and I need to pass the scope to the Code component:
import { Button } from 'my-button'
const scope = { Button };
Try out this button!
```tsx live scope={scope}
<Button>click me!</Button>
```
This is not possible with today's parser.
@KutnerUri You can use a component for that now.
js
<MyComponent language="tsx" live scope={scope}>
{"<Button>click me!</Button>"}
</MyComponent>
@wooorm I know, just wanted to upvote, and add another use-case.
I'm using MDX to document components, and I would like the document to be portable, i.e. every MDX reader would be able to render its own live playground with it's own styles.
It would be nice to get the component from context, but it doesn't seem to be working
import { Button } from 'my-button';
<components.Code scope={{ button }}>
{"<Button>click me!</Button>"}
</components.Code>
Replacing components.Code with Code in that file, assuming Code is passed into components somewhere, might currently work.
Most helpful comment
I just ran into this and assumed (wrongly) that this was how it worked. I'd love to see this and would also be willing to do the above PR.