Merge branch 'sidv/viz' into sidv/dagre-es-viz

* sidv/viz:
  Fix Lodash import
  fix: Viz build
  feat: Add package visualization
  Ignore stats.html
  feat: Add bundle visualization
  style(docs): use `github-dark` hightlight theme
  refactor(docs): use default vitepress highlighter
  fix: Move redirection to router
  chore: Add docs to redirect.ts
  feat: Redirect old documentation links.
  comments in states are skipped now
  Remove extra arrow and adjust cross position
This commit is contained in:
Sidharth Vinod 2022-11-20 19:15:49 +05:30
commit 4890999206
No known key found for this signature in database
GPG Key ID: FB5CCD378D3907CD
17 changed files with 273 additions and 47 deletions

1
.gitignore vendored
View File

@ -35,3 +35,4 @@ tsconfig.tsbuildinfo
knsv*.html
local*.html
stats/

View File

@ -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();
}

View File

@ -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"
]
}

View File

@ -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",

View File

@ -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",

View File

@ -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
};

View File

@ -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);
});
});
});
});
});

View File

@ -109,6 +109,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<STATE>[^\n\s\{]+ {/*console.log('COMPOSIT_STATE', yytext);*/return 'COMPOSIT_STATE';}
<STATE>\n {this.popState();}
<INITIAL,STATE>\{ {this.popState();this.pushState('struct'); /*console.log('begin struct', yytext);*/return 'STRUCT_START';}
<struct>\%\%(?!\{)[^\n]* /* skip comments inside state*/
<struct>\} { /*console.log('Ending struct');*/ this.popState(); return 'STRUCT_STOP';}}
<struct>[\n] /* nothing */

View File

@ -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');
}

View File

@ -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' },
],
},
];

View File

@ -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(/<span/g, '<span v-pre')
.replace('#2e3440ff', 'transparent')
.replace('#292D3E', 'transparent');
if (!md.options.highlight) {
// this function is always created by vitepress, but we need to check it
// anyway to make TypeScript happy
throw new Error(
'Missing MarkdownIt highlight function (should be automatically created by vitepress'
);
}
// doing ```mermaid-example {line-numbers=5 highlight=14-17} is not supported
const langAttrs = '';
return `<h5>Code:</h5>
<div class="language-mermaid">
<button class="copy"></button>
<span class="lang">mermaid</span>
${highlight}
${
// 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)
}
</div>
<h5>Diagram:</h5>`;
}

View File

@ -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;

View File

@ -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<string, string> = {
'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}` : ''}`;
};

View File

@ -32,6 +32,7 @@ features:
<script setup>
import { VPTeamMembers } from 'vitepress/theme'
const websiteSVG = {
svg: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>'
}

View File

@ -0,0 +1,37 @@
// This file should be moved into .vitepress folder once https://github.com/vitest-dev/vitest/issues/2344 is resolved.
// Update https://github.com/mermaid-js/mermaid/blob/18c27c6f1d0537a738cbd95898df301b83c38ffc/packages/mermaid/src/docs.mts#L246 once fixed
import { expect, test } from 'vitest';
import { getRedirect } from './.vitepress/theme/redirect';
test.each([
['http://localhost:1234/mermaid/#/flowchart.md', 'syntax/flowchart.html'],
['http://localhost/mermaid/#/flowchart.md', 'syntax/flowchart.html'],
['https://mermaid-js.github.io/mermaid/#/flowchart.md', 'syntax/flowchart.html'],
['https://mermaid-js.github.io/mermaid/#/./flowchart', 'syntax/flowchart.html'],
['https://mermaid-js.github.io/mermaid/#/flowchart', 'syntax/flowchart.html'],
['https://mermaid-js.github.io/mermaid/#flowchart', 'syntax/flowchart.html'],
['https://mermaid-js.github.io/mermaid/#/flowchart', 'syntax/flowchart.html'],
['https://mermaid-js.github.io/mermaid/#/flowchart.md?id=my-id', 'syntax/flowchart.html#my-id'],
['https://mermaid-js.github.io/mermaid/#/./flowchart.md?id=my-id', 'syntax/flowchart.html#my-id'],
[
'https://mermaid-js.github.io/mermaid/#/flowchart?another=test&id=my-id&one=more',
'syntax/flowchart.html#my-id',
],
['https://mermaid-js.github.io/mermaid/#/n00b-advanced', 'config/n00b-advanced.html'],
['https://mermaid-js.github.io/mermaid/#/n00b-advanced.md', 'config/n00b-advanced.html'],
[
'https://mermaid-js.github.io/mermaid/#/flowchart?id=a-node-in-the-form-of-a-circle',
'syntax/flowchart.html#a-node-in-the-form-of-a-circle',
],
])('should process url %s to %s', (link: string, path: string) => {
expect(getRedirect(link)).toBe(path);
});
test('should throw for invalid URL', () => {
// Not mermaid domain
expect(() => getRedirect('https://www.google.com')).toThrowError();
// Not `/mermaid/` path
expect(() => getRedirect('http://localhost/#/flowchart.md')).toThrowError();
});

View File

@ -29,7 +29,7 @@ import utils, { directiveSanitizer } from './utils';
import DOMPurify from 'dompurify';
import { MermaidConfig } from './config.type';
import { evaluate } from './diagrams/common/common';
import { isEmpty } from 'lodash';
import isEmpty from 'lodash/isEmpty';
// diagram names that support classDef statements
const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram', 'stateDiagram-v2'];

51
pnpm-lock.yaml generated
View File

@ -37,6 +37,9 @@ importers:
'@types/prettier':
specifier: ^2.7.1
version: 2.7.1
'@types/rollup-plugin-visualizer':
specifier: ^4.2.1
version: 4.2.1
'@typescript-eslint/eslint-plugin':
specifier: ^5.42.1
version: 5.42.1_2udltptbznfmezdozpdoa2aemq
@ -133,6 +136,9 @@ importers:
rimraf:
specifier: ^3.0.2
version: 3.0.2
rollup-plugin-visualizer:
specifier: ^5.8.3
version: 5.8.3_rollup@2.79.1
start-server-and-test:
specifier: ^1.14.0
version: 1.14.0
@ -269,9 +275,6 @@ importers:
rimraf:
specifier: ^3.0.2
version: 3.0.2
shiki:
specifier: ^0.11.1
version: 0.11.1
start-server-and-test:
specifier: ^1.14.0
version: 1.14.0
@ -2581,6 +2584,13 @@ packages:
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
dev: true
/@types/rollup-plugin-visualizer/4.2.1:
resolution: {integrity: sha512-Fk4y0EgmsSbvbayYhtSI9+cGvgw1rcQ9RlbExkQt4ivXRdiEwFKuRpxNuJCr0JktXIvOPUuPR7GSmtyZu0dujQ==}
dependencies:
'@types/node': 18.11.9
rollup: 2.79.1
dev: true
/@types/semver/7.3.12:
resolution: {integrity: sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==}
dev: true
@ -3531,7 +3541,7 @@ packages:
/axios/0.21.4_debug@4.3.2:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies:
follow-redirects: 1.15.2
follow-redirects: 1.15.2_debug@4.3.2
transitivePeerDependencies:
- debug
dev: true
@ -6042,6 +6052,18 @@ packages:
optional: true
dev: true
/follow-redirects/1.15.2_debug@4.3.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dependencies:
debug: 4.3.2
dev: true
/foreground-child/2.0.0:
resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==}
engines: {node: '>=8.0.0'}
@ -9404,6 +9426,22 @@ packages:
resolution: {integrity: sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==}
dev: false
/rollup-plugin-visualizer/5.8.3_rollup@2.79.1:
resolution: {integrity: sha512-QGJk4Bqe4AOat5AjipOh8esZH1nck5X2KFpf4VytUdSUuuuSwvIQZjMGgjcxe/zXexltqaXp5Vx1V3LmnQH15Q==}
engines: {node: '>=14'}
hasBin: true
peerDependencies:
rollup: 2.x || 3.x
peerDependenciesMeta:
rollup:
optional: true
dependencies:
open: 8.4.0
rollup: 2.79.1
source-map: 0.7.4
yargs: 17.5.1
dev: true
/rollup/2.79.1:
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
engines: {node: '>=10.0.0'}
@ -9760,6 +9798,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/source-map/0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'}
dev: true
/sourcemap-codec/1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
dev: true