hagnerd / gatsby-starter-blog-mdx

Live Demo
https://gatsby-starter-blog-mdx-demo.netlify.com
MIT License
67 stars 43 forks source link

Something's wrong with wrap root element? #18

Closed twerkmeister closed 4 years ago

twerkmeister commented 4 years ago

Hi Matt @hagnerd,

I am struggling with adding components to the MDXProviders global scope and I am seeing this error when compiling a blogpost using this component: Component SCM was not imported, exported, or provided by MDXProvider as global scope

I simply extended the existing wrap-root-element.js

const components = {
  pre: preProps => {
    const props = preToCodeBlock(preProps)
    // if there's a codeString and some props, we passed the test
    if (props) {
      return <Code {...props} />
    } else {
      // it's possible to have a pre without a code in it
      return <pre {...preProps} />
    }
  },
  SCM: props => {return <SCM {...props} />}
}

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={components}>
    {element}
  </MDXProvider>
)

I started wondering whether this MDXProvider was being used at all? Even if I inspect the code elements in your live demo this is what I am seeing

Screenshot 2019-11-28 at 12 44 08

instead of something like this (from the code.js):

export const Code = ({ codeString, language, ...props }) => {
  if (props['react-live']) {
    return (
      <LiveProvider code={codeString} noInline={true}>
        <LiveEditor />
        <LiveError />
        <LivePreview />
      </LiveProvider>
    )
  } else {
    return (
      <Highlight {...defaultProps} code={codeString} language={language}>
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre className={className} style={style}>
            {tokens.map((line, i) => (
              <div {...getLineProps({ line, key: i })}>
                {line.map((token, key) => (
                  <span {...getTokenProps({ token, key })} />
                ))}
              </div>
            ))}
          </pre>
        )}
      </Highlight>
    )
  }
}

So it seems to me pre isn't overwritten by the MDXProvider either and this just has gone unnoticed since there is an existing component for pre. I am pretty new to Gatsby and frontend dev in general so I kinda hit a wall here and was wondering if there was something wrong that you would spot?

Best, Thomas

twerkmeister commented 4 years ago

brief update, seems wrap root element is working as intended, but there is a second MDX Provider below it in the react component hierarchy without any component prop. So the outermost Provider is being overwritten.

Screenshot 2019-11-28 at 13 48 50 Screenshot 2019-11-28 at 13 49 03

Wondering whether this has something to do with a default layout being applied by the gatsby mdx plugin https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-mdx#default-layouts

twerkmeister commented 4 years ago

seems gatsby-plugin-mdx is doing its own root wrapping

https://www.christopherbiscardi.com/post/removing-gatsby-mdx-wrappers/

https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-mdx

twerkmeister commented 4 years ago

I got it to work the following way (also the code examples work now):

I removed wrap-root-elements.js, gatsby-ssr.js and gatsby-browser.js as the root wrapping was being overwritten anyway. What instead helped was defining a custom layout for mdx pages and importing+default exporting it in the mdx file as shown here https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-plugin-mdx#default-layouts

src/components/mdx-layout.js

import React from "react"
import { MDXProvider } from "@mdx-js/react"
import { Code } from '../components/code'
import { preToCodeBlock } from 'mdx-utils'
import SCM from '../scm/scm'

// components is its own object outside of render so that the references to
// components are stable
const components = {
  pre: preProps => {
    const props = preToCodeBlock(preProps)
    // if there's a codeString and some props, we passed the test
    if (props) {
      return <Code {...props} />
    } else {
      // it's possible to have a pre without a code in it
      return <pre {...preProps} />
    }
  },
  SCM: props => {return <SCM {...props} />}
}

export default ({ children }) => (
  <MDXProvider components={components}>
    {children}
  </MDXProvider>
)

content/blog/hi-folks/index.mdx

---
title: New Beginnings
date: '2015-05-28T22:40:32.169Z'
---

import PageLayout from './../../../src/components/mdx-layout'

export default PageLayout
...

Unfortunately I didn't get the default layout mechanism in gatsby-config.js to work as outlined in the docs linked above, which would make importing the layout into every mdx file unnecessary. I think the reason is that gatsby-plugin-mdx is quite opinionated and makes a few assumptions that this project isn't making. For example, I assume the right way to compile the mdx files would be to use gatsby-plugin-page-creator and point it at the content directory and to turn the blog-post.js into the default layout for gatsby-plugin-mdx files. However, I wouldn't know how to fill the previous and next fields in this case. In any case, this feels like a bigger change which I am not entirely comfortable making given my limited understanding of the interplay of the different components. Maybe one of the maintainers can take the lead here beyond my quick fix.