diff --git a/.gitignore b/.gitignore index 8cc09354b..f29286825 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ tsconfig.tsbuildinfo knsv*.html local*.html +stats/ diff --git a/.vite/build.ts b/.vite/build.ts index 50b7fb1ad..f2dd7db05 100644 --- a/.vite/build.ts +++ b/.vite/build.ts @@ -1,9 +1,12 @@ -import { build, InlineConfig } from 'vite'; +import { build, InlineConfig, type PluginOption } from 'vite'; import { resolve } from 'path'; import { fileURLToPath } from 'url'; import jisonPlugin from './jisonPlugin.js'; import { readFileSync } from 'fs'; +import { visualizer } from 'rollup-plugin-visualizer'; +import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js'; +const visualize = process.argv.includes('--visualize'); const watch = process.argv.includes('--watch'); const mermaidOnly = process.argv.includes('--mermaid'); const __dirname = fileURLToPath(new URL('.', import.meta.url)); @@ -13,6 +16,20 @@ type OutputOptions = Exclude< undefined >['output']; +const visualizerOptions = (packageName: string, core = false): PluginOption[] => { + if (packageName !== 'mermaid' || !visualize) { + return []; + } + return ['network', 'treemap', 'sunburst'].map((chartType) => + visualizer({ + filename: `./stats/${chartType}${core ? '.core' : ''}.html`, + template: chartType as TemplateType, + gzipSize: true, + brotliSize: true, + }) + ); +}; + const packageOptions = { mermaid: { name: 'mermaid', @@ -95,7 +112,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions) resolve: { extensions: ['.jison', '.js', '.ts', '.json'], }, - plugins: [jisonPlugin()], + plugins: [jisonPlugin(), ...visualizerOptions(packageName, core)], }; if (watch && config.build) { @@ -121,7 +138,7 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => { const main = async () => { const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[]; - for (const pkg of packageNames) { + for (const pkg of packageNames.filter((pkg) => !mermaidOnly || pkg === 'mermaid')) { await buildPackage(pkg); } }; @@ -132,6 +149,9 @@ if (watch) { build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' })); // build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' })); } +} else if (visualize) { + await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' })); + await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' })); } else { void main(); } diff --git a/cSpell.json b/cSpell.json index 08fce1d1c..03891165f 100644 --- a/cSpell.json +++ b/cSpell.json @@ -14,6 +14,7 @@ "bilkent", "bisheng", "brolin", + "brotli", "codedoc", "colour", "cpettitt", @@ -70,6 +71,7 @@ "substate", "sveidqvist", "techn", + "treemap", "ts-nocheck", "tuleap", "verdana", @@ -116,5 +118,8 @@ "Multi-line code blocks", "HTML Tags" ], - "ignorePaths": ["packages/mermaid/src/docs/CHANGELOG.md"] + "ignorePaths": [ + "packages/mermaid/src/docs/CHANGELOG.md", + "packages/mermaid/src/docs/.vitepress/redirect.ts" + ] } diff --git a/package.json b/package.json index 70fac0ea9..783a6ca45 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,9 @@ "git graph" ], "scripts": { - "build:mermaid": "ts-node-esm --transpileOnly .vite/build.ts --mermaid", "build:vite": "ts-node-esm --transpileOnly .vite/build.ts", + "build:mermaid": "pnpm build:vite --mermaid", + "build:viz": "pnpm build:mermaid --visualize", "build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-mindmap/tsconfig.json --emitDeclarationOnly", "build:watch": "pnpm build:vite --watch", "build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"", @@ -64,6 +65,7 @@ "@types/mdast": "^3.0.10", "@types/node": "^18.11.9", "@types/prettier": "^2.7.1", + "@types/rollup-plugin-visualizer": "^4.2.1", "@typescript-eslint/eslint-plugin": "^5.42.1", "@typescript-eslint/parser": "^5.42.1", "@vitest/coverage-c8": "^0.25.1", @@ -96,6 +98,7 @@ "prettier": "^2.7.1", "prettier-plugin-jsdoc": "^0.4.2", "rimraf": "^3.0.2", + "rollup-plugin-visualizer": "^5.8.3", "start-server-and-test": "^1.14.0", "ts-node": "^10.9.1", "typescript": "^4.8.4", diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index f79ffe6f3..0b4c7aad1 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -90,7 +90,6 @@ "prettier": "^2.7.1", "remark": "^14.0.2", "rimraf": "^3.0.2", - "shiki": "^0.11.1", "start-server-and-test": "^1.14.0", "typedoc": "^0.23.18", "typedoc-plugin-markdown": "^3.13.6", diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 0dc437721..ed4373514 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -752,7 +752,7 @@ export const insertSequenceNumber = function (elem) { // .style("fill", '#f00'); }; /** - * Setup arrow head and define the marker. The result is appended to the svg. + * Setup cross head and define the marker. The result is appended to the svg. * * @param {any} elem */ @@ -764,26 +764,16 @@ export const insertArrowCrossHead = function (elem) { .attr('markerWidth', 15) .attr('markerHeight', 8) .attr('orient', 'auto') - .attr('refX', 16) - .attr('refY', 4); - - // The arrow - marker - .append('path') - .attr('fill', 'black') - .attr('stroke', '#000000') - .style('stroke-dasharray', '0, 0') - .attr('stroke-width', '1px') - .attr('d', 'M 9,2 V 6 L16,4 Z'); - + .attr('refX', 4) + .attr('refY', 5); // The cross marker .append('path') .attr('fill', 'none') .attr('stroke', '#000000') .style('stroke-dasharray', '0, 0') - .attr('stroke-width', '1px') - .attr('d', 'M 0,1 L 6,7 M 6,1 L 0,7'); + .attr('stroke-width', '1pt') + .attr('d', 'M 1,2 L 6,7 M 6,2 L 1,7'); // this is actual shape for arrowhead }; diff --git a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js index 3c734ff7d..75ecb4b13 100644 --- a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js +++ b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js @@ -184,6 +184,27 @@ describe('ClassDefs and classes when parsing a State diagram', () => { }); }); }); + + describe('comments parsing', () => { + it('working inside states', function () { + let diagram = ''; + diagram += 'stateDiagram-v2\n\n'; + diagram += '[*] --> Moving\n'; + diagram += 'Moving --> Still\n'; + diagram += 'Moving --> Crash\n'; + diagram += 'state Moving {\n'; + diagram += '%% comment inside state\n'; + diagram += 'slow --> fast\n'; + diagram += '}\n'; + + stateDiagram.parser.parse(diagram); + stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); + + const states = stateDiagram.parser.yy.getStates(); + + expect(states['Moving'].doc.length).toEqual(1); + }); + }); }); }); }); diff --git a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison index 1115edfe9..67073210b 100644 --- a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison +++ b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison @@ -109,6 +109,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili [^\n\s\{]+ {/*console.log('COMPOSIT_STATE', yytext);*/return 'COMPOSIT_STATE';} \n {this.popState();} \{ {this.popState();this.pushState('struct'); /*console.log('begin struct', yytext);*/return 'STRUCT_START';} +\%\%(?!\{)[^\n]* /* skip comments inside state*/ \} { /*console.log('Ending struct');*/ this.popState(); return 'STRUCT_STOP';}} [\n] /* nothing */ diff --git a/packages/mermaid/src/docs.mts b/packages/mermaid/src/docs.mts index e2f46ff1e..aa57a3c01 100644 --- a/packages/mermaid/src/docs.mts +++ b/packages/mermaid/src/docs.mts @@ -245,7 +245,7 @@ const transformHtml = (filename: string) => { }; const getGlobs = (globs: string[]): string[] => { - globs.push('!**/dist'); + globs.push('!**/dist', '!**/redirect.spec.ts'); if (!vitepress) { globs.push('!**/.vitepress', '!**/vite.config.ts', '!src/docs/index.md'); } diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 7d3ec40dd..1417bca72 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -4,6 +4,8 @@ import { MermaidMarkdown } from 'vitepress-plugin-mermaid'; import { defineConfig, MarkdownOptions } from 'vitepress'; const allMarkdownTransformers: MarkdownOptions = { + // the shiki theme to highlight code blocks + theme: 'github-dark', config: async (md) => { await MermaidExample(md); MermaidMarkdown(md); @@ -35,7 +37,7 @@ function nav() { { text: 'Intro', link: '/intro/', activeMatch: '/intro/' }, { text: 'Configuration', - link: '/config/Tutorials', + link: '/config/configuration', activeMatch: '/config/', }, { text: 'Syntax', link: '/syntax/classDiagram', activeMatch: '/syntax/' }, @@ -118,6 +120,7 @@ function sidebarConfig() { text: '⚙️ Deployment and Configuration', collapsible: true, items: [ + { text: 'Configuration', link: '/config/configuration' }, { text: 'Tutorials', link: '/config/Tutorials' }, { text: 'API-Usage', link: '/config/usage' }, { text: 'Mermaid API Configuration', link: '/config/setup/README' }, @@ -126,7 +129,6 @@ function sidebarConfig() { { text: 'Accessibility', link: '/config/accessibility' }, { text: 'Mermaid CLI', link: '/config/mermaidCLI' }, { text: 'Advanced usage', link: '/config/n00b-advanced' }, - { text: 'Configuration', link: '/config/configuration' }, ], }, ]; diff --git a/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts b/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts index db9b764bc..3a9521375 100644 --- a/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts +++ b/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts @@ -1,8 +1,5 @@ import type { MarkdownRenderer } from 'vitepress'; -// Note: using "import shiki from 'shiki' and then "const highlighter = await shiki.getHighlighter(...) does not work 2022-11-15 -import { getHighlighter } from 'shiki'; - const MermaidExample = async (md: MarkdownRenderer) => { const defaultRenderer = md.renderer.rules.fence; @@ -10,26 +7,29 @@ const MermaidExample = async (md: MarkdownRenderer) => { throw new Error('defaultRenderer is undefined'); } - const highlighter = await getHighlighter({ - theme: 'material-palenight', - langs: ['mermaid'], - }); - md.renderer.rules.fence = (tokens, index, options, env, slf) => { const token = tokens[index]; if (token.info.trim() === 'mermaid-example') { - const highlight = highlighter - .codeToHtml(token.content, { lang: 'mermaid' }) - .replace(/Code:
- - mermaid -${highlight} + + mermaid + ${ + // html is pre-escaped by the highlight function + // (it also adds `v-pre` to ignore Vue template syntax) + md.options.highlight(token.content, 'mermaid', langAttrs) + }
Diagram:
`; } diff --git a/packages/mermaid/src/docs/.vitepress/theme/index.ts b/packages/mermaid/src/docs/.vitepress/theme/index.ts index bcce341a2..e57bb03e2 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/index.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/index.ts @@ -2,11 +2,25 @@ import DefaultTheme from 'vitepress/theme'; // @ts-ignore import Mermaid from 'vitepress-plugin-mermaid/Mermaid.vue'; import './custom.css'; +import { getRedirect } from './redirect'; export default { ...DefaultTheme, - enhanceApp({ app }) { + enhanceApp({ app, router }) { // register global components app.component('Mermaid', Mermaid); + router.onBeforeRouteChange = (to) => { + if (router.route.path !== '/') { + return; + } + try { + const newPath = getRedirect(to); + if (newPath) { + console.log(`Redirecting to ${newPath} from ${window.location}`); + // router.go isn't loading the ID properly. + window.location.href = `/mermaid/${newPath}`; + } + } catch (e) {} + }; }, -}; +} as typeof DefaultTheme; diff --git a/packages/mermaid/src/docs/.vitepress/theme/redirect.ts b/packages/mermaid/src/docs/.vitepress/theme/redirect.ts new file mode 100644 index 000000000..ca4606be0 --- /dev/null +++ b/packages/mermaid/src/docs/.vitepress/theme/redirect.ts @@ -0,0 +1,89 @@ +export interface Redirect { + path: string; + id?: string; +} + +/** + * Extracts the base slug from the old URL. + * @param link - The old URL. + */ +const getBaseFile = (link: string): Redirect => { + const url = new URL(link); + if ( + (url.hostname !== 'mermaid-js.github.io' && url.hostname !== 'localhost') || + url.pathname !== '/mermaid/' + ) { + throw new Error('Not mermaidjs url'); + } + const [path, params, ...rest] = url.hash + .toLowerCase() + .replace('.md', '') + .replace(/^#\/?/g, '') + .replace(/^\.\//g, '') + .split('?'); + + // Find id in params + const id = params + ?.split('&') + .find((param) => param.startsWith('id=')) + ?.split('=')[1]; + + return { path, id }; +}; + +const redirectMap: Record = { + '8.6.0_docs': '', + accessibility: 'config/theming', + breakingchanges: '', + c4c: 'syntax/c4c', + classdiagram: 'syntax/classDiagram', + configuration: 'config/configuration', + demos: 'misc/integrations', + development: 'community/development', + directives: 'config/directives', + entityrelationshipdiagram: 'syntax/entityRelationshipDiagram', + examples: 'syntax/examples', + faq: 'misc/faq', + flowchart: 'syntax/flowchart', + gantt: 'syntax/gantt', + gitgraph: 'syntax/gitgraph', + integrations: 'misc/integrations', + 'language-highlight': '', + markdown: '', + mermaidapi: 'config/usage', + mermaidcli: 'config/mermaidCLI', + mindmap: 'syntax/mindmap', + 'more-pages': '', + 'n00b-advanced': 'config/n00b-advanced', + 'n00b-gettingstarted': 'intro/n00b-gettingStarted', + 'n00b-overview': 'community/n00b-overview', + 'n00b-syntaxreference': '', + newdiagram: 'community/newDiagram', + pie: 'syntax/pie', + plugins: '', + quickstart: 'intro/n00b-gettingStarted', + requirementdiagram: 'syntax/requirementDiagram', + security: 'community/security', + sequencediagram: 'syntax/sequenceDiagram', + setup: 'config/setup/README', + statediagram: 'syntax/stateDiagram', + themes: 'config/theming', + theming: 'config/theming', + tutorials: 'config/Tutorials', + upgrading: '', + usage: 'config/usage', + 'user-journey': 'syntax/userJourney', +}; + +/** + * + * @param link - The old documentation URL. + * @returns The new documentation path. + */ +export const getRedirect = (link: string): string | undefined => { + const { path, id } = getBaseFile(link); + if (!(path in redirectMap)) { + return; + } + return `${redirectMap[path]}.html${id ? `#${id}` : ''}`; +}; diff --git a/packages/mermaid/src/docs/index.md b/packages/mermaid/src/docs/index.md index 975546f4b..6c2763904 100644 --- a/packages/mermaid/src/docs/index.md +++ b/packages/mermaid/src/docs/index.md @@ -32,6 +32,7 @@ features: