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

feat: support mermaid code blocks in Markdown #7490

Merged
merged 33 commits into from Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
54ee20f
feat: add mermaid diagram support ootb #1258
sjwall May 24, 2022
a003697
feat(mdx-loader): mermaid rendering optional
sjwall May 26, 2022
ed1f044
refactor: move mermaid components to own package
sjwall May 27, 2022
ed1f21c
refactor: remove theme-classic mermaid dependency
sjwall May 27, 2022
777c086
refactor(mdx-loader): markdown config option
sjwall May 27, 2022
3a32f60
refactor(theme-mermaid): remove exports
sjwall May 27, 2022
11836a0
little refactors
Josh-Cena May 27, 2022
5d836f5
Merge branch 'main' into mermaid
Josh-Cena May 27, 2022
8c9e005
remove unused
Josh-Cena May 27, 2022
722f75b
remark plugin refactor
Josh-Cena May 29, 2022
c0a17ac
Merge branch 'main' into mermaid
Josh-Cena May 29, 2022
0a5e372
greatly simplify
Josh-Cena May 29, 2022
73f53a8
simplify API
Josh-Cena May 29, 2022
ec6f920
fix tests
Josh-Cena May 29, 2022
47bec9b
move dogfooding
Josh-Cena May 29, 2022
be53cf3
Merge branch 'main' into mermaid
sjwall Jun 6, 2022
addacc6
Merge branch 'main' into mermaid
Josh-Cena Jun 15, 2022
51b6c48
Merge branch 'main' into mermaid
slorber Oct 7, 2022
5ec7c0b
mermaid.mermaidOptions => mermaid.options
slorber Oct 13, 2022
25cb8aa
validate config.markdown.mermaid
slorber Oct 13, 2022
56ca9c7
do not spread markdown config to mdx loader: be more explicit with at…
slorber Oct 13, 2022
9772c2e
typo
slorber Oct 13, 2022
5d49364
temp better mermaid integration
slorber Oct 13, 2022
062ed20
expose mermaid hooks as client apis instead of themes
slorber Oct 13, 2022
4da56ed
fix snapshots
slorber Oct 14, 2022
b6c408d
good mermaid integration
slorber Oct 14, 2022
d5ca004
increase random count
slorber Oct 14, 2022
76c8703
add max-width 100% to container/svg
slorber Oct 14, 2022
c8d1e2c
try to fix journey bug
slorber Oct 14, 2022
bd67df1
fix bad project-words merge
slorber Oct 14, 2022
8c88c55
refactor MDXComponents usage
slorber Oct 14, 2022
57d481c
add mermaid in tabs dogfood, see https://github.com/sjwall/mdx-mermai…
slorber Oct 14, 2022
1377f4a
````mdx-code-block
slorber Oct 14, 2022
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
1 change: 1 addition & 0 deletions packages/docusaurus-mdx-loader/package.json
Expand Up @@ -39,6 +39,7 @@
"@docusaurus/types": "2.0.0-beta.20",
"@types/escape-html": "^1.0.2",
"@types/mdast": "^3.0.10",
"@types/mermaid": "^8.2.9",
"@types/stringify-object": "^3.3.1",
"@types/unist": "^2.0.6",
"remark": "^12.0.1",
Expand Down
30 changes: 17 additions & 13 deletions packages/docusaurus-mdx-loader/src/loader.ts
Expand Up @@ -22,7 +22,9 @@ import toc from './remark/toc';
import unwrapMdxCodeBlocks from './remark/unwrapMdxCodeBlocks';
import transformImage from './remark/transformImage';
import transformLinks from './remark/transformLinks';
import mermaid from './remark/mermaid';

import type {MarkdownConfig} from '@docusaurus/types';
import type {LoaderContext} from 'webpack';
import type {Processor, Plugin} from 'unified';

Expand Down Expand Up @@ -55,19 +57,20 @@ export type MDXOptions = {
beforeDefaultRehypePlugins: MDXPlugin[];
};

export type Options = Partial<MDXOptions> & {
staticDirs: string[];
siteDir: string;
isMDXPartial?: (filePath: string) => boolean;
isMDXPartialFrontMatterWarningDisabled?: boolean;
removeContentTitle?: boolean;
metadataPath?: string | ((filePath: string) => string);
createAssets?: (metadata: {
frontMatter: {[key: string]: unknown};
metadata: {[key: string]: unknown};
}) => {[key: string]: unknown};
filepath: string;
};
export type Options = Partial<MDXOptions> &
MarkdownConfig & {
staticDirs: string[];
siteDir: string;
isMDXPartial?: (filePath: string) => boolean;
isMDXPartialFrontMatterWarningDisabled?: boolean;
removeContentTitle?: boolean;
metadataPath?: string | ((filePath: string) => string);
createAssets?: (metadata: {
frontMatter: {[key: string]: unknown};
metadata: {[key: string]: unknown};
}) => {[key: string]: unknown};
filepath: string;
};

/**
* When this throws, it generally means that there's no metadata file associated
Expand Down Expand Up @@ -154,6 +157,7 @@ export async function mdxLoader(
remarkPlugins: [
...(reqOptions.beforeDefaultRemarkPlugins ?? []),
...DEFAULT_OPTIONS.remarkPlugins,
...(reqOptions.mermaid ? [mermaid] : []),
[
transformImage,
{
Expand Down
@@ -0,0 +1,62 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {createCompiler} from '@mdx-js/mdx';
import mermaid from '..';

describe('mermaid remark plugin', () => {
function createTestCompiler() {
return createCompiler({
remarkPlugins: [mermaid],
});
}

it('no mermaid', async () => {
const mdxCompiler = createTestCompiler();
const result = await mdxCompiler.process(
'# Heading 1\n\nNo Mermaid diagram :(',
);
expect(result.contents).toBe(
'\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;',
);
});

it('basic', async () => {
const mdxCompiler = createTestCompiler();
const result = await mdxCompiler.process(`# Heading 1\n
\`\`\`mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
\`\`\``);
expect(result.contents).toBe(`


const layoutProps = {
${' '}
};
const MDXLayout = "wrapper"
export default function MDXContent({
components,
...props
}) {
return <MDXLayout {...layoutProps} {...props} components={components} mdxType="MDXLayout">
<h1>{\`Heading 1\`}</h1>
<mermaid value={\`graph TD;
A-->B;
A-->C;
B-->D;
C-->D;\`} />
</MDXLayout>;
}

;
MDXContent.isMDXComponent = true;`);
});
});
48 changes: 48 additions & 0 deletions packages/docusaurus-mdx-loader/src/remark/mermaid/index.ts
@@ -0,0 +1,48 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import visit from 'unist-util-visit';
import type {Transformer} from 'unified';
import type {Data, Literal, Node, Parent} from 'unist';

type CodeMermaid = Literal<string> & {
type: 'code';
lang: 'mermaid';
};

function processMermaidNode(
node: CodeMermaid,
index: number,
parent: Parent<Node<Data> | Literal, Data>,
) {
parent.children.splice(index, 1, {
type: 'jsx',
value: `<mermaid value={\`${node.value}\`}/>`,
position: node.position,
});
}

export default function plugin(): Transformer {
return async (root) => {
// Find all the mermaid diagram code blocks. i.e. ```mermaid
const instances: [CodeMermaid, number, Parent<Node<Data>, Data>][] = [];
visit(
root,
{type: 'code', lang: 'mermaid'},
(node: CodeMermaid, index, parent) => {
if (parent) {
instances.push([node, index, parent]);
}
},
);

// Replace each Mermaid code block with the Mermaid component
instances.forEach(([node, index, parent]) => {
processMermaidNode(node, index, parent);
});
};
}
1 change: 1 addition & 0 deletions packages/docusaurus-plugin-content-blog/src/index.ts
Expand Up @@ -465,6 +465,7 @@ export default async function pluginContentBlog(
(author) => author.imageURL,
),
}),
...siteConfig.markdown,
},
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Expand Up @@ -379,6 +379,7 @@ export default async function pluginContentDocs(
}) => ({
image: frontMatter.image,
}),
...siteConfig.markdown,
},
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-plugin-content-pages/src/index.ts
Expand Up @@ -221,6 +221,7 @@ export default function pluginContentPages(
`${docuHash(aliasedSource)}.json`,
);
},
...siteConfig.markdown,
slorber marked this conversation as resolved.
Show resolved Hide resolved
},
},
{
Expand Down
3 changes: 3 additions & 0 deletions packages/docusaurus-theme-mermaid/.npmignore
@@ -0,0 +1,3 @@
.tsbuildinfo*
tsconfig*
__tests__
21 changes: 21 additions & 0 deletions packages/docusaurus-theme-mermaid/README.md
@@ -0,0 +1,21 @@
# Docusaurus Theme Mermaid

The mermaid components for Docusaurus.

## Installation

Add `docusaurus/theme-mermaid` to your package:

```bash
npm i @docusaurus/theme-mermaid
# or
yarn add @docusaurus/theme-mermaid
```

## Swizzling components

```bash
$ npm swizzle @docusaurus/theme-mermaid [component name]
```

All components used by this theme can be found [here](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-mermaid/src/theme)
47 changes: 47 additions & 0 deletions packages/docusaurus-theme-mermaid/package.json
@@ -0,0 +1,47 @@
{
"name": "@docusaurus/theme-mermaid",
"version": "2.0.0-beta.20",
"description": "Mermaid components for Docusaurus.",
"main": "lib/index.js",
"types": "src/theme-mermaid.d.ts",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/facebook/docusaurus.git",
"directory": "packages/docusaurus-theme-mermaid"
},
"license": "MIT",
"scripts": {
"build": "tsc --build && node ../../admin/scripts/copyUntypedFiles.js && prettier --config ../../.prettierrc --write \"lib/theme/**/*.js\"",
"watch": "run-p -c copy:watch build:watch",
"build:watch": "tsc --build --watch",
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.20",
"@docusaurus/theme-common": "2.0.0-beta.20",
"@docusaurus/theme-translations": "2.0.0-beta.20",
"@docusaurus/utils": "2.0.0-beta.20",
"@docusaurus/utils-common": "2.0.0-beta.20",
"@docusaurus/utils-validation": "2.0.0-beta.20",
"@mdx-js/react": "^1.6.22",
"mermaid": "^9.1.1",
"tslib": "^2.4.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.20",
"@docusaurus/types": "2.0.0-beta.20",
"@types/mdx-js__react": "^1.5.5",
"@types/mermaid": "^8.2.9",
"react-test-renderer": "^17.0.2"
},
"peerDependencies": {
"react": "^16.8.4 || ^17.0.0",
"react-dom": "^16.8.4 || ^17.0.0"
},
"engines": {
"node": ">=14"
}
}
@@ -0,0 +1,71 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {validateThemeConfig} from '../validateThemeConfig';
import type {Joi} from '@docusaurus/utils-validation';

function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
function validate(
schema: Joi.ObjectSchema<{[key: string]: unknown}>,
cfg: {[key: string]: unknown},
) {
const {value, error} = schema.validate(cfg, {
convert: false,
});
if (error) {
throw error;
}
return value;
}

return validateThemeConfig({validate, themeConfig});
}

describe('validateThemeConfig', () => {
it('undefined config', () => {
const mermaid = undefined;
expect(testValidateThemeConfig({mermaid})).toEqual({});
});

it('nonexistent config', () => {
expect(testValidateThemeConfig({})).toEqual({});
});

it('empty config', () => {
const mermaid = {};
expect(testValidateThemeConfig({mermaid})).toEqual({
mermaid: {},
});
});

it('theme', () => {
const mermaid = {
theme: {
light: 'light',
dark: 'dark',
},
};
expect(testValidateThemeConfig({mermaid})).toEqual({
mermaid: {
...mermaid,
},
});
});

it('config', () => {
const mermaid = {
config: {
fontFamily: 'Ariel',
},
};
expect(testValidateThemeConfig({mermaid})).toEqual({
mermaid: {
...mermaid,
},
});
});
});
35 changes: 35 additions & 0 deletions packages/docusaurus-theme-mermaid/src/index.ts
@@ -0,0 +1,35 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
import type {LoadContext, Plugin} from '@docusaurus/types';

export default function themeMermaid(context: LoadContext): Plugin<void> {
const {
i18n: {currentLocale},
} = context;

return {
name: 'docusaurus-theme-mermaid',

getThemePath() {
return '../lib/theme';
},
getTypeScriptThemePath() {
return '../src/theme';
},

getDefaultCodeTranslationMessages() {
return readDefaultCodeTranslationMessages({
locale: currentLocale,
name: 'theme-mermaid',
});
},
};
}

export {validateThemeConfig} from './validateThemeConfig';