diff --git a/.esbuild/docs.ts b/.esbuild/docs.ts deleted file mode 100644 index 61e362346..000000000 --- a/.esbuild/docs.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { execFileSync } from 'child_process'; -import { build } from 'esbuild'; -import { rm } from 'fs/promises'; -import { generateLangium } from '../.build/generateLangium.js'; -import type { MermaidBuildOptions } from './util.js'; -import { defaultOptions, getBuildConfig } from './util.js'; - -const buildDocs = async () => { - const option: MermaidBuildOptions = { - ...defaultOptions, - options: { - file: 'rendering-util/rendering-elements/shapes.cli.ts', - name: 'mermaid-shapes', - packageName: 'mermaid', - }, - } as const; - - await build({ ...getBuildConfig(option), splitting: false, sourcemap: false }); -}; - -const handler = (e) => { - // eslint-disable-next-line no-console - console.error(e); - process.exit(1); -}; - -const main = async () => { - await generateLangium(); - await buildDocs().catch(handler); - execFileSync('node', ['packages/mermaid/dist/mermaid-shapes.esm.mjs']); - await rm('packages/mermaid/dist/mermaid-shapes.esm.mjs'); -}; - -void main(); diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index dfd20d232..8f8b54e21 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -316,8 +316,6 @@ This syntax creates a node A as a rectangle. It renders in the same way as `A["A Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases: - - | **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** | | --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- | | Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` | diff --git a/packages/mermaid/.gitignore b/packages/mermaid/.gitignore index ff0a6cc9e..91c74fe7d 100644 --- a/packages/mermaid/.gitignore +++ b/packages/mermaid/.gitignore @@ -4,4 +4,3 @@ README.* src/docs/public/user-avatars/ src/docs/.vitepress/cache src/docs/.vitepress/components.d.ts -src/docs/syntax/shapesTable.md diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 351ace75c..afee6aab5 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -34,9 +34,8 @@ "scripts": { "clean": "rimraf dist", "dev": "pnpm -w dev", - "docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaid.ts && prettier --write ./src/docs/config/setup && pnpm docs:shapes", + "docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaid.ts && prettier --write ./src/docs/config/setup", "docs:build": "rimraf ../../docs && pnpm docs:code && pnpm docs:spellcheck && tsx scripts/docs.cli.mts", - "docs:shapes": "cd ../.. && tsx .esbuild/docs.ts", "docs:verify": "pnpm docs:code && pnpm docs:spellcheck && tsx scripts/docs.cli.mts --verify", "docs:pre:vitepress": "pnpm --filter ./src/docs prefetch && rimraf src/vitepress && pnpm docs:code && tsx scripts/docs.cli.mts --vitepress && pnpm --filter ./src/vitepress install --no-frozen-lockfile --ignore-scripts", "docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing", diff --git a/packages/mermaid/scripts/docs.mts b/packages/mermaid/scripts/docs.mts index 374e78870..073a3c1a9 100644 --- a/packages/mermaid/scripts/docs.mts +++ b/packages/mermaid/scripts/docs.mts @@ -41,7 +41,8 @@ import { exec } from 'child_process'; import { globby } from 'globby'; import { JSDOM } from 'jsdom'; import { dump, load, JSON_SCHEMA } from 'js-yaml'; -import type { Code, ListItem, Root, Text, YAML } from 'mdast'; +import type { Code, ListItem, PhrasingContent, Root, Text, YAML } from 'mdast'; +import { register } from 'node:module'; import { posix, dirname, relative, join } from 'path'; import prettier from 'prettier'; import { remark } from 'remark'; @@ -53,6 +54,10 @@ import mm from 'micromatch'; import flatmap from 'unist-util-flatmap'; import { visit } from 'unist-util-visit'; +// short-circuit `.schema.yaml` imports, so that we can safely import `shapes.js` +register('./loadHook.mjs', import.meta.url); +const { shapesDefs } = await import('../src/rendering-util/rendering-elements/shapes.js'); + export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8')) .version as string; const MERMAID_MAJOR_VERSION = MERMAID_RELEASE_VERSION.split('.')[0]; @@ -103,6 +108,60 @@ const generateHeader = (file: string): string => { > ## Please edit the corresponding file in [${filePathFromRoot}](${sourcePathRelativeToGenerated}).`; }; +/** + * Builds a markdown list of shapes supported in flowcharts. + */ +export function buildShapeDoc() { + const data = shapesDefs + .sort((a, b) => a.semanticName.localeCompare(b.semanticName)) + .map((shape): PhrasingContent[][] => { + const { name, semanticName, description, shortName, aliases = [] } = shape; + return [ + [{ type: 'text', value: semanticName }], + [{ type: 'text', value: name }], + [{ type: 'inlineCode', value: shortName }], + [{ type: 'text', value: description }], + aliases.sort().flatMap((alias, index) => [ + ...(index !== 0 ? ([{ type: 'text', value: ', ' }] as const) : []), + { + type: 'inlineCode', + value: alias, + }, + ]), + ]; + }); + + // don't prettify this table, since we'd do it later + return remark() + .use(remarkGfm) + .stringify({ + type: 'root', + children: [ + { + type: 'table', + children: [ + ['Semantic Name', 'Shape Name', 'Short Name', 'Description', 'Alias Supported'].map( + (s): PhrasingContent[] => [ + { + type: 'strong', + children: [{ type: 'text', value: s }], + }, + ] + ), + ...data, + ].map((row) => ({ + type: 'tableRow', + children: row.map((cell) => ({ + type: 'tableCell', + children: cell, + })), + })), + }, + ], + }) + .toString(); +} + /** * Given a source file name and path, return the documentation destination full path and file name * Create the destination path if it does not already exist. @@ -192,10 +251,22 @@ export const transformToBlockQuote = ( const injectPlaceholders = (text: string): string => text.replace(//g, MERMAID_MAJOR_VERSION).replace(//g, CDN_URL); +const virtualGenerators: Record string> = { + shapesTable: buildShapeDoc, +}; + const transformIncludeStatements = (file: string, text: string): string => { // resolve includes - src https://github.com/vuejs/vitepress/blob/428eec3750d6b5648a77ac52d88128df0554d4d1/src/node/markdownToVue.ts#L65-L76 - return text.replace(includesRE, (m, m1) => { + return text.replace(includesRE, (m, m1: string) => { try { + if (m1.startsWith('virtual:')) { + const key = m1.replace('virtual:', ''); + const generator = virtualGenerators[key]; + if (!generator) { + throw new Error(`Unknown virtual generator: ${key} in "${file}"`); + } + return generator(); + } const includePath = join(dirname(file), m1).replaceAll('\\', '/'); const content = readSyncedUTF8file(includePath); includedFiles.add(changeToFinalDocDir(includePath)); diff --git a/packages/mermaid/scripts/docs.spec.ts b/packages/mermaid/scripts/docs.spec.ts index c84bc1bac..68677d4c9 100644 --- a/packages/mermaid/scripts/docs.spec.ts +++ b/packages/mermaid/scripts/docs.spec.ts @@ -1,4 +1,4 @@ -import { transformMarkdownAst, transformToBlockQuote } from './docs.mjs'; +import { buildShapeDoc, transformMarkdownAst, transformToBlockQuote } from './docs.mjs'; import { remark } from 'remark'; // import it this way so we can mock it import remarkFrontmatter from 'remark-frontmatter'; @@ -165,4 +165,59 @@ This Markdown should be kept. }); }); }); + + describe('buildShapeDoc', () => { + it('should build shapesTable based on the shapeDefs', () => { + expect(buildShapeDoc()).toMatchInlineSnapshot(` + "| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** | + | --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- | + | Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` | + | Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` | + | Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` | + | Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` | + | Comment Right | Curly Brace | \`brace-r\` | Adds a comment | | + | Comment with braces on both sides | Curly Braces | \`braces\` | Adds a comment | | + | Data Input/Output | Lean Right | \`lean-r\` | Represents input or output | \`in-out\`, \`lean-right\` | + | Data Input/Output | Lean Left | \`lean-l\` | Represents output or input | \`lean-left\`, \`out-in\` | + | Database | Cylinder | \`cyl\` | Database storage | \`cylinder\`, \`database\`, \`db\` | + | Decision | Diamond | \`diam\` | Decision-making step | \`decision\`, \`diamond\`, \`question\` | + | Delay | Half-Rounded Rectangle | \`delay\` | Represents a delay | \`half-rounded-rectangle\` | + | Direct Access Storage | Horizontal Cylinder | \`h-cyl\` | Direct access storage | \`das\`, \`horizontal-cylinder\` | + | Disk Storage | Lined Cylinder | \`lin-cyl\` | Disk storage | \`disk\`, \`lined-cylinder\` | + | Display | Curved Trapezoid | \`curv-trap\` | Represents a display | \`curved-trapezoid\`, \`display\` | + | Divided Process | Divided Rectangle | \`div-rect\` | Divided process shape | \`div-proc\`, \`divided-process\`, \`divided-rectangle\` | + | Document | Document | \`doc\` | Represents a document | \`doc\`, \`document\` | + | Event | Rounded Rectangle | \`rounded\` | Represents an event | \`event\` | + | Extract | Triangle | \`tri\` | Extraction process | \`extract\`, \`triangle\` | + | Fork/Join | Filled Rectangle | \`fork\` | Fork or join in process flow | \`join\` | + | Internal Storage | Window Pane | \`win-pane\` | Internal storage | \`internal-storage\`, \`window-pane\` | + | Junction | Filled Circle | \`f-circ\` | Junction point | \`filled-circle\`, \`junction\` | + | Lined Document | Lined Document | \`lin-doc\` | Lined document | \`lined-document\` | + | Lined/Shaded Process | Lined Rectangle | \`lin-rect\` | Lined process shape | \`lin-proc\`, \`lined-process\`, \`lined-rectangle\`, \`shaded-process\` | + | Loop Limit | Trapezoidal Pentagon | \`notch-pent\` | Loop limit step | \`loop-limit\`, \`notched-pentagon\` | + | Manual File | Flipped Triangle | \`flip-tri\` | Manual file operation | \`flipped-triangle\`, \`manual-file\` | + | Manual Input | Sloped Rectangle | \`sl-rect\` | Manual input step | \`manual-input\`, \`sloped-rectangle\` | + | Manual Operation | Trapezoid Base Top | \`trap-t\` | Represents a manual task | \`inv-trapezoid\`, \`manual\`, \`trapezoid-top\` | + | Multi-Document | Stacked Document | \`docs\` | Multiple documents | \`documents\`, \`st-doc\`, \`stacked-document\` | + | Multi-Process | Stacked Rectangle | \`st-rect\` | Multiple processes | \`processes\`, \`procs\`, \`stacked-rectangle\` | + | Odd | Odd | \`odd\` | Odd shape | | + | Paper Tape | Flag | \`flag\` | Paper tape | \`paper-tape\` | + | Prepare Conditional | Hexagon | \`hex\` | Preparation or condition step | \`hexagon\`, \`prepare\` | + | Priority Action | Trapezoid Base Bottom | \`trap-b\` | Priority action | \`priority\`, \`trapezoid\`, \`trapezoid-bottom\` | + | Process | Rectangle | \`rect\` | Standard process shape | \`proc\`, \`process\`, \`rectangle\` | + | Start | Circle | \`circle\` | Starting point | \`circ\` | + | Start | Small Circle | \`sm-circ\` | Small starting point | \`small-circle\`, \`start\` | + | Stop | Double Circle | \`dbl-circ\` | Represents a stop point | \`double-circle\` | + | Stop | Framed Circle | \`fr-circ\` | Stop point | \`framed-circle\`, \`stop\` | + | Stored Data | Bow Tie Rectangle | \`bow-rect\` | Stored data | \`bow-tie-rectangle\`, \`stored-data\` | + | Subprocess | Framed Rectangle | \`fr-rect\` | Subprocess | \`framed-rectangle\`, \`subproc\`, \`subprocess\`, \`subroutine\` | + | Summary | Crossed Circle | \`cross-circ\` | Summary | \`crossed-circle\`, \`summary\` | + | Tagged Document | Tagged Document | \`tag-doc\` | Tagged document | \`tag-doc\`, \`tagged-document\` | + | Tagged Process | Tagged Rectangle | \`tag-rect\` | Tagged process | \`tag-proc\`, \`tagged-process\`, \`tagged-rectangle\` | + | Terminal Point | Stadium | \`stadium\` | Terminal point | \`pill\`, \`terminal\` | + | Text Block | Text Block | \`text\` | Text block | | + " + `); + }); + }); }); diff --git a/packages/mermaid/scripts/loadHook.mjs b/packages/mermaid/scripts/loadHook.mjs new file mode 100644 index 000000000..50129861b --- /dev/null +++ b/packages/mermaid/scripts/loadHook.mjs @@ -0,0 +1,22 @@ +import { fileURLToPath } from 'node:url'; +/** @import import { LoadHook } from "node:module"; */ +/** + * @type {LoadHook} + * + * Load hook that short circuits the loading of `.schema.yaml` files with `export default {}`. + * These would normally be loaded using ESBuild, but that doesn't work for these local scripts. + * + * @see https://nodejs.org/api/module.html#loadurl-context-nextload + */ +export const load = async (url, context, nextLoad) => { + const filePath = url.startsWith('file://') ? fileURLToPath(url) : url; + if (filePath.endsWith('.schema.yaml')) { + return { + format: 'module', + shortCircuit: true, + source: `export default {}`, + }; + } else { + return await nextLoad(url, context); + } +}; diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index dd923f84a..86347bd80 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -212,8 +212,7 @@ This syntax creates a node A as a rectangle. It renders in the same way as `A["A Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases: - - + ### Example Flowchart with New Shapes diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.cli.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.cli.ts deleted file mode 100644 index 098b3f179..000000000 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes.cli.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { writeFile } from 'fs/promises'; -import { buildShapeDoc } from './shapesDoc.js'; - -export const writeShapeDoc = async () => { - await writeFile('packages/mermaid/src/docs/syntax/shapesTable.md', buildShapeDoc()); -}; - -void writeShapeDoc(); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.spec.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.spec.ts deleted file mode 100644 index 9df052e80..000000000 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, it } from 'vitest'; -import { buildShapeDoc } from './shapesDoc.js'; - -describe('build shapesTable', () => { - it('should build shapesTable based on the shapeDefs', () => { - expect(buildShapeDoc()).toMatchInlineSnapshot(` - "| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** | - | --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- | - | Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` | - | Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` | - | Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` | - | Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` | - | Comment Right | Curly Brace | \`brace-r\` | Adds a comment | | - | Comment with braces on both sides | Curly Braces | \`braces\` | Adds a comment | | - | Data Input/Output | Lean Right | \`lean-r\` | Represents input or output | \`in-out\`, \`lean-right\` | - | Data Input/Output | Lean Left | \`lean-l\` | Represents output or input | \`lean-left\`, \`out-in\` | - | Database | Cylinder | \`cyl\` | Database storage | \`cylinder\`, \`database\`, \`db\` | - | Decision | Diamond | \`diam\` | Decision-making step | \`decision\`, \`diamond\`, \`question\` | - | Delay | Half-Rounded Rectangle | \`delay\` | Represents a delay | \`half-rounded-rectangle\` | - | Direct Access Storage | Horizontal Cylinder | \`h-cyl\` | Direct access storage | \`das\`, \`horizontal-cylinder\` | - | Disk Storage | Lined Cylinder | \`lin-cyl\` | Disk storage | \`disk\`, \`lined-cylinder\` | - | Display | Curved Trapezoid | \`curv-trap\` | Represents a display | \`curved-trapezoid\`, \`display\` | - | Divided Process | Divided Rectangle | \`div-rect\` | Divided process shape | \`div-proc\`, \`divided-process\`, \`divided-rectangle\` | - | Document | Document | \`doc\` | Represents a document | \`doc\`, \`document\` | - | Event | Rounded Rectangle | \`rounded\` | Represents an event | \`event\` | - | Extract | Triangle | \`tri\` | Extraction process | \`extract\`, \`triangle\` | - | Fork/Join | Filled Rectangle | \`fork\` | Fork or join in process flow | \`join\` | - | Internal Storage | Window Pane | \`win-pane\` | Internal storage | \`internal-storage\`, \`window-pane\` | - | Junction | Filled Circle | \`f-circ\` | Junction point | \`filled-circle\`, \`junction\` | - | Lined Document | Lined Document | \`lin-doc\` | Lined document | \`lined-document\` | - | Lined/Shaded Process | Lined Rectangle | \`lin-rect\` | Lined process shape | \`lin-proc\`, \`lined-process\`, \`lined-rectangle\`, \`shaded-process\` | - | Loop Limit | Trapezoidal Pentagon | \`notch-pent\` | Loop limit step | \`loop-limit\`, \`notched-pentagon\` | - | Manual File | Flipped Triangle | \`flip-tri\` | Manual file operation | \`flipped-triangle\`, \`manual-file\` | - | Manual Input | Sloped Rectangle | \`sl-rect\` | Manual input step | \`manual-input\`, \`sloped-rectangle\` | - | Manual Operation | Trapezoid Base Top | \`trap-t\` | Represents a manual task | \`inv-trapezoid\`, \`manual\`, \`trapezoid-top\` | - | Multi-Document | Stacked Document | \`docs\` | Multiple documents | \`documents\`, \`st-doc\`, \`stacked-document\` | - | Multi-Process | Stacked Rectangle | \`st-rect\` | Multiple processes | \`processes\`, \`procs\`, \`stacked-rectangle\` | - | Odd | Odd | \`odd\` | Odd shape | | - | Paper Tape | Flag | \`flag\` | Paper tape | \`paper-tape\` | - | Prepare Conditional | Hexagon | \`hex\` | Preparation or condition step | \`hexagon\`, \`prepare\` | - | Priority Action | Trapezoid Base Bottom | \`trap-b\` | Priority action | \`priority\`, \`trapezoid\`, \`trapezoid-bottom\` | - | Process | Rectangle | \`rect\` | Standard process shape | \`proc\`, \`process\`, \`rectangle\` | - | Start | Circle | \`circle\` | Starting point | \`circ\` | - | Start | Small Circle | \`sm-circ\` | Small starting point | \`small-circle\`, \`start\` | - | Stop | Double Circle | \`dbl-circ\` | Represents a stop point | \`double-circle\` | - | Stop | Framed Circle | \`fr-circ\` | Stop point | \`framed-circle\`, \`stop\` | - | Stored Data | Bow Tie Rectangle | \`bow-rect\` | Stored data | \`bow-tie-rectangle\`, \`stored-data\` | - | Subprocess | Framed Rectangle | \`fr-rect\` | Subprocess | \`framed-rectangle\`, \`subproc\`, \`subprocess\`, \`subroutine\` | - | Summary | Crossed Circle | \`cross-circ\` | Summary | \`crossed-circle\`, \`summary\` | - | Tagged Document | Tagged Document | \`tag-doc\` | Tagged document | \`tag-doc\`, \`tagged-document\` | - | Tagged Process | Tagged Rectangle | \`tag-rect\` | Tagged process | \`tag-proc\`, \`tagged-process\`, \`tagged-rectangle\` | - | Terminal Point | Stadium | \`stadium\` | Terminal point | \`pill\`, \`terminal\` | - | Text Block | Text Block | \`text\` | Text block | |" - `); - }); -}); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapesDoc.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapesDoc.ts deleted file mode 100644 index 4540c9725..000000000 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapesDoc.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { writeFile } from 'fs/promises'; -import { markdownTable } from 'markdown-table'; -import { shapesDefs } from './shapes.js'; - -export const buildShapeDoc = () => { - const data = shapesDefs.map((shape) => { - const { name, semanticName, description, shortName, aliases } = shape; - aliases?.sort(); - const aliasString = aliases?.join('`, `'); - return [ - semanticName, - name, - `\`${shortName}\``, - description, - aliasString ? `\`${aliasString}\`` : '', - ]; - }); - data.sort((a, b) => a[0].localeCompare(b[0])); - return markdownTable([ - ['Semantic Name', 'Shape Name', 'Short Name', 'Description', 'Alias Supported'].map( - (s) => `**${s}**` - ), - ...data, - ]); -}; - -export const writeShapeDoc = async () => { - await writeFile('packages/mermaid/src/docs/syntax/shapesTable.md', buildShapeDoc()); -};