Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[idea] Parse fenced code block metastrings as JSX props #702

Closed
shawnbot opened this issue Jul 31, 2019 · 20 comments
Closed

[idea] Parse fenced code block metastrings as JSX props #702

shawnbot opened this issue Jul 31, 2019 · 20 comments
Labels
💬 type/discussion This is a request for comments

Comments

@shawnbot
Copy link

shawnbot commented Jul 31, 2019

I originally brought this up in #464 (comment) and again in system-ui/theme-ui#212 (comment), 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:

```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:

```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 metastringprops 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!

@johno johno added the 💬 type/discussion This is a request for comments label Jul 31, 2019
@RichiCoder1
Copy link

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.

@wooorm
Copy link
Member

wooorm commented Apr 23, 2020

👋

Would this need to support expressions? Spread assignment expressions? Such as:

```js {...object} key={<h2>asdasd</h2>}
```

@shawnbot
Copy link
Author

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.

@wooorm
Copy link
Member

wooorm commented Apr 24, 2020

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:

```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>?):

<MyCode live foo={1} bar="baz 2" {...object} key={<h2>asdasd</h2>}>
  ```js
  console.log(1)
  ```
</MyCode>

Meta string is a weird and intriguing thing! It’s the “hidden” place in Markdown.

@shawnbot
Copy link
Author

shawnbot commented May 4, 2020

@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. 🤷

@wooorm
Copy link
Member

wooorm commented May 7, 2020

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.

@KutnerUri

This comment has been minimized.

@wooorm
Copy link
Member

wooorm commented Dec 17, 2020

@KutnerUri You can use a component for that now.

<MyComponent language="tsx" live scope={scope}>
   {"<Button>click me!</Button>"}
</MyComponent>

@KutnerUri

This comment has been minimized.

@wooorm

This comment has been minimized.

@prichey
Copy link

prichey commented Feb 24, 2021

@johno @wooorm As a stopgap, what would you think about using jsdom to do something like:

const dom = new JSDOM(`<!DOCTYPE html><div ${metastring} />`);

then just parsing the attributes off of that? It wouldn't work for objects, functions, etc., but IMO supporting quoted props would make this much more useful than just splitting on spaces.

@prichey
Copy link

prichey commented Feb 25, 2021

Realizing now this could be done with the underlying parse5 rather than the full jsdom package. If you'd entertain a PR with this implementation, I'd be happy to submit one!

@wooorm
Copy link
Member

wooorm commented Oct 20, 2021

This is now possible in the latest RC for MDX 2: https://v2.mdxjs.com.
Specifically, it’s one line to use a plugin to do it: https://github.com/remcohaszing/remark-mdx-code-meta

@wooorm wooorm closed this as completed Oct 20, 2021
@didaktio

This comment was marked as off-topic.

@wooorm

This comment was marked as off-topic.

@didaktio

This comment was marked as off-topic.

@wooorm
Copy link
Member

wooorm commented Mar 31, 2022

I think the problem is coming from that fact I am using remark-rehype,

...right... you shouldn’t use remark-rehype. That’s not in the example. And it’s not going to work.

but I'm getting 'cannot compile node x' errors if I do not

You shouldn’t

I read that MDX supports basic markdown things like headings by default, but evaluate (at least) doesn't seem to.

It does.
I think you should open a question with a reproduction of your original problem!

@didaktio
Copy link

It does. I think you should open a question with a reproduction of your original problem!

Alright. I'll play around a bit and open one if no progress results :)

Thanks for the rapid responses. You've got an insanely massive body of work here.

@muditjuneja
Copy link

I had a similar issue where I was not able to get the meta properties in my code component.
I was able to create a simple rehype plugin that allows you to pass properties like the below -

```mdx path=google.com src=no-src
# Heading 1
```

Later on this is available in the code component as a metaData prop which can be extracted like below -

const components = {
  code: (props) => {
    console.log("props", props);
    const metaDataObject = props.metaData.split(' ').reduce((obj, item) => {
        const [key, value] = item.split('=');
        obj[key] = value;
        return obj;
      }, {});

    return <Code {...props} path={metaDataObject.path} src={metaDataObject.src} />;
  },
}

Using this in gatsby variant of Carbon design system here - https://github.com/carbon-design-system/gatsby-theme-carbon/pull/1289/files#diff-3c2d1eade54db26a66f65bf042875994c93c044734a9c62a19385e0d15435a35R132

Plugin - https://www.npmjs.com/package/rehype-mdx-fenced-code-meta-support

@wooorm
Copy link
Member

wooorm commented Mar 4, 2024

See the last comment when closing, this is closed because there is a solution to it already, I recommend https://github.com/remcohaszing/rehype-mdx-code-props!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💬 type/discussion This is a request for comments
Development

No branches or pull requests

8 participants