CVE-2022-36036 (TypeScript, published 2022-08-29T18:15Z)
Description: value: mdx-mermaid provides plug and play access to Mermaid in MDX. There is a potential for an arbitrary javascript injection in versions less than 1.3.0 and 2.0.0-rc1. Modify any mermaid code blocks with arbitrary code and it will execute when the component is loaded by MDXjs. This vulnerability was patched in version(s) 1.3.0 and 2.0.0-rc2. There are currently no known workarounds.
Severity: nan (CVSS v3 base score: 7.8)
Weakness: CWE-94 \u2014 Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
Repository: https://github.com/sjwall/mdx-mermaid
Files changed:
package.json
src/Mermaid.spec.tsx
src/Mermaid.tsx
src/__snapshots__/Mermaid.spec.tsx.snap
src/mdxast-mermaid.spec.ts
src/mdxast-mermaid.ts
yarn.lock
Commit message: Merge pull request from GHSA-rvgm-35jw-q628



Vulnerable code:
    "react-test-renderer": "^17.0.2",
import mermaid from 'mermaid'
import renderer from 'react-test-renderer'
async function waitFor (ms: number) {
  return new Promise<void>(resolve => {
    setTimeout(() => resolve(), ms)
  })
}

  const component = renderer.create(<Mermaid chart={''} config={{}}  />)
  expect(mermaid.initialize).toBeCalledTimes(0)
  expect(mermaid.render).toBeCalledTimes(0)
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  const component = renderer.create(<Mermaid chart={`graph TD;
      A-->B;
      A-->C;
      B-->D;
      C-->D;`} config={{}} />)
  expect(mermaid.initialize).toBeCalledTimes(0)
  expect(mermaid.render).toBeCalledTimes(0)
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
it('initializes only once', async () => {
  const component = renderer.create(<>
        <Mermaid chart={'foo'} config={{}} />
        <Mermaid chart={'bar'} />
      </>)
  expect(mermaid.initialize).toBeCalledTimes(0)
  expect(mermaid.render).toBeCalledTimes(0)
  await waitFor(1000)
  component.update()
  expect(mermaid.render).toBeCalledTimes(2)
  component.update()
  expect(mermaid.render).toBeCalledTimes(2)
  const component = renderer.create(<Mermaid chart={`graph TD;
      A-->B;
      A-->C;
      B-->D;
      C-->D;`} config={{ mermaid: { theme: 'dark' } } } />)
  expect(mermaid.initialize).toBeCalledTimes(0)
  expect(mermaid.render).toBeCalledTimes(0)
  component.update()
  expect(mermaid.render).toHaveBeenCalled()
  expect(mermaid.initialize).toHaveBeenNthCalledWith(1, { startOnLoad: true, theme: 'dark' })
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
it('re-renders mermaid theme on html data-theme attribute change', async () => {
  const component = renderer.create(
    <html data-theme='light'>
      <Mermaid chart={`graph TD;
            A-->B;
            A-->C;
            B-->D;
            C-->D;`} config={{}} />
    </html>)
  expect(mermaid.initialize).toBeCalledTimes(0)
  expect(mermaid.render).toBeCalledTimes(0)
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  component.update()
  expect(mermaid.render).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  component.update(
    <html data-theme='dark'>
      <Mermaid chart={`graph TD;
            A-->B;
            A-->C;
            B-->D;
            C-->D;`} config={{}} />
        </html>)
  // Time for mutation observer to notice change.
  await waitFor(2000)
  expect(mermaid.render).toBeCalledTimes(2)
  expect(mermaid.initialize).toBeCalledTimes(2)
})
it('renders the output of mermaid into the div', async () => {
  const expectedOutput = 'mermaid output'
  mermaid.render = jest.fn((_, __, cb) => {
    if (cb) cb(expectedOutput, () => 0)
    return expectedOutput
  })
  let component: any
  renderer.act(() => {
    component = renderer.create(
      <Mermaid chart={`graph TD;
            A-->B;
            A-->C;
            B-->D;
            C-->D;`} config={{}} />
    )
  })
  expect(mermaid.render).toBeCalledTimes(1)
  expect(component.toJSON()).toMatchSnapshot()
})
describe('changing the theme at runtime', () => {
  let useRefSpy: jest.SpyInstance
  let html: HTMLHtmlElement
  beforeEach(() => {
    html = document.createElement('html')
    html.setAttribute(HTML_THEME_ATTRIBUTE, LIGHT_THEME_KEY)
    useRefSpy = jest.spyOn(document, 'querySelector').mockReturnValue(html)
  })
  afterEach(() => {
    expect(useRefSpy).toHaveBeenCalled()
  })
  it('reacts to changed theme', async () => {
    const getThemeSpy = jest.spyOn(ThemeHelper, 'getTheme')
    renderer.act(() => {
      renderer.create(
        <Mermaid chart={`graph TD;
              A-->B;
              A-->C;
              B-->D;
              C-->D;`} config={{}} />
      )
    })

    await renderer.act(async () => {
      html.setAttribute(HTML_THEME_ATTRIBUTE, DARK_THEME_KEY)
      await waitFor(1000)
    })

    expect(getThemeSpy.mock.calls.length).toBeGreaterThan(2)
  })
  it('does not react to non-theme attribute changes of html', async () => {
    const getThemeSpy = jest.spyOn(ThemeHelper, 'getTheme')
    renderer.act(() => {
      renderer.create(
        <Mermaid chart={`graph TD;
              A-->B;
              A-->C;
              B-->D;
              C-->D;`} config={{}} />
      )
    })

    await renderer.act(async () => {
      html.setAttribute('manifest', 'some-value')
      await waitFor(1000)
    })
    expect(getThemeSpy).toHaveBeenCalledTimes(2)
  })
import React, { useEffect, useState, ReactElement } from 'react'
import mermaidAPI from 'mermaid/mermaidAPI'
/**
 * Assign a unique ID to each mermaid svg as per requirements
 * of `mermaid.render`.
 */
let id = 0

  config?: Config
export const Mermaid = ({ chart, config }: MermaidProps): ReactElement<MermaidProps> => {
    return <div></div>
  // Watch for changes in theme in the HTML attribute `data-theme`.
  const [theme, setTheme] = useState<mermaidAPI.Theme>(getTheme(html, config))
        setTheme(getTheme(mutation.target as HTMLHtmlElement, config))
  }, [chart, config, theme])
  // When theme updates, rerender the SVG.
  const [svg, setSvg] = useState<string>('')
    const render = () => {
      mermaid.render(`mermaid-svg-${id.toString()}`, chart, (renderedSvg) => setSvg(renderedSvg))
      id++
    }

      render()
    } else {
      // Is there a better way?
      setTimeout(render, 0)
  }, [theme, chart])
  return <div dangerouslySetInnerHTML={{ __html: svg }}></div>
exports[`renders the output of mermaid into the div 1`] = `
<div
  dangerouslySetInnerHTML={
    Object {
      "__html": "mermaid output",
    }
  }
/>
  expect(result.contents).toEqual('\n\n\nconst layoutProps = {\n  \n};\nconst MDXLayout = "wrapper"\nexport default function MDXContent({\n  components,\n  ...props\n}) {\n  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">\n    <h1>{`Heading 1`}</h1>\n    <p>{`No Mermaid diagram :(`}</p>\n    </MDXLayout>;\n}\n\n;\nMDXContent.isMDXComponent = true;')
  expect(result.contents).toEqual("import { Mermaid } from 'mdx-mermaid/lib/Mermaid';\n\n\nconst layoutProps = {\n  \n};\nconst MDXLayout = \"wrapper\"\nexport default function MDXContent({\n  components,\n  ...props\n}) {\n  return <MDXLayout {...layoutProps} {...props} components={components} mdxType=\"MDXLayout\">\n\n    <h1>{`Heading 1`}</h1>\n    <Mermaid config={{}} chart={`graph TD;\n    A-->B;\n    A-->C;\n    B-->D;\n    C-->D;`} mdxType=\"Mermaid\" />\n    </MDXLayout>;\n}\n\n;\nMDXContent.isMDXComponent = true;")
  expect(result.contents).toEqual("import { Mermaid } from 'mdx-mermaid/lib/Mermaid';\n\n\nconst layoutProps = {\n  \n};\nconst MDXLayout = \"wrapper\"\nexport default function MDXContent({\n  components,\n  ...props\n}) {\n  return <MDXLayout {...layoutProps} {...props} components={components} mdxType=\"MDXLayout\">\n\n    <h1>{`Heading 1`}</h1>\n    <Mermaid config={{}} chart={`graph TD;\n    A-->B;\n    A-->C;\n    B-->D;\n    C-->D;`} mdxType=\"Mermaid\" />\n    </MDXLayout>;\n}\n\n;\nMDXContent.isMDXComponent = true;")
  expect(result.contents).toEqual("import { Mermaid } from 'mdx-mermaid/Mermaid';\n\n\nconst layoutProps = {\n  \n};\nconst MDXLayout = \"wrapper\"\nexport default function MDXContent({\n  components,\n  ...props\n}) {\n  return <MDXLayout {...layoutProps} {...props} components={components} mdxType=\"MDXLayout\">\n\n    <h1>{`Heading 1`}</h1>\n    <Mermaid config={{}} chart={`graph TD;\n    A-->B;\n    A-->C;\n    B-->D;\n    C-->D;`} mdxType=\"Mermaid\" />\n    </MDXLayout>;\n}\n\n;\nMDXContent.isMDXComponent = true;")
  expect(result.contents).toEqual("import { Mermaid } from 'mdx-mermaid/lib/Mermaid';\nimport { A } from 'a';\n\n\nconst layoutProps = {\n  \n};\nconst MDXLayout = \"wrapper\"\nexport default function MDXContent({\n  components,\n  ...props\n}) {\n  return <MDXLayout {...layoutProps} {...props} components={components} mdxType=\"MDXLayout\">\n\n\n    <h1>{`Heading 1`}</h1>\n    <Mermaid config={{}} chart={`graph TD;\n    A-->B;\n    A-->C;\n    B-->D;\n    C-->D;`} mdxType=\"Mermaid\" />\n    </MDXLayout>;\n}\n\n;\nMDXContent.isMDXComponent = true;")
    <Mermaid config={{}} chart={\`graph TD;
    <Mermaid config={{}} chart={\`graph TD;
    <Mermaid config={{
      "mermaid": {
        "theme": "dark"
      }
    }} chart={\`graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;\`} mdxType="Mermaid" />
    <Mermaid config={{
      "mermaid": {
        "theme": "dark"
      }
    }} chart={\`graph TD;
    <Mermaid config={{
      "mermaid": {
        "theme": "dark"
      }
    }} chart={\`graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;\`} mdxType="Mermaid" />
    instances.forEach(([node, index, parent]) => {
        type: 'jsx',
        value: `<Mermaid chart={\`${node.value}\`}/>`,
        position: node.position
    // Look for any components
    visit<Literal<string> & { type: 'jsx' }>(ast, { type: 'jsx' }, (node, index, parent) => {
      if (/.*<Mermaid.*/.test(node.value)) {
        // If the component doesn't have config
        if (!/.*config={.*/.test(node.value)) {
          const index = node.value.indexOf('<Mermaid') + 8
          node.value = node.value.substring(0, index) +
          ` config={${JSON.stringify(config || {})}}` +
            node.value.substring(index)
        }
        insertImport(ast)
        return visit.EXIT
      }
    })
"@babel/runtime@^7.8.4":
"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2:
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-shallow-renderer@^16.13.1:
  version "16.14.1"
  resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124"
  integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg==
  dependencies:
    object-assign "^4.1.1"
    react-is "^16.12.0 || ^17.0.0"

react-test-renderer@^17.0.2:
  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
  integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ==
  dependencies:
    object-assign "^4.1.1"
    react-is "^17.0.2"
    react-shallow-renderer "^16.13.1"
    scheduler "^0.20.2"

Fixed code:
    "@testing-library/react": "^11.1.0",
    "react-dom": "^17.0.0",
import { act, render, RenderResult } from '@testing-library/react'
// eslint-disable-next-line import/first
import mermaid from 'mermaid'

const getThemeSpy = jest.spyOn(ThemeHelper, 'getTheme')

const diagram = `graph TD;
A-->B;
A-->C;
B-->D;
C-->D;`

const removeUniqueness = (element: Element) => {
  element.querySelectorAll('style').forEach((v) => v.remove())
  element.querySelectorAll('svg').forEach((v) => {
    v.removeAttribute('id')
    v.parentElement!.removeAttribute('id')
  })
}

const expectMermaidMatch = (result: RenderResult) => {
  removeUniqueness(result.baseElement)
  expect(result.baseElement.parentElement).toMatchSnapshot()
  return result
}

  expectMermaidMatch(render(<Mermaid chart={''} config={{}} />))
  expectMermaidMatch(render(<svg><Mermaid chart={diagram} config={{}} /></svg>))
it('renders with diagram change', () => {
  const config = {}
  jest.useFakeTimers()
  const view = expectMermaidMatch(render(<Mermaid chart={diagram} config={config} />))
  view.rerender(<Mermaid chart={`graph TD;
D-->C;
D-->B;
C-->A;
B-->A;`} config={config} />)
  jest.advanceTimersByTime(1000)
  expectMermaidMatch(view)
  jest.useRealTimers()
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
})

it('initializes only once', () => {
  expectMermaidMatch(render(<>
    <Mermaid chart={'foo'} config={{}} />
    <Mermaid chart={'bar'} />
  </>))
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expectMermaidMatch(render(<Mermaid chart={diagram} config={{ mermaid: { theme: 'dark' } }} />))
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledWith({ startOnLoad: true, theme: 'dark' })
it('renders with mermaid config change', () => {
  const view = expectMermaidMatch(render(<Mermaid chart={diagram} config={{ mermaid: { theme: 'dark' } }} />))
  view.baseElement.querySelectorAll('div.mermaid').forEach((v) => {
    v.setAttribute('data-processed', 'true')
  })
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledWith({ startOnLoad: true, theme: 'dark' })
  view.rerender(<Mermaid chart={diagram} config={{ mermaid: { theme: 'forest' } }} />)
  // await waitFor(1000)
  expectMermaidMatch(view)
  expect(mermaid.contentLoaded).toBeCalledTimes(2)
  expect(mermaid.initialize).toHaveBeenNthCalledWith(2, { startOnLoad: true, theme: 'forest' })
})
it('renders with string mermaid config', () => {
  expectMermaidMatch(render(<Mermaid chart={diagram} config={JSON.stringify({ mermaid: { theme: 'dark' } })} />))
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledWith({ startOnLoad: true, theme: 'dark' })
})
it('re-renders mermaid theme on html data-theme attribute change', () => {
  const component = render(
    <Mermaid chart={diagram} config={{}} />)
  expectMermaidMatch(component)
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  expect(getThemeSpy).toBeCalledTimes(1)
  act(() => document.querySelector('html')!.setAttribute(HTML_THEME_ATTRIBUTE, DARK_THEME_KEY))
  expectMermaidMatch(component)
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expect(getThemeSpy).toBeCalledTimes(1)
  act(() => document.querySelector('html')!.setAttribute(HTML_THEME_ATTRIBUTE, LIGHT_THEME_KEY))
  expectMermaidMatch(component)
})
it('does not react to non-theme attribute changes of html', () => {
  const component = render(<Mermaid chart={diagram} config={{}} />)
  expectMermaidMatch(component)
  expect(mermaid.contentLoaded).toBeCalledTimes(1)
  expect(mermaid.initialize).toBeCalledTimes(1)
  act(() => document.querySelector('html')!.setAttribute('manifest', 'some-value'))

  expectMermaidMatch(component)
import React, { useEffect, useState, ReactElement, useMemo } from 'react'
  config?: Config | string
export const Mermaid = ({ chart, config: configSrc }: MermaidProps): ReactElement<MermaidProps> => {
    return <div className="mermaid" data-mermaid-src={chart}>{chart}</div>
  const config: Config = useMemo(() => typeof configSrc === 'string' ? JSON.parse(configSrc) : configSrc, [configSrc])

  const [rerender, setRerender] = useState<boolean>(false)

  const theme = useMemo(() => getTheme(html, config), [config, rerender])
        setRerender((cur) => !cur)
  }, [])
      document.querySelectorAll('div.mermaid[data-processed="true"]').forEach((v) => {
        v.removeAttribute('data-processed')
        v.innerHTML = v.getAttribute('data-mermaid-src') as string
      })
      mermaid.contentLoaded()
  }, [config, theme])

  useEffect(() => {
    setTimeout(() => mermaid.contentLoaded, 0)
  }, [chart])
  return <div className="mermaid" data-mermaid-src={chart}>{chart}</div>
exports[`does not react to non-theme attribute changes of html 1`] = `
<html
  data-theme="light"
>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`does not react to non-theme attribute changes of html 2`] = `
<html
  data-theme="light"
  manifest="some-value"
>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`initializes only once 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="foo"
      >
        foo
      </div>
      <div
        class="mermaid"
        data-mermaid-src="bar"
      >
        bar
      </div>
    </div>
  </body>
</html>
`;

exports[`re-renders mermaid theme on html data-theme attribute change 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`re-renders mermaid theme on html data-theme attribute change 2`] = `
<html
  data-theme="dark"
>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`re-renders mermaid theme on html data-theme attribute change 3`] = `
<html
  data-theme="light"
>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders with diagram 1`] = `
<html>
  <head />
  <body>
    <div>
      <svg>
        <div
          class="mermaid"
          data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
        >
          graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
        </div>
      </svg>
    </div>
  </body>
</html>
`;

exports[`renders with diagram change 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders with diagram change 2`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
D-->C;
D-->B;
C-->A;
B-->A;"
      >
        graph TD;
D--&gt;C;
D--&gt;B;
C--&gt;A;
B--&gt;A;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders with mermaid config 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders with mermaid config change 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders with mermaid config change 2`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders with string mermaid config 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src="graph TD;
A-->B;
A-->C;
B-->D;
C-->D;"
      >
        graph TD;
A--&gt;B;
A--&gt;C;
B--&gt;D;
C--&gt;D;
      </div>
    </div>
  </body>
</html>
`;

exports[`renders without diagram 1`] = `
<html>
  <head />
  <body>
    <div>
      <div
        class="mermaid"
        data-mermaid-src=""
      />
    </div>
  </body>
</html>
  expect(result.contents).toEqual(`import { Mermaid } from 'mdx-mermaid/lib/Mermaid';


const layoutProps = {\n  \n};
const MDXLayout = "wrapper"
export default function MDXContent({
  components,
  ...props
}) {
  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">

    <h1>{\`Heading 1\`}</h1>
    <p>{\`No Mermaid diagram :(\`}</p>
    </MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`)
  expect(result.contents).toEqual(`import { Mermaid } from 'mdx-mermaid/lib/Mermaid';


const layoutProps = {\n  \n};
const MDXLayout = "wrapper"
export default function MDXContent({
  components,
  ...props
}) {
  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">

    <h1>{\`Heading 1\`}</h1>
    <Mermaid {...{
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>
    </MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`)
  expect(result.contents).toEqual(`import { Mermaid } from 'mdx-mermaid/lib/Mermaid';


const layoutProps = {\n  \n};
const MDXLayout = "wrapper"
export default function MDXContent({
  components,
  ...props
}) {
  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">

    <h1>{\`Heading 1\`}</h1>
    <Mermaid {...{
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>
    </MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`)
  expect(result.contents).toEqual(`import { Mermaid } from 'mdx-mermaid/Mermaid';


const layoutProps = {\n  \n};
const MDXLayout = "wrapper"
export default function MDXContent({
  components,
  ...props
}) {
  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">

    <h1>{\`Heading 1\`}</h1>
    <Mermaid {...{
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>
    </MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`)
  expect(result.contents).toEqual(`import { Mermaid } from 'mdx-mermaid/lib/Mermaid';
import { A } from 'a';


const layoutProps = {\n  \n};
const MDXLayout = "wrapper"
export default function MDXContent({
  components,
  ...props
}) {
  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">


    <h1>{\`Heading 1\`}</h1>
    <Mermaid {...{
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>
    </MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`)
    <Mermaid chart={\`graph TD;
    <Mermaid chart={\`graph TD;
    <Mermaid {...{
      "config": "{\\"mermaid\\":{\\"theme\\":\\"dark\\"}}",
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>
    <Mermaid chart={\`graph TD;
    <Mermaid {...{
      "config": "{\\"mermaid\\":{\\"theme\\":\\"dark\\"}}",
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>

test('Multiple code block', async () => {
  const mdxCompiler = createTestCompiler({ mermaid: { theme: 'dark' } })
  const result = await mdxCompiler.process(`# Heading 1\n
\`\`\`mermaid
graph TD;
    A-->B;
    A-->C;
    B-->D;
    C-->D;
\`\`\`
\`\`\`mermaid
graph TD;
E-->F;
E-->G;
F-->H;
G-->H;
\`\`\``)
  expect(result.contents).toEqual(`import { Mermaid } from 'mdx-mermaid/lib/Mermaid';


const layoutProps = {\n  \n};
const MDXLayout = "wrapper"
export default function MDXContent({
  components,
  ...props
}) {
  return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">

    <h1>{\`Heading 1\`}</h1>
    <Mermaid {...{
      "config": "{\\"mermaid\\":{\\"theme\\":\\"dark\\"}}",
      "chart": "graph TD;\\n    A-->B;\\n    A-->C;\\n    B-->D;\\n    C-->D;"
    }} mdxType="Mermaid"></Mermaid>
    <Mermaid {...{
      "chart": "graph TD;\\nE-->F;\\nE-->G;\\nF-->H;\\nG-->H;"
    }} mdxType="Mermaid"></Mermaid>
    </MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`)
})
    instances.forEach(([node, index, parent], i) => {
        type: 'mermaidCodeBlock',
        data: {
          hName: 'Mermaid',
          hProperties: {
            config: i > 0 ? undefined : JSON.stringify(config),
            chart: node.value
          }
        }
    insertImport(ast)
"@babel/runtime-corejs3@^7.10.2":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae"
  integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==
  dependencies:
    core-js-pure "^3.20.2"
    regenerator-runtime "^0.13.4"

"@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
"@jest/types@^26.6.2":
  version "26.6.2"
  resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
  integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
  dependencies:
    "@types/istanbul-lib-coverage" "^2.0.0"
    "@types/istanbul-reports" "^3.0.0"
    "@types/node" "*"
    "@types/yargs" "^15.0.0"
    chalk "^4.0.0"

"@testing-library/dom@^7.28.1":
  version "7.31.2"
  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a"
  integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==
  dependencies:
    "@babel/code-frame" "^7.10.4"
    "@babel/runtime" "^7.12.5"
    "@types/aria-query" "^4.2.0"
    aria-query "^4.2.2"
    chalk "^4.1.0"
    dom-accessibility-api "^0.5.6"
    lz-string "^1.4.4"
    pretty-format "^26.6.2"

"@testing-library/react@^11.1.0":
  version "11.2.7"
  resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818"
  integrity sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA==
  dependencies:
    "@babel/runtime" "^7.12.5"
    "@testing-library/dom" "^7.28.1"

"@types/aria-query@^4.2.0":
  version "4.2.2"
  resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc"
  integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==

"@types/yargs@^15.0.0":
  version "15.0.14"
  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06"
  integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==
  dependencies:
    "@types/yargs-parser" "*"

aria-query@^4.2.2:
  version "4.2.2"
  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
  integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
  dependencies:
    "@babel/runtime" "^7.10.2"
    "@babel/runtime-corejs3" "^7.10.2"

core-js-pure@^3.20.2:
  version "3.24.1"
  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3"
  integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==

dom-accessibility-api@^0.5.6:
  version "0.5.14"
  resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
  integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==

lz-string@^1.4.4:
  version "1.4.4"
  resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
  integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==

pretty-format@^26.6.2:
  version "26.6.2"
  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
  integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
  dependencies:
    "@jest/types" "^26.6.2"
    ansi-regex "^5.0.0"
    ansi-styles "^4.0.0"
    react-is "^17.0.1"

react-dom@^17.0.0:
  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
  integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
  dependencies:
    loose-envify "^1.1.0"
    object-assign "^4.1.1"
    scheduler "^0.20.2"
react-is@^17.0.1:
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==