mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
refactor(docs): use remark-compatible plugin
Change the `transformBlocks` function, which transforms a markdown str, and instead making it into a `transformMarkdownAst` function, which transforms a Markdown AST. This means we can use the remark/unifiedjs plugin infrastructure, see https://unifiedjs.com/learn/guide/create-a-plugin/
This commit is contained in:
parent
b26cdb3e46
commit
8f4caa4537
@ -37,7 +37,7 @@ import { JSDOM } from 'jsdom';
|
||||
import type { Code, Root } from 'mdast';
|
||||
import { posix, dirname, relative, join } from 'path';
|
||||
import prettier from 'prettier';
|
||||
import { remark as remarkBuilder } from 'remark';
|
||||
import { remark } from 'remark';
|
||||
import remarkFrontmatter from 'remark-frontmatter';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import chokidar from 'chokidar';
|
||||
@ -45,11 +45,6 @@ import mm from 'micromatch';
|
||||
// @ts-ignore No typescript declaration file
|
||||
import flatmap from 'unist-util-flatmap';
|
||||
|
||||
const remark = remarkBuilder()
|
||||
// support tables and other GitHub Flavored Markdown syntax in markdown
|
||||
.use(remarkGfm)
|
||||
.use(remarkFrontmatter, ['yaml']); // support YAML front-matter in Markdown
|
||||
|
||||
const MERMAID_MAJOR_VERSION = (
|
||||
JSON.parse(readFileSync('../mermaid/package.json', 'utf8')).version as string
|
||||
).split('.')[0];
|
||||
@ -205,47 +200,45 @@ const transformIncludeStatements = (file: string, text: string): string => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform code blocks in a Markdown file.
|
||||
* Use remark.parse() to turn the given content (a String) into an AST.
|
||||
* Remark plugin that transforms code blocks in a Markdown file.
|
||||
*
|
||||
* For any AST node that is a code block: transform it as needed:
|
||||
* - blocks marked as MERMAID_DIAGRAM_ONLY will be set to a 'mermaid' code block so it will be rendered as (only) a diagram
|
||||
* - blocks marked as MERMAID_EXAMPLE_KEYWORDS will be copied and the original node will be a code only block and the copy with be rendered as the diagram
|
||||
* - blocks marked as BLOCK_QUOTE_KEYWORDS will be transformed into block quotes
|
||||
*
|
||||
* Convert the AST back to a string and return it.
|
||||
*
|
||||
* @param content - the contents of a Markdown file
|
||||
* @returns the contents with transformed code blocks
|
||||
* @returns plugin function for Remark
|
||||
*/
|
||||
export const transformBlocks = (content: string): string => {
|
||||
const ast: Root = remark.parse(content);
|
||||
const astWithTransformedBlocks = flatmap(ast, (node: Code) => {
|
||||
if (node.type !== 'code' || !node.lang) {
|
||||
return [node]; // no transformation if this is not a code block
|
||||
}
|
||||
export function transformMarkdownAst() {
|
||||
return (tree: Root, _file?: any): Root => {
|
||||
const astWithTransformedBlocks = flatmap(tree, (node: Code) => {
|
||||
if (node.type !== 'code' || !node.lang) {
|
||||
return [node]; // no transformation if this is not a code block
|
||||
}
|
||||
|
||||
if (node.lang === MERMAID_DIAGRAM_ONLY) {
|
||||
// Set the lang to 'mermaid' so it will be rendered as a diagram.
|
||||
node.lang = MERMAID_KEYWORD;
|
||||
return [node];
|
||||
} else if (MERMAID_EXAMPLE_KEYWORDS.includes(node.lang)) {
|
||||
// Return 2 nodes:
|
||||
// 1. the original node with the language now set to 'mermaid-example' (will be rendered as code), and
|
||||
// 2. a copy of the original node with the language set to 'mermaid' (will be rendered as a diagram)
|
||||
node.lang = MERMAID_CODE_ONLY_KEYWORD;
|
||||
return [node, Object.assign({}, node, { lang: MERMAID_KEYWORD })];
|
||||
}
|
||||
if (node.lang === MERMAID_DIAGRAM_ONLY) {
|
||||
// Set the lang to 'mermaid' so it will be rendered as a diagram.
|
||||
node.lang = MERMAID_KEYWORD;
|
||||
return [node];
|
||||
} else if (MERMAID_EXAMPLE_KEYWORDS.includes(node.lang)) {
|
||||
// Return 2 nodes:
|
||||
// 1. the original node with the language now set to 'mermaid-example' (will be rendered as code), and
|
||||
// 2. a copy of the original node with the language set to 'mermaid' (will be rendered as a diagram)
|
||||
node.lang = MERMAID_CODE_ONLY_KEYWORD;
|
||||
return [node, Object.assign({}, node, { lang: MERMAID_KEYWORD })];
|
||||
}
|
||||
|
||||
// Transform these blocks into block quotes.
|
||||
if (BLOCK_QUOTE_KEYWORDS.includes(node.lang)) {
|
||||
return [remark.parse(transformToBlockQuote(node.value, node.lang, node.meta))];
|
||||
}
|
||||
// Transform these blocks into block quotes.
|
||||
if (BLOCK_QUOTE_KEYWORDS.includes(node.lang)) {
|
||||
return [remark.parse(transformToBlockQuote(node.value, node.lang, node.meta))];
|
||||
}
|
||||
|
||||
return [node]; // default is to do nothing to the node
|
||||
});
|
||||
return [node]; // default is to do nothing to the node
|
||||
});
|
||||
|
||||
return remark.stringify(astWithTransformedBlocks);
|
||||
};
|
||||
return astWithTransformedBlocks;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a markdown file and write the transformed file to the directory for published
|
||||
@ -263,7 +256,13 @@ export const transformBlocks = (content: string): string => {
|
||||
*/
|
||||
const transformMarkdown = (file: string) => {
|
||||
const doc = injectPlaceholders(transformIncludeStatements(file, readSyncedUTF8file(file)));
|
||||
let transformed = transformBlocks(doc);
|
||||
|
||||
let transformed = remark()
|
||||
.use(remarkGfm)
|
||||
.use(remarkFrontmatter, ['yaml']) // support YAML front-matter in Markdown
|
||||
.use(transformMarkdownAst) // mermaid project specific plugin
|
||||
.processSync(doc).toString();
|
||||
|
||||
if (!noHeader) {
|
||||
// Add the header to the start of the file
|
||||
transformed = `${generateHeader(file)}\n${transformed}`;
|
||||
|
@ -1,40 +1,20 @@
|
||||
import { transformBlocks, transformToBlockQuote } from './docs.mjs';
|
||||
import { transformMarkdownAst, transformToBlockQuote } from './docs.mjs';
|
||||
|
||||
import { remark as remarkBuilder } from 'remark'; // import it this way so we can mock it
|
||||
import { vi, afterEach, describe, it, expect } from 'vitest';
|
||||
|
||||
const remark = remarkBuilder();
|
||||
|
||||
vi.mock('remark', async (importOriginal) => {
|
||||
const { remark: originalRemarkBuilder } = (await importOriginal()) as {
|
||||
remark: typeof remarkBuilder;
|
||||
};
|
||||
|
||||
// make sure that both `docs.mts` and this test file are using the same remark
|
||||
// object so that we can mock it
|
||||
const sharedRemark = originalRemarkBuilder();
|
||||
return {
|
||||
remark: () => sharedRemark,
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('docs.mts', () => {
|
||||
describe('transformBlocks', () => {
|
||||
it('uses remark.parse to create the AST for the file ', () => {
|
||||
const remarkParseSpy = vi
|
||||
.spyOn(remark, 'parse')
|
||||
.mockReturnValue({ type: 'root', children: [] });
|
||||
const contents = 'Markdown file contents';
|
||||
transformBlocks(contents);
|
||||
expect(remarkParseSpy).toHaveBeenCalledWith(contents);
|
||||
});
|
||||
describe('transformMarkdownAst', () => {
|
||||
describe('checks each AST node', () => {
|
||||
it('does no transformation if there are no code blocks', async () => {
|
||||
const contents = 'Markdown file contents\n';
|
||||
const result = transformBlocks(contents);
|
||||
const result = (
|
||||
await remarkBuilder().use(transformMarkdownAst).process(contents)
|
||||
).toString();
|
||||
expect(result).toEqual(contents);
|
||||
});
|
||||
|
||||
@ -46,8 +26,10 @@ describe('docs.mts', () => {
|
||||
const lang_keyword = 'mermaid-nocode';
|
||||
const contents = beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
|
||||
|
||||
it('changes the language to "mermaid"', () => {
|
||||
const result = transformBlocks(contents);
|
||||
it('changes the language to "mermaid"', async () => {
|
||||
const result = (
|
||||
await remarkBuilder().use(transformMarkdownAst).process(contents)
|
||||
).toString();
|
||||
expect(result).toEqual(
|
||||
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagram_text + '\n```\n'
|
||||
);
|
||||
@ -61,8 +43,10 @@ describe('docs.mts', () => {
|
||||
const contents =
|
||||
beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
|
||||
|
||||
it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', () => {
|
||||
const result = transformBlocks(contents);
|
||||
it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', async () => {
|
||||
const result = (
|
||||
await remarkBuilder().use(transformMarkdownAst).process(contents)
|
||||
).toString();
|
||||
expect(result).toEqual(
|
||||
beforeCodeLine +
|
||||
'\n' +
|
||||
@ -77,12 +61,14 @@ describe('docs.mts', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('calls transformToBlockQuote with the node information', () => {
|
||||
it('calls transformToBlockQuote with the node information', async () => {
|
||||
const lang_keyword = 'note';
|
||||
const contents =
|
||||
beforeCodeLine + '```' + lang_keyword + '\n' + 'This is the text\n' + '```\n';
|
||||
|
||||
const result = transformBlocks(contents);
|
||||
const result = (
|
||||
await remarkBuilder().use(transformMarkdownAst).process(contents)
|
||||
).toString();
|
||||
expect(result).toEqual(beforeCodeLine + '\n> **Note**\n' + '> This is the text\n');
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user