I've begun work on a set of components that are going to be
drop-in replacements for gatsby-remark plugins where
appropriate. This list includes plugins such as
gatsby-remark-autolink-headers. Some plugins don't make
sense to implement like this currently, such as
gatsby-remark-images, because these plugins use Gatsby
APIs to optimize images, etc.
Since I practice README-first API design, This brings up an interesting point of API design quite early in the process. The core idea is something like this:
In the end, what we end up with is:
We have the following two options when it comes to how we require users to use gatsby plugins for these components.
That is, do we want to nest
MDXProviders or not. The first
plugin API option lets us include an arbitrary number of
MDXProviders at the root of the tree, which implicitly
lets us also consume any potential components above our
position in the tree and defer to another component later if
we want to. It would look something like this for
implementation (simplified here, it would be multiple
wrapRootElement calls from multiple plugins).
We could, as we said before, progressively enhance components over time. If we originally only used the PrismJS component, we could add a ReactLive component by including it in a lower position in the plugin list.
This is sort of complicated for people to understand and is
not obvious if you're going to write an MDX component plugin
for the first time. Instead what we can do is make this API
explicit and documented, then merge the result into a single
components object and use a single
default export for the gatsby-plugin-mdx components would be
an object matching the
MDXProvider spec. (Take the
following PrismJS example).
Then we say "Do what you want, and return undefined if you
don't want to handle it", which allows us to handle the
passthrough inside of
gatsby-mdx in a more complex way
(which could let us optimize a bit more) instead of pushing
the complexity onto component plugin authors. Our
ReactLive implementation now becomes simpler:
and also handles the "lazy" case where someone just wants to not deal with anything but conditionally using their own component.
gasby-mdx we could basically write some code to attempt
to render each component and if it returns
the next component (otherwise pass it through to usage).
This is ok, but we can also make this a bit more explicit
too by adding guards. An example guard for the
component would be:
gatsby-mdx, we can write code that operates more
This allows us explicit composition while also falling back
to always render something. Some components can completely
take over rendering, like
PrismJS, while others can only
take over if we use a specific style code block: