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

Upgrade unist-util-visit to 4.1.0 #19

Merged
merged 7 commits into from Jan 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/docusaurus.config.js
Expand Up @@ -23,7 +23,7 @@ const config = {
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
remarkPlugins: [require('mdx-mermaid')],
remarkPlugins: [import('mdx-mermaid')],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docusaurus does not support ESM yet facebook/docusaurus#5379
I am happy to merge this change in (and make the appropriate changes to document this) if there are people wanting to use this library outside of Docusaurus.
I don't see a reason to merge this change until Docusaurus is up upgraded to support ESM if there are no use cases outside of it. The main reason for this is that it could cause confusion in the documentation and will add more support overhead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that doesn't work?
The GitHub Action at https://github.com/candrews/mdx-mermaid/blob/135b622f14972e347b1e6b84f9303d3ea23cee43/.github/workflows/build.yml#L28 passes, meaning that the "doc" project builds (which includes the require -> import change).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code compiles and the React component works but the mdx parser in Docusuarus doesn't process the markdown files.
Running up the docs locally with yarn start at the bottom of intro.md there should be a diagram http://localhost:3000/mdx-mermaid/docs/intro/

Actual:
image

Expected:
image

The diagram on the index works because it is using the React component directly and not the plugin

<Mermaid chart={`
graph LR;
User-->mf[Markdown file];
mf-->cm[\`\`\`mermaid \`\`\`];
cm-->mdx[mdx-mermaid];
mdx-->Mermaid;
Mermaid-->SVG;
`}
// This isn't processed by the parser so needs config passing if it's to be configured
config={{}}/>

The import statement is returning a promise which I guess that the parser is ignoring as it doesn't know how to handle it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A dynamic import is always a promise this is why it needs to be awaited. Docusaurus itself is also using .mjs inside their own config.

Which means the docusaurus config needs to be following:

const createConfig = async () => ({
  ...
  docs: {
    remarkPlugins: [
      (await import('mdx-mermaid')).default,
    ],
  },
  ...
});

module.exports = createConfig;

With that config it should actually work.

sidebarPath: require.resolve('./sidebars.js'),
// Please change this to your repo.
editUrl:
Expand Down
10 changes: 8 additions & 2 deletions jest.config.js
@@ -1,6 +1,12 @@
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
export default {
preset: 'ts-jest/presets/default-esm',
transform: {},
globals: {
'ts-jest': {
useESM: true
}
},
testEnvironment: 'jsdom',
coverageThreshold: {
global: {
Expand Down
7 changes: 4 additions & 3 deletions package.json
Expand Up @@ -11,9 +11,10 @@
},
"author": "Sam Wall (oss@samuelwall.co.uk)",
"license": "MIT",
"type": "module",
"scripts": {
"build": "rimraf lib && tsc",
"test": "jest --coverage"
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage"
},
"repository": {
"type": "git",
Expand All @@ -33,7 +34,7 @@
"peerDependencies": {
"mermaid": ">= 8.11.0 < 8.12.0",
"react": "^16.8.4 || ^17.0.0",
"unist-util-visit": "^2.0.0"
"unist-util-visit": "^4.1.0"
},
"devDependencies": {
"@mdx-js/mdx": "^1.6.22",
Expand All @@ -57,6 +58,6 @@
"rimraf": "^3.0.2",
"ts-jest": "^27.1.2",
"typescript": "^4.5.4",
"unist-util-visit": "^2.0.0"
"unist-util-visit": "^4.1.0"
}
}
2 changes: 1 addition & 1 deletion readme.md
Expand Up @@ -31,7 +31,7 @@ Update `docusaurus.config.js`
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [require('mdx-mermaid')],
remarkPlugins: [import('mdx-mermaid')],
```

Use code blocks in `.md` or `.mdx` files:
Expand Down
63 changes: 38 additions & 25 deletions src/Mermaid.spec.tsx
Expand Up @@ -10,6 +10,16 @@
import mermaid from 'mermaid'
import React from 'react'
import renderer from 'react-test-renderer'

import mermaid from 'mermaid'

import { jest } from '@jest/globals'

const spy = {
initialize: jest.spyOn(mermaid, 'initialize').mockImplementation(() => {}),
render: jest.spyOn(mermaid, 'render').mockImplementation(() => {})
}

import { Mermaid } from './Mermaid'
import {
DARK_THEME_KEY,
Expand All @@ -25,22 +35,22 @@ async function waitFor (ms: number) {
})
}

jest.mock('mermaid')

afterEach(() => {
jest.clearAllMocks()
})

it('renders without diagram', () => {
const component = renderer.create(<Mermaid chart={''} />)
expect(mermaid.initialize).toBeCalledTimes(0)
expect(mermaid.render).toBeCalledTimes(0)
expect(component.toJSON()).toMatchSnapshot()
expect(spy.initialize).toBeCalledTimes(0)
expect(spy.render).toBeCalledTimes(0)
component.update()
expect(mermaid.render).toHaveBeenCalled()
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toHaveBeenCalled()
expect(spy.initialize).toBeCalledTimes(1)
component.update()
expect(mermaid.render).toBeCalledTimes(1)
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toBeCalledTimes(1)
expect(spy.initialize).toBeCalledTimes(1)
})

it('renders with diagram', () => {
Expand All @@ -49,14 +59,15 @@ it('renders with diagram', () => {
A-->C;
B-->D;
C-->D;`} />)
expect(mermaid.initialize).toBeCalledTimes(0)
expect(mermaid.render).toBeCalledTimes(0)
expect(component.toJSON()).toMatchSnapshot()
expect(spy.initialize).toBeCalledTimes(0)
expect(spy.render).toBeCalledTimes(0)
component.update()
expect(mermaid.render).toBeCalledTimes(1)
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toBeCalledTimes(1)
expect(spy.initialize).toBeCalledTimes(1)
component.update()
expect(mermaid.render).toBeCalledTimes(1)
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toBeCalledTimes(1)
expect(spy.initialize).toBeCalledTimes(1)
})

it('renders with config', () => {
Expand All @@ -65,14 +76,15 @@ it('renders with config', () => {
A-->C;
B-->D;
C-->D;`} config={{}} />)
expect(mermaid.initialize).toBeCalledTimes(0)
expect(mermaid.render).toBeCalledTimes(0)
expect(component.toJSON()).toMatchSnapshot()
expect(spy.initialize).toBeCalledTimes(0)
expect(spy.render).toBeCalledTimes(0)
component.update()
expect(mermaid.render).toHaveBeenCalled()
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toHaveBeenCalled()
expect(spy.initialize).toBeCalledTimes(1)
component.update()
expect(mermaid.render).toBeCalledTimes(1)
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toBeCalledTimes(1)
expect(spy.initialize).toBeCalledTimes(1)
})

it('renders with mermaid config', () => {
Expand All @@ -81,14 +93,15 @@ it('renders with mermaid config', () => {
A-->C;
B-->D;
C-->D;`} config={{ mermaid: { theme: 'dark' } } } />)
expect(mermaid.initialize).toBeCalledTimes(0)
expect(mermaid.render).toBeCalledTimes(0)
expect(component.toJSON()).toMatchSnapshot()
expect(spy.initialize).toBeCalledTimes(0)
expect(spy.render).toBeCalledTimes(0)
component.update()
expect(mermaid.render).toHaveBeenCalled()
expect(mermaid.initialize).toHaveBeenNthCalledWith(1, { startOnLoad: true, theme: 'dark' })
expect(spy.render).toHaveBeenCalled()
expect(spy.initialize).toHaveBeenNthCalledWith(1, { startOnLoad: true, theme: 'dark' })
component.update()
expect(mermaid.render).toBeCalledTimes(1)
expect(mermaid.initialize).toBeCalledTimes(1)
expect(spy.render).toBeCalledTimes(1)
expect(spy.initialize).toBeCalledTimes(1)
})

it('re-renders mermaid theme on html data-theme attribute change', async () => {
Expand Down
20 changes: 9 additions & 11 deletions src/mdxast-mermaid.ts
Expand Up @@ -5,7 +5,7 @@
* license file in the root directory of this source tree.
*/

import visit from 'unist-util-visit'
import { visit, EXIT } from 'unist-util-visit'
import { Literal, Parent, Node, Data } from 'unist'

import { Config } from './config.model'
Expand All @@ -21,18 +21,18 @@ type CodeMermaid = Literal<string> & {
* @param config Config passed in from parser.
* @returns Function to transform mdxast.
*/
function plugin (config?: Config) {
export default function plugin (config?: Config) {
/**
* Insert the component import into the document.
* @param ast The document to insert into.
*/
function insertImport (ast: Parent<Node<Data> | Literal, Data>) {
function insertImport (ast: any) {
// See if there is already an import for the Mermaid component
let importFound = false
visit(ast, { type: 'import' }, (node: Literal<string>) => {
if (/\s*import\s*{\s*Mermaid\s*}\s*from\s*'mdx-mermaid(\/lib)?\/Mermaid'\s*;?\s*/.test(node.value)) {
importFound = true
return visit.EXIT
return EXIT
}
})

Expand All @@ -45,16 +45,16 @@ function plugin (config?: Config) {
}
}

return async function transformer (ast: Parent<Node<Data> | Literal, Data>): Promise<Parent> {
return async function transformer (ast: any): Promise<Parent> {
// Find all the mermaid diagram code blocks. i.e. ```mermaid
const instances: [Literal, number, Parent<Node<Data> | Literal, Data>][] = []
visit<CodeMermaid>(ast, { type: 'code', lang: 'mermaid' }, (node, index, parent) => {
instances.push([node, index, parent as Parent<Node<Data>, Data>])
visit(ast, { type: 'code', lang: 'mermaid' }, (node: CodeMermaid, index, parent) => {
instances.push([node, index!, parent as Parent<Node<Data>, Data>])
})
// If there are no code blocks return
if (!instances.length) {
// Look for any components
visit<Literal<string> & { type: 'jsx' }>(ast, { type: 'jsx' }, (node, index, parent) => {
visit(ast, { type: 'jsx' }, (node: Literal<string> & { type: 'jsx' }, index, parent) => {
if (/.*<Mermaid.*/.test(node.value)) {
// If the component doesn't have config
if (typeof config !== 'undefined' && !/.*config={.*/.test(node.value)) {
Expand All @@ -64,7 +64,7 @@ function plugin (config?: Config) {
node.value.substring(index)
}
insertImport(ast)
return visit.EXIT
return EXIT
}
})
return ast
Expand Down Expand Up @@ -92,5 +92,3 @@ function plugin (config?: Config) {
return ast
}
}

export = plugin
5 changes: 3 additions & 2 deletions tsconfig.json
@@ -1,17 +1,18 @@
{
"compilerOptions": {
"declaration": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "react",
"lib": ["ESNext", "DOM"],
"module": "CommonJS",
"module": "ES2020",
"moduleResolution": "node",
"noImplicitAny": false,
"outDir": "lib",
"rootDir": "src",
"sourceMap": true,
"strict": true,
"target": "ES2015"
"target": "ES2020"
},
"include": [
"src/**/*.ts",
Expand Down
22 changes: 22 additions & 0 deletions yarn.lock
Expand Up @@ -6177,6 +6177,11 @@ unist-util-is@^4.0.0:
resolved "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz"
integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==

unist-util-is@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236"
integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==

unist-util-position@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47"
Expand Down Expand Up @@ -6211,6 +6216,14 @@ unist-util-visit-parents@^3.0.0:
"@types/unist" "^2.0.0"
unist-util-is "^4.0.0"

unist-util-visit-parents@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz#44bbc5d25f2411e7dfc5cecff12de43296aa8521"
integrity sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==
dependencies:
"@types/unist" "^2.0.0"
unist-util-is "^5.0.0"

unist-util-visit@2.0.3, unist-util-visit@^2.0.0:
version "2.0.3"
resolved "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz"
Expand All @@ -6220,6 +6233,15 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0:
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"

unist-util-visit@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.0.tgz#f41e407a9e94da31594e6b1c9811c51ab0b3d8f5"
integrity sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==
dependencies:
"@types/unist" "^2.0.0"
unist-util-is "^5.0.0"
unist-util-visit-parents "^5.0.0"

universalify@^0.1.0, universalify@^0.1.2:
version "0.1.2"
resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz"
Expand Down