-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
RFC: MDX plugins #741
Comments
I wonder if we should think about namespacing this early. Something like putting each plugins'
what about at the
This feels like we should experiment with a few different ways to achieve it. On one hand, automatically injecting props somehow seems nice because the Overall this seems like it'll work well with gatsby-plugin-mdx, especially once we get lazy-loading of components. We can drop mdx-toc's Another thing I'm very excited about is the ability to potentially get rid of This could potentially allow us to swap between remark-processed output (like the prism plugins, etc) and repls, leaving the prism processing (or vscode, etc) to build time. We'd need some sort of way to "extend" the exports with remark processing passes that happen after the raw string is plucked into the exports object, which makes me feel like this plugin requires holding state and also requires this plugin having multiple layers operating pre-remark, post-remark, post-hast, etc for full functionality. Thus making this the most complicated mdx plugin I can think of... sorry lol. Given that we've already established that "This will dramatically add to the complexity of the MDX codebase" I think the fact that this sort of really advanced and cool use case is enabled by that complexity is a sign of good things. Even further, this would expose all code blocks in an MDX content into the Gatsby graphql API 🤯 allowing you to pull them easily for opengraph image generation, reconstructing a working example, etc. export const mdxCodeBlocks = {
props: {
lang: 'js'
},
fl23j9fyh: {
rawCode: `import whatever from thing`,
vsCodeHighlightedHtmlString: `<div>...</div>`
extraProps: {}
}
}
If we head down this route we'd probably want to make it pluggable for gatsby and next or anyone else who wants to control how the fetching happens to batch it up, etc. |
This PR changes the internals of the core `@mdx-js/mdx` package to generate a JavaScript syntax tree instead of a string. This fixes escaping issues such as #1219. It makes `mdx-hast-to-jsx` much more palatable. It also prevents several Babel parses. It paves the way for passing in Babel plugins, which is useful for users, but also for us to compile to `React.createElement`, `_jsx`, or Vue’s `h` calls directly and make MDX’s output directly usable. * `babel-plugin-apply-mdx-type-props`: add `parentType` * `mdx`: use `rehype-minify-whitespace` to remove superfluous whitespace * `mdx`: use `hast-util-to-estree` to transform hast to estree * `mdx`: use `estree-to-babel` to transform estree to Babel * `mdx`: generate estree/Babel instead of strings * `mdx`: use `@babel/generator` to serialize Babel AST * `vue`: stop supporting the react transform: (it doesn’t make sense) * `vue`: fix support for props to components Related to GH-741. Related to GH-1152. Closes GH-606. Closes GH-1028. Closes GH-1219.
This PR changes the internals of the core `@mdx-js/mdx` package to generate a JavaScript syntax tree instead of a string. This fixes escaping issues such as #1219. It makes `mdx-hast-to-jsx` much more palatable. It also prevents several Babel parses. It paves the way for passing in Babel plugins, which is useful for users, but also for us to compile to `React.createElement`, `_jsx`, or Vue’s `h` calls directly and make MDX’s output directly usable. * `babel-plugin-apply-mdx-type-props`: add `parentType` * `mdx`: use `rehype-minify-whitespace` to remove superfluous whitespace * `mdx`: use `hast-util-to-estree` to transform hast to estree * `mdx`: use `estree-to-babel` to transform estree to Babel * `mdx`: generate estree/Babel instead of strings * `mdx`: use `@babel/generator` to serialize Babel AST * `vue`: stop supporting the react transform: (it doesn’t make sense) * `vue`: fix support for props to components Related to GH-741. Related to GH-1152. Closes GH-606. Closes GH-1028. Closes GH-1219.
This PR changes the internals of the core `@mdx-js/mdx` package to generate a JavaScript syntax tree instead of a string. This fixes escaping issues such as #1219. It makes `mdx-hast-to-jsx` much more palatable. It also prevents several Babel parses. It paves the way for passing in Babel plugins, which is useful for users, but also for us to compile to `React.createElement`, `_jsx`, or Vue’s `h` calls directly and make MDX’s output directly usable. * `babel-plugin-apply-mdx-type-props`: add `parentType` * `mdx`: use `rehype-minify-whitespace` to remove superfluous whitespace * `mdx`: use `hast-util-to-estree` to transform hast to estree * `mdx`: use `estree-to-babel` to transform estree to Babel * `mdx`: generate estree/Babel instead of strings * `mdx`: use `@babel/generator` to serialize Babel AST * `vue`: stop supporting the react transform: (it doesn’t make sense) * `vue`: fix support for props to components Related to GH-741. Related to GH-1152. Closes GH-606. Closes GH-1028. Closes GH-1219. Closes GH-1382. Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
This PR changes the internals of the core `@mdx-js/mdx` package to generate a JavaScript syntax tree instead of a string. This fixes escaping issues such as #1219. It makes `mdx-hast-to-jsx` much more palatable. It also prevents several Babel parses. It paves the way for passing in Babel plugins, which is useful for users, but also for us to compile to `React.createElement`, `_jsx`, or Vue’s `h` calls directly and make MDX’s output directly usable. * `babel-plugin-apply-mdx-type-props`: add `parentType` * `mdx`: use `rehype-minify-whitespace` to remove superfluous whitespace * `mdx`: use `hast-util-to-estree` to transform hast to estree * `mdx`: use `estree-to-babel` to transform estree to Babel * `mdx`: generate estree/Babel instead of strings * `mdx`: use `@babel/generator` to serialize Babel AST * `vue`: stop supporting the react transform: (it doesn’t make sense) * `vue`: fix support for props to components Related to GH-741. Related to GH-1152. Closes GH-606. Closes GH-1028. Closes GH-1219. Closes GH-1382. Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
@johno – any progress on this actually becoming something available through MDX? |
MDX 2 supports remark, rehype, and recma plugins. I think this issue can be closed? |
Perhaps. I think there’s still room for a package format that supports a) plugins (remark/recma), b) components ( |
Summary
The plugins from remark and rehype have been great to add in features and functionality, however I think MDX-specific plugins can take advantage of features more inline with the MDX language.
Motivation
There are many common needs in the MDX ecosystem (frontmatter, table of contents, syntax highlighting, oembed, etc.) which don't make sense in core but are complex to set up or configure. Not to mention, there's some fragmentation developing around how these things are accomplished in different ecosystems (like Gatsby and Next.js).
Providing a plugin-based approach for these common pieces of functionality can help ensure that the best possible solution can be arrived upon and shared amongst a large group of folks. Not to mention, this could also provide a path for folks to experiment and innovate much more in userland.
A table of contents is the perfect example to show how a piece of functionality in an MDX-based stack needs a "vertical slice" of functionality. This allows a single plugin to set up everything it needs across all transformations
MDX => MDAST => HAST => JSX => React|Vue
.The way it works today
First, let's consider the ecosystem as it currently stands. For this RFC we will use adding a table of contents as the example functionality. Without MDX plugins, the current flow looks like:
remark-slug
github-slugger
whichremark-slug
usesh1-h6
to be linkable usinggithub-slugger
gatsby-plugin-mdx
, etc)One could also use
remark-toc
to achieve some of this, but it doesn't give you much flexibility in how/when the toc is rendered and it doesn't allow the heading information to be imported from outside the document.This isn't the end of the world, but it results in a lot of moving parts where a single plugin could handle a lot of this and end up being more flexible.
Basic (proposed) example
Note: This is not intended to be the implementation/usage but an abstract example to illustrate the functionality.
mdx-toc
End user functionality
Inject table of contents data
By default,
mdx-toc
might process the MDXAST and inject an export of the table of contents as an array.Import table of contents data for usage at the layout level
The injected data could be used at the layout level to add a table of contents.
Use the table of contents inside the document
This
TableOfContents
would receive the data it expects as part of its babel transformation.Plugin developer perspective
A plugin author could expose a file with properties for the different stages in the pipeline (all of which are optional)
Detailed design
An MDX plugin would be given access to each stage of the transpilation pipeline. This means that a plugin could manipulate the MDAST and HAST in order to add rich functionality.
MDX plugins would be passed as their own option and would be appended to the existing remark/rehype plugins. We'd also need to add babel plugins to the existing babel processing.
The component rendering portion would exist in userland to avoid any magic. They'd have to be manually added to MDXProvider at the layout level.
Ultimately this wouldn't require much engineering effort since we'll be mostly forwarding plugins to remark/rehype/babel. Most of the work will be in making sure the API is solid and documenting it.
Other feature considerations
Perhaps it could make sense to allow plugins to compose in additional functionality via the MDXProvider. Considering the above example for
mdx-toc
, that same plugin might want to do something like:Example usage:
Using a context-based approach for plugins to change rendering would allow for more fine-grained customizability for users. Plugins typically act globally on all MDX documents, however it's probable that this isn't desired when MDX is used for a large site with complex needs.
Something worth exploring is the ability to add some type of
getInitialProps
orquery
that can be used to pull in data and statically render. This can be super useful for things like Twitter embeds, oembed, or anything else that only needs to fetch data at compile time.Drawbacks
This will dramatically add to the complexity of the MDX codebase, but I think it will generally be worth the effort and longterm maintenance. It will allow for richer functionality without having to directly modify core and will add additional features that remark plugins can't necessarily provide as easily.
Debugging could get tricky, too. Plugins will have the ability to drastically change MDX compilation and rendering.
Adoption strategy
This would be an optional feature, but will allow us to provide plugins for common asks that don't make sense adding to core.
Related issues
None that I know of.
Acknowledgements
Thanks to @jxnblk, @wooorm, and @ChristopherBiscardi for discussions on this RFC.
The text was updated successfully, but these errors were encountered: