Merge branch 'develop' into feature/class-namespace

This commit is contained in:
Kazuki Tsunemi 2023-04-25 16:40:42 +09:00
commit 95d8e3a5df
34 changed files with 5294 additions and 4224 deletions

View File

@ -50,7 +50,7 @@ body:
attributes: attributes:
label: Setup label: Setup
description: |- description: |-
Please fill out the below info. Please fill out the info below.
Note that you only need to fill out the relevant section Note that you only need to fill out the relevant section
value: |- value: |-
- Mermaid version: - Mermaid version:

29
.github/workflows/build-docs.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: Build Vitepress docs
on:
pull_request:
permissions:
contents: read
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: pnpm
node-version: 18
- name: Install Packages
run: pnpm install --frozen-lockfile
- name: Run Build
run: pnpm --filter mermaid run docs:build:vitepress

View File

@ -38,15 +38,8 @@ jobs:
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
cache: pnpm
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Install Packages
run: |
pnpm install --frozen-lockfile
env:
CYPRESS_CACHE_FOLDER: .cache/Cypress
- if: ${{ env.USE_APPLI }} - if: ${{ env.USE_APPLI }}
name: Notify applitools of new batch name: Notify applitools of new batch
# Copied from docs https://applitools.com/docs/topics/integrations/github-integration-ci-setup.html # Copied from docs https://applitools.com/docs/topics/integrations/github-integration-ci-setup.html
@ -54,19 +47,22 @@ jobs:
env: env:
# e.g. mermaid-js/mermaid/my-branch # e.g. mermaid-js/mermaid/my-branch
APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }} APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }}
APPLITOOLS_PARENT_BRANCH: ${{ github.inputs.parent_branch }} APPLITOOLS_PARENT_BRANCH: ${{ github.event.inputs.parent_branch }}
APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }} APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}
APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com' APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com'
- name: Run E2E Tests - name: Cypress run
run: pnpm run e2e uses: cypress-io/github-action@v4
id: cypress
with:
start: pnpm run dev
wait-on: 'http://localhost:9000'
env: env:
CYPRESS_CACHE_FOLDER: .cache/Cypress
# Mermaid applitools.config.js uses this to pick batch name. # Mermaid applitools.config.js uses this to pick batch name.
APPLI_BRANCH: ${{ github.ref_name }} APPLI_BRANCH: ${{ github.ref_name }}
APPLITOOLS_BATCH_ID: ${{ github.sha }} APPLITOOLS_BATCH_ID: ${{ github.sha }}
# e.g. mermaid-js/mermaid/my-branch # e.g. mermaid-js/mermaid/my-branch
APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }} APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }}
APPLITOOLS_PARENT_BRANCH: ${{ github.inputs.parent_branch }} APPLITOOLS_PARENT_BRANCH: ${{ github.event.inputs.parent_branch }}
APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }} APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}
APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com' APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com'

View File

@ -36,7 +36,7 @@ jobs:
restore-keys: cache-lychee- restore-keys: cache-lychee-
- name: Link Checker - name: Link Checker
uses: lycheeverse/lychee-action@v1.6.1 uses: lycheeverse/lychee-action@v1.7.0
with: with:
args: >- args: >-
--verbose --verbose

View File

@ -5,10 +5,6 @@ on:
push: push:
branches: branches:
- master - master
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions: permissions:
@ -53,7 +49,6 @@ jobs:
# Deployment job # Deployment job
deploy: deploy:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
environment: environment:
name: github-pages name: github-pages
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -61,4 +56,4 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v1 uses: actions/deploy-pages@v2

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: fregante/setup-git-user@v1 - uses: fregante/setup-git-user@v2
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json

View File

@ -11,6 +11,7 @@ const visualize = process.argv.includes('--visualize');
const watch = process.argv.includes('--watch'); const watch = process.argv.includes('--watch');
const mermaidOnly = process.argv.includes('--mermaid'); const mermaidOnly = process.argv.includes('--mermaid');
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
const sourcemap = false;
type OutputOptions = Exclude< type OutputOptions = Exclude<
Exclude<InlineConfig['build'], undefined>['rollupOptions'], Exclude<InlineConfig['build'], undefined>['rollupOptions'],
@ -60,9 +61,15 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
{ {
name, name,
format: 'esm', format: 'esm',
sourcemap: true, sourcemap,
entryFileNames: `${name}.esm${minify ? '.min' : ''}.mjs`, entryFileNames: `${name}.esm${minify ? '.min' : ''}.mjs`,
}, },
{
name,
format: 'umd',
sourcemap,
entryFileNames: `${name}${minify ? '.min' : ''}.js`,
},
]; ];
if (core) { if (core) {
@ -79,7 +86,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
{ {
name, name,
format: 'esm', format: 'esm',
sourcemap: true, sourcemap,
entryFileNames: `${name}.core.mjs`, entryFileNames: `${name}.core.mjs`,
}, },
]; ];

View File

@ -286,7 +286,7 @@ describe('Class diagram', () => {
cy.get('svg'); cy.get('svg');
}); });
it('15: should render a simple class diagram with css classes applied two multiple classes', () => { it('15: should render a simple class diagram with css classes applied to multiple classes', () => {
imgSnapshotTest( imgSnapshotTest(
` `
classDiagram classDiagram

View File

@ -393,9 +393,9 @@ mindmap
<script type="module"> <script type="module">
// import mindmap from '../../packages/mermaid-mindmap/src/detector'; // import mindmap from '../../packages/mermaid-mindmap/src/detector';
import example from '../../packages/mermaid-example-diagram/src/mermaid-example-diagram.core.mjs'; // import example from '../../packages/mermaid-example-diagram/src/mermaid-example-diagram.core.mjs';
import mermaid from './mermaid.esm.mjs'; import mermaid from './mermaid.esm.mjs';
await mermaid.registerExternalDiagrams([example]); // await mermaid.registerExternalDiagrams([example]);
mermaid.parseError = function (err, hash) { mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err); // console.error('Mermaid error: ', err);
}; };

View File

@ -38,7 +38,7 @@
</pre> </pre>
<script type="module"> <script type="module">
import mermaid from '../packages/mermaid/src/mermaid'; import mermaid from './mermaid.esm.mjs';
mermaid.initialize({ mermaid.initialize({
theme: 'forest', theme: 'forest',
// themeCSS: '.node rect { fill: red; }', // themeCSS: '.node rect { fill: red; }',

View File

@ -27,7 +27,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [Swimm](https://swimm.io) (**Native support**) - [Swimm](https://swimm.io) (**Native support**)
- [Notion](https://notion.so) (**Native support**) - [Notion](https://notion.so) (**Native support**)
- [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**) - [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**)
- [Obsidian](https://help.obsidian.md/How+to/Format+your+notes#Diagram) (**Native support**) - [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) (**Native support**)
- [GitBook](https://gitbook.com) - [GitBook](https://gitbook.com)
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid) - [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
- [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli) - [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli)
@ -161,6 +161,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin) - [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin)
- [mdbook](https://rust-lang.github.io/mdBook/index.html) - [mdbook](https://rust-lang.github.io/mdBook/index.html)
- [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid) - [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid)
- [Quarto](https://quarto.org/)
## Browser Extensions ## Browser Extensions

View File

@ -128,7 +128,7 @@ classDiagram
Vehicle <|-- Car Vehicle <|-- Car
``` ```
Naming convention: a class name should be composed only of alphanumeric characters (including unicode), and underscores. Naming convention: a class name should be composed only of alphanumeric characters (including unicode), underscores, and dashes (-).
### Class labels ### Class labels
@ -283,12 +283,12 @@ To describe the visibility (or encapsulation) of an attribute or method/function
- `#` Protected - `#` Protected
- `~` Package/Internal - `~` Package/Internal
> _note_ you can also include additional _classifiers_ to a method definition by adding the following notation to the _end_ of the method, i.e.: after the `()`: > _note_ you can also include additional _classifiers_ to a method definition by adding the following notation to the _end_ of the method, i.e.: after the `()` or after the return type:
> >
> - `*` Abstract e.g.: `someAbstractMethod()*` > - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*`
> - `$` Static e.g.: `someStaticMethod()$` > - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$`
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the end of its name: > _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end:
> >
> - `$` Static e.g.: `String someField$` > - `$` Static e.g.: `String someField$`
@ -632,10 +632,26 @@ You would define these actions on a separate line after all classes have been de
## Notes ## Notes
It is possible to add notes on diagram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"` It is possible to add notes on the diagram using `note "line1\nline2"`. A note can be added for a specific class using `note for <CLASS NAME> "line1\nline2"`.
### Examples ### Examples
```mermaid-example
classDiagram
note "This is a general note"
note for MyClass "This is a note for a class"
class MyClass{
}
```
```mermaid
classDiagram
note "This is a general note"
note for MyClass "This is a note for a class"
class MyClass{
}
```
_URL Link:_ _URL Link:_
```mermaid-example ```mermaid-example

View File

@ -742,9 +742,9 @@ end
Formatting: Formatting:
- For bold text, use double asterisks \*\* before and after the text. - For bold text, use double asterisks (`**`) before and after the text.
- For italics, use single asterisks \* before and after the text. - For italics, use single asterisks (`*`) before and after the text.
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag. - With traditional strings, you needed to add `<br>` tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a `<br>` tag.
This feature is applicable to node labels, edge labels, and subgraph labels. This feature is applicable to node labels, edge labels, and subgraph labels.

View File

@ -4,7 +4,7 @@
"version": "10.1.0", "version": "10.1.0",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"packageManager": "pnpm@7.30.1", "packageManager": "pnpm@8.3.1",
"keywords": [ "keywords": [
"diagram", "diagram",
"markdown", "markdown",
@ -54,65 +54,65 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@applitools/eyes-cypress": "^3.27.6", "@applitools/eyes-cypress": "^3.32.0",
"@commitlint/cli": "^17.2.0", "@commitlint/cli": "^17.6.1",
"@commitlint/config-conventional": "^17.2.0", "@commitlint/config-conventional": "^17.6.1",
"@cspell/eslint-plugin": "^6.14.2", "@cspell/eslint-plugin": "^6.31.1",
"@rollup/plugin-typescript": "^11.0.0", "@rollup/plugin-typescript": "^11.1.0",
"@types/cors": "^2.8.13", "@types/cors": "^2.8.13",
"@types/eslint": "^8.4.10", "@types/eslint": "^8.37.0",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"@types/jsdom": "^21.0.0", "@types/jsdom": "^21.1.1",
"@types/lodash": "^4.14.188", "@types/lodash": "^4.14.194",
"@types/mdast": "^3.0.10", "@types/mdast": "^3.0.11",
"@types/node": "^18.11.9", "@types/node": "^18.16.0",
"@types/prettier": "^2.7.1", "@types/prettier": "^2.7.2",
"@types/rollup-plugin-visualizer": "^4.2.1", "@types/rollup-plugin-visualizer": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^5.48.2", "@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.48.2", "@typescript-eslint/parser": "^5.59.0",
"@vitest/coverage-c8": "^0.29.0", "@vitest/coverage-c8": "^0.30.1",
"@vitest/spy": "^0.29.0", "@vitest/spy": "^0.30.1",
"@vitest/ui": "^0.29.0", "@vitest/ui": "^0.30.1",
"concurrently": "^7.5.0", "concurrently": "^8.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"coveralls": "^3.1.1", "coveralls": "^3.1.1",
"cypress": "^12.0.0", "cypress": "^12.10.0",
"cypress-image-snapshot": "^4.0.1", "cypress-image-snapshot": "^4.0.1",
"esbuild": "^0.17.0", "esbuild": "^0.17.18",
"eslint": "^8.32.0", "eslint": "^8.39.0",
"eslint-config-prettier": "^8.6.0", "eslint-config-prettier": "^8.8.0",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.13.2",
"eslint-plugin-html": "^7.1.0", "eslint-plugin-html": "^7.1.0",
"eslint-plugin-jest": "^27.1.5", "eslint-plugin-jest": "^27.2.1",
"eslint-plugin-jsdoc": "^39.6.2", "eslint-plugin-jsdoc": "^43.0.7",
"eslint-plugin-json": "^3.1.0", "eslint-plugin-json": "^3.1.0",
"eslint-plugin-lodash": "^7.4.0", "eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-markdown": "^3.0.0", "eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-tsdoc": "^0.2.17", "eslint-plugin-tsdoc": "^0.2.17",
"eslint-plugin-unicorn": "^45.0.0", "eslint-plugin-unicorn": "^46.0.0",
"express": "^4.18.2", "express": "^4.18.2",
"globby": "^13.1.2", "globby": "^13.1.4",
"husky": "^8.0.2", "husky": "^8.0.3",
"jest": "^29.3.1", "jest": "^29.5.0",
"jison": "^0.4.18", "jison": "^0.4.18",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsdom": "^21.0.0", "jsdom": "^21.1.1",
"lint-staged": "^13.0.3", "lint-staged": "^13.2.1",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"pnpm": "^7.15.0", "pnpm": "^8.3.1",
"prettier": "^2.7.1", "prettier": "^2.8.8",
"prettier-plugin-jsdoc": "^0.4.2", "prettier-plugin-jsdoc": "^0.4.2",
"rimraf": "^4.0.0", "rimraf": "^5.0.0",
"rollup-plugin-visualizer": "^5.8.3", "rollup-plugin-visualizer": "^5.9.0",
"start-server-and-test": "^1.15.4", "start-server-and-test": "^2.0.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.8.4", "typescript": "^5.0.4",
"vite": "^4.1.1", "vite": "^4.3.1",
"vitest": "^0.29.0" "vitest": "^0.30.1"
}, },
"volta": { "volta": {
"node": "18.15.0" "node": "18.16.0"
} }
} }

View File

@ -48,8 +48,8 @@
}, },
"devDependencies": { "devDependencies": {
"@types/cytoscape": "^3.19.9", "@types/cytoscape": "^3.19.9",
"concurrently": "^7.5.0", "concurrently": "^8.0.0",
"rimraf": "^4.0.0", "rimraf": "^5.0.0",
"mermaid": "workspace:*" "mermaid": "workspace:*"
}, },
"resolutions": { "resolutions": {

View File

@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "10.1.0", "version": "10.2.0-rc.2",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"module": "./dist/mermaid.core.mjs", "module": "./dist/mermaid.core.mjs",
@ -52,20 +52,20 @@
] ]
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^6.0.0", "@braintree/sanitize-url": "^6.0.2",
"@khanacademy/simple-markdown": "^0.8.6", "@khanacademy/simple-markdown": "^0.9.0",
"cytoscape": "^3.23.0", "cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0", "cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0", "cytoscape-fcose": "^2.1.0",
"d3": "^7.4.0", "d3": "^7.4.0",
"dagre-d3-es": "7.0.10", "dagre-d3-es": "7.0.10",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"dompurify": "2.4.5", "dompurify": "3.0.2",
"elkjs": "^0.8.2", "elkjs": "^0.8.2",
"khroma": "^2.0.0", "khroma": "^2.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"non-layered-tidy-tree-layout": "^2.0.2", "non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2", "stylis": "^4.1.3",
"ts-dedent": "^2.2.0", "ts-dedent": "^2.2.0",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"web-worker": "^1.2.0" "web-worker": "^1.2.0"
@ -73,43 +73,46 @@
"devDependencies": { "devDependencies": {
"@types/cytoscape": "^3.19.9", "@types/cytoscape": "^3.19.9",
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/dompurify": "^2.4.0", "@types/dompurify": "^3.0.2",
"@types/jsdom": "^21.0.0", "@types/jsdom": "^21.1.1",
"@types/lodash-es": "^4.17.7", "@types/lodash-es": "^4.17.7",
"@types/micromatch": "^4.0.2", "@types/micromatch": "^4.0.2",
"@types/prettier": "^2.7.1", "@types/prettier": "^2.7.2",
"@types/stylis": "^4.0.2", "@types/stylis": "^4.0.2",
"@types/uuid": "^9.0.0", "@types/uuid": "^9.0.1",
"@typescript-eslint/eslint-plugin": "^5.42.1", "@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.42.1", "@typescript-eslint/parser": "^5.59.0",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"concurrently": "^7.5.0", "concurrently": "^8.0.1",
"coveralls": "^3.1.1", "coveralls": "^3.1.1",
"cpy-cli": "^4.2.0", "cpy-cli": "^4.2.0",
"cspell": "^6.14.3", "cspell": "^6.31.1",
"csstree-validator": "^3.0.0", "csstree-validator": "^3.0.0",
"globby": "^13.1.2", "globby": "^13.1.4",
"jison": "^0.4.18", "jison": "^0.4.18",
"js-base64": "^3.7.2", "js-base64": "^3.7.5",
"jsdom": "^21.0.0", "jsdom": "^21.1.1",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"prettier": "^2.7.1", "prettier": "^2.8.8",
"remark": "^14.0.2", "remark": "^14.0.2",
"remark-frontmatter": "^4.0.1", "remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"rimraf": "^4.0.0", "rimraf": "^5.0.0",
"start-server-and-test": "^1.14.0", "start-server-and-test": "^2.0.0",
"typedoc": "^0.23.18", "typedoc": "^0.24.5",
"typedoc-plugin-markdown": "^3.13.6", "typedoc-plugin-markdown": "^3.15.2",
"typescript": "^4.8.4", "typescript": "^5.0.4",
"unist-util-flatmap": "^1.0.0", "unist-util-flatmap": "^1.0.0",
"vitepress": "^1.0.0-alpha.46", "vitepress": "^1.0.0-alpha.72",
"vitepress-plugin-search": "^1.0.4-alpha.19" "vitepress-plugin-search": "^1.0.4-alpha.20"
}, },
"files": [ "files": [
"dist", "dist/",
"README.md" "README.md"
], ],
"sideEffects": false "sideEffects": false,
"publishConfig": {
"access": "public"
}
} }

View File

@ -117,6 +117,7 @@ export const clear = function () {
export const getClass = function (id: string) { export const getClass = function (id: string) {
return classes[id]; return classes[id];
}; };
export const getClasses = function () { export const getClasses = function () {
return classes; return classes;
}; };
@ -181,9 +182,10 @@ export const addMember = function (className: string, member: string) {
const memberString = member.trim(); const memberString = member.trim();
if (memberString.startsWith('<<') && memberString.endsWith('>>')) { if (memberString.startsWith('<<') && memberString.endsWith('>>')) {
// Remove leading and trailing brackets // its an annotation
theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2))); theClass.annotations.push(sanitizeText(memberString.substring(2, memberString.length - 2)));
} else if (memberString.indexOf(')') > 0) { } else if (memberString.indexOf(')') > 0) {
//its a method
theClass.methods.push(sanitizeText(memberString)); theClass.methods.push(sanitizeText(memberString));
} else if (memberString) { } else if (memberString) {
theClass.members.push(sanitizeText(memberString)); theClass.members.push(sanitizeText(memberString));
@ -245,6 +247,7 @@ const setTooltip = function (ids: string, tooltip?: string) {
} }
}); });
}; };
export const getTooltip = function (id: string, namespace?: string) { export const getTooltip = function (id: string, namespace?: string) {
if (namespace) { if (namespace) {
return namespaces[namespace].classes[id].tooltip; return namespaces[namespace].classes[id].tooltip;

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require('fs');
import { LALRGenerator } from 'jison';
describe('class diagram grammar', function () {
it('should introduce no new conflicts', function () {
const file = require.resolve('./parser/classDiagram.jison');
const grammarSource = fs.readFileSync(file, 'utf8');
const grammarParser = new LALRGenerator(grammarSource, {});
expect(grammarParser.conflicts < 16).toBe(true);
});
});

View File

@ -0,0 +1,16 @@
import { readFile } from 'node:fs/promises';
// @ts-ignore - no types
import { LALRGenerator } from 'jison';
import path from 'path';
const getAbsolutePath = (relativePath: string) => {
return new URL(path.join(path.dirname(import.meta.url), relativePath)).pathname;
};
describe('class diagram grammar', function () {
it('should have no conflicts', async function () {
const grammarSource = await readFile(getAbsolutePath('./parser/classDiagram.jison'), 'utf8');
const grammarParser = new LALRGenerator(grammarSource, {});
expect(grammarParser.conflicts).toBe(0);
});
});

View File

@ -0,0 +1,78 @@
import { setConfig } from '../../config.js';
import classDB from './classDb.js';
// @ts-ignore - no types in jison
import classDiagram from './parser/classDiagram.jison';
setConfig({
securityLevel: 'strict',
});
describe('when parsing class diagram', function () {
beforeEach(function () {
classDiagram.parser.yy = classDB;
classDiagram.parser.yy.clear();
});
it('should parse diagram with direction', () => {
classDiagram.parser.parse(`classDiagram
direction TB
class Student {
-idCard : IdCard
}
class IdCard{
-id : int
-name : string
}
class Bike{
-id : int
-name : string
}
Student "1" --o "1" IdCard : carries
Student "1" --o "1" Bike : rides`);
expect(Object.keys(classDB.getClasses()).length).toBe(3);
expect(classDB.getClasses().Student).toMatchInlineSnapshot(`
{
"annotations": [],
"cssClasses": [],
"domId": "classId-Student-0",
"id": "Student",
"label": "Student",
"members": [
"-idCard : IdCard",
],
"methods": [],
"type": "",
}
`);
expect(classDB.getRelations().length).toBe(2);
expect(classDB.getRelations()).toMatchInlineSnapshot(`
[
{
"id1": "Student",
"id2": "IdCard",
"relation": {
"lineType": 0,
"type1": "none",
"type2": 0,
},
"relationTitle1": "1",
"relationTitle2": "1",
"title": "carries",
},
{
"id1": "Student",
"id2": "Bike",
"relation": {
"lineType": 0,
"type1": "none",
"type2": 0,
},
"relationTitle1": "1",
"relationTitle2": "1",
"title": "rides",
},
]
`);
});
});

View File

@ -222,9 +222,8 @@ Function arguments are optional: 'call <callback_name>()' simply executes 'callb
start start
: mermaidDoc : mermaidDoc
| statments
| direction
| directive start | directive start
| statements
; ;
direction direction
@ -286,8 +285,8 @@ className
: alphaNumToken { $$=$1; } : alphaNumToken { $$=$1; }
| classLiteralName { $$=$1; } | classLiteralName { $$=$1; }
| alphaNumToken className { $$=$1+$2; } | alphaNumToken className { $$=$1+$2; }
| alphaNumToken GENERICTYPE { $$=$1+'~'+$2; } | alphaNumToken GENERICTYPE { $$=$1+'~'+$2+'~'; }
| classLiteralName GENERICTYPE { $$=$1+'~'+$2; } | classLiteralName GENERICTYPE { $$=$1+'~'+$2+'~'; }
; ;
statement statement
@ -300,7 +299,6 @@ statement
| clickStatement | clickStatement
| cssClassStatement | cssClassStatement
| noteStatement | noteStatement
| directive
| direction | direction
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } | acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
@ -409,7 +407,7 @@ textToken : textNoTagsToken | TAGSTART | TAGEND | '==' | '--' | PCT | DEFA
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ; textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ;
alphaNumToken : UNICODE_TEXT | NUM | ALPHA; alphaNumToken : UNICODE_TEXT | NUM | ALPHA | MINUS;
classLiteralName : BQUOTE_STR; classLiteralName : BQUOTE_STR;

View File

@ -199,11 +199,7 @@ export const drawClass = function (elem, classDef, conf, diagObj) {
isFirst = false; isFirst = false;
}); });
let classTitleString = classDef.id; let classTitleString = getClassTitleString(classDef);
if (classDef.type !== undefined && classDef.type !== '') {
classTitleString += '<' + classDef.type + '>';
}
const classTitle = title.append('tspan').text(classTitleString).attr('class', 'title'); const classTitle = title.append('tspan').text(classTitleString).attr('class', 'title');
@ -291,6 +287,16 @@ export const drawClass = function (elem, classDef, conf, diagObj) {
return classInfo; return classInfo;
}; };
export const getClassTitleString = function (classDef) {
let classTitleString = classDef.id;
if (classDef.type) {
classTitleString += '<' + classDef.type + '>';
}
return classTitleString;
};
/** /**
* Renders a note diagram * Renders a note diagram
* *
@ -355,6 +361,9 @@ export const drawNote = function (elem, note, conf, diagObj) {
}; };
export const parseMember = function (text) { export const parseMember = function (text) {
// Note: these two regular expressions don't parse the official UML syntax for attributes
// and methods. They parse a Java-style syntax of the form
// "String name" (for attributes) and "String name(int x)" for methods
const fieldRegEx = /^([#+~-])?(\w+)(~\w+~|\[])?\s+(\w+) *([$*])?$/; const fieldRegEx = /^([#+~-])?(\w+)(~\w+~|\[])?\s+(\w+) *([$*])?$/;
const methodRegEx = /^([#+|~-])?(\w+) *\( *(.*)\) *([$*])? *(\w*[[\]|~]*\s*\w*~?)$/; const methodRegEx = /^([#+|~-])?(\w+) *\( *(.*)\) *([$*])? *(\w*[[\]|~]*\s*\w*~?)$/;
@ -421,33 +430,48 @@ const buildLegacyDisplay = function (text) {
let displayText = ''; let displayText = '';
let cssStyle = ''; let cssStyle = '';
let returnType = ''; let returnType = '';
let visibility = '';
let firstChar = text.substring(0, 1);
let lastChar = text.substring(text.length - 1, text.length);
if (firstChar.match(/[#+~-]/)) {
visibility = firstChar;
}
let noClassifierRe = /[\s\w)~]/;
if (!lastChar.match(noClassifierRe)) {
cssStyle = parseClassifier(lastChar);
}
let startIndex = visibility === '' ? 0 : 1;
let endIndex = cssStyle === '' ? text.length : text.length - 1;
text = text.substring(startIndex, endIndex);
let methodStart = text.indexOf('('); let methodStart = text.indexOf('(');
let methodEnd = text.indexOf(')'); let methodEnd = text.indexOf(')');
if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) { if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) {
let visibility = ''; let methodName = text.substring(0, methodStart).trim();
let methodName = '';
let firstChar = text.substring(0, 1);
if (firstChar.match(/\w/)) {
methodName = text.substring(0, methodStart).trim();
} else {
if (firstChar.match(/[#+~-]/)) {
visibility = firstChar;
}
methodName = text.substring(1, methodStart).trim();
}
const parameters = text.substring(methodStart + 1, methodEnd); const parameters = text.substring(methodStart + 1, methodEnd);
const classifier = text.substring(methodEnd + 1, 1);
cssStyle = parseClassifier(text.substring(methodEnd + 1, methodEnd + 2));
displayText = visibility + methodName + '(' + parseGenericTypes(parameters.trim()) + ')'; displayText = visibility + methodName + '(' + parseGenericTypes(parameters.trim()) + ')';
if (methodEnd < text.length) { if (methodEnd < text.length) {
returnType = text.substring(methodEnd + 2).trim(); // special case: classifier after the closing parenthesis
let potentialClassifier = text.substring(methodEnd + 1, methodEnd + 2);
if (cssStyle === '' && !potentialClassifier.match(noClassifierRe)) {
cssStyle = parseClassifier(potentialClassifier);
returnType = text.substring(methodEnd + 2).trim();
} else {
returnType = text.substring(methodEnd + 1).trim();
}
if (returnType !== '') { if (returnType !== '') {
if (returnType.charAt(0) === ':') {
returnType = returnType.substring(1).trim();
}
returnType = ' : ' + parseGenericTypes(returnType); returnType = ' : ' + parseGenericTypes(returnType);
displayText += returnType; displayText += returnType;
} }
@ -502,6 +526,7 @@ const parseClassifier = function (classifier) {
}; };
export default { export default {
getClassTitleString,
drawClass, drawClass,
drawEdge, drawEdge,
drawNote, drawNote,

View File

@ -1,8 +1,19 @@
import svgDraw from './svgDraw.js'; import svgDraw from './svgDraw.js';
describe('class member Renderer, ', function () { describe('given a string representing class method, ', function () {
describe('when parsing text to build method display string', function () { it('should handle class names with generics', function () {
it('should handle simple method declaration', function () { const classDef = {
id: 'Car',
type: 'T',
label: 'Car',
};
let actual = svgDraw.getClassTitleString(classDef);
expect(actual).toBe('Car<T>');
});
describe('when parsing base method declaration', function () {
it('should handle simple declaration', function () {
const str = 'foo()'; const str = 'foo()';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -10,71 +21,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle public visibility', function () { it('should handle declaration with parameters', function () {
const str = '+foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('+foo()');
expect(actual.cssStyle).toBe('');
});
it('should handle private visibility', function () {
const str = '-foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('-foo()');
expect(actual.cssStyle).toBe('');
});
it('should handle protected visibility', function () {
const str = '#foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('#foo()');
expect(actual.cssStyle).toBe('');
});
it('should handle package/internal visibility', function () {
const str = '~foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('~foo()');
expect(actual.cssStyle).toBe('');
});
it('should ignore unknown character for visibility', function () {
const str = '!foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('');
});
it('should handle abstract method classifier', function () {
const str = 'foo()*';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('font-style:italic;');
});
it('should handle static method classifier', function () {
const str = 'foo()$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should ignore unknown character for classifier', function () {
const str = 'foo()!';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('');
});
it('should handle simple method declaration with parameters', function () {
const str = 'foo(int id)'; const str = 'foo(int id)';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -82,7 +29,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle simple method declaration with multiple parameters', function () { it('should handle declaration with multiple parameters', function () {
const str = 'foo(int id, object thing)'; const str = 'foo(int id, object thing)';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -90,7 +37,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle simple method declaration with single item in parameters', function () { it('should handle declaration with single item in parameters', function () {
const str = 'foo(id)'; const str = 'foo(id)';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -98,7 +45,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle simple method declaration with single item in parameters with extra spaces', function () { it('should handle declaration with single item in parameters with extra spaces', function () {
const str = ' foo ( id) '; const str = ' foo ( id) ';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -106,22 +53,6 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle method declaration with return value', function () {
const str = 'foo(id) int';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(id) : int');
expect(actual.cssStyle).toBe('');
});
it('should handle method declaration with generic return value', function () {
const str = 'foo(id) List~int~';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(id) : List<int>');
expect(actual.cssStyle).toBe('');
});
it('should handle method declaration with generic parameter', function () { it('should handle method declaration with generic parameter', function () {
const str = 'foo(List~int~)'; const str = 'foo(List~int~)';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -130,6 +61,46 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle method declaration with normal and generic parameter', function () {
const str = 'foo(int, List~int~)';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(int, List<int>)');
expect(actual.cssStyle).toBe('');
});
it('should handle declaration with return value', function () {
const str = 'foo(id) int';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(id) : int');
expect(actual.cssStyle).toBe('');
});
it('should handle declaration with colon return value', function () {
const str = 'foo(id) : int';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(id) : int');
expect(actual.cssStyle).toBe('');
});
it('should handle declaration with generic return value', function () {
const str = 'foo(id) List~int~';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(id) : List<int>');
expect(actual.cssStyle).toBe('');
});
it('should handle declaration with colon generic return value', function () {
const str = 'foo(id) : List~int~';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(id) : List<int>');
expect(actual.cssStyle).toBe('');
});
it('should handle method declaration with all possible markup', function () { it('should handle method declaration with all possible markup', function () {
const str = '+foo ( List~int~ ids )* List~Item~'; const str = '+foo ( List~int~ ids )* List~Item~';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -138,7 +109,7 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe('font-style:italic;'); expect(actual.cssStyle).toBe('font-style:italic;');
}); });
it('should handle method declaration with nested markup', function () { it('should handle method declaration with nested generics', function () {
const str = '+foo ( List~List~int~~ ids )* List~List~Item~~'; const str = '+foo ( List~List~int~~ ids )* List~List~Item~~';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -147,8 +118,134 @@ describe('class member Renderer, ', function () {
}); });
}); });
describe('when parsing text to build field display string', function () { describe('when parsing method visibility', function () {
it('should handle simple field declaration', function () { it('should correctly handle public', function () {
const str = '+foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('+foo()');
expect(actual.cssStyle).toBe('');
});
it('should correctly handle private', function () {
const str = '-foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('-foo()');
expect(actual.cssStyle).toBe('');
});
it('should correctly handle protected', function () {
const str = '#foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('#foo()');
expect(actual.cssStyle).toBe('');
});
it('should correctly handle package/internal', function () {
const str = '~foo()';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('~foo()');
expect(actual.cssStyle).toBe('');
});
});
describe('when parsing method classifier', function () {
it('should handle abstract method', function () {
const str = 'foo()*';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('font-style:italic;');
});
it('should handle abstract method with return type', function () {
const str = 'foo(name: String) int*';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(name: String) : int');
expect(actual.cssStyle).toBe('font-style:italic;');
});
it('should handle abstract method classifier after parenthesis with return type', function () {
const str = 'foo(name: String)* int';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(name: String) : int');
expect(actual.cssStyle).toBe('font-style:italic;');
});
it('should handle static method classifier', function () {
const str = 'foo()$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should handle static method classifier with return type', function () {
const str = 'foo(name: String) int$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(name: String) : int');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should handle static method classifier with colon and return type', function () {
const str = 'foo(name: String): int$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(name: String) : int');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should handle static method classifier after parenthesis with return type', function () {
const str = 'foo(name: String)$ int';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo(name: String) : int');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should ignore unknown character for classifier', function () {
const str = 'foo()!';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo()');
expect(actual.cssStyle).toBe('');
});
});
});
describe('given a string representing class member, ', function () {
describe('when parsing member declaration', function () {
it('should handle simple field', function () {
const str = 'id';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('id');
expect(actual.cssStyle).toBe('');
});
it('should handle field with type', function () {
const str = 'int id';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('int id');
expect(actual.cssStyle).toBe('');
});
it('should handle field with type (name first)', function () {
const str = 'id: int';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('id: int');
expect(actual.cssStyle).toBe('');
});
it('should handle array field', function () {
const str = 'int[] ids'; const str = 'int[] ids';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -156,7 +253,15 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle field declaration with generic type', function () { it('should handle array field (name first)', function () {
const str = 'ids: int[]';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('ids: int[]');
expect(actual.cssStyle).toBe('');
});
it('should handle field with generic type', function () {
const str = 'List~int~ ids'; const str = 'List~int~ ids';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
@ -164,12 +269,62 @@ describe('class member Renderer, ', function () {
expect(actual.cssStyle).toBe(''); expect(actual.cssStyle).toBe('');
}); });
it('should handle static field classifier', function () { it('should handle field with generic type (name first)', function () {
const str = 'ids: List~int~';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('ids: List<int>');
expect(actual.cssStyle).toBe('');
});
});
describe('when parsing classifiers', function () {
it('should handle static field', function () {
const str = 'String foo$'; const str = 'String foo$';
let actual = svgDraw.parseMember(str); let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('String foo'); expect(actual.displayText).toBe('String foo');
expect(actual.cssStyle).toBe('text-decoration:underline;'); expect(actual.cssStyle).toBe('text-decoration:underline;');
}); });
it('should handle static field (name first)', function () {
const str = 'foo: String$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo: String');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should handle static field with generic type', function () {
const str = 'List~String~ foo$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('List<String> foo');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should handle static field with generic type (name first)', function () {
const str = 'foo: List~String~$';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('foo: List<String>');
expect(actual.cssStyle).toBe('text-decoration:underline;');
});
it('should handle field with nested generic type', function () {
const str = 'List~List~int~~ idLists';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('List<List<int>> idLists');
expect(actual.cssStyle).toBe('');
});
it('should handle field with nested generic type (name first)', function () {
const str = 'idLists: List~List~int~~';
let actual = svgDraw.parseMember(str);
expect(actual.displayText).toBe('idLists: List<List<int>>');
expect(actual.cssStyle).toBe('');
});
}); });
}); });

View File

@ -1,6 +1,8 @@
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import { MermaidConfig } from '../../config.type.js'; import { MermaidConfig } from '../../config.type.js';
export const lineBreakRegex = /<br\s*\/?>/gi;
/** /**
* Gets the rows of lines in a string * Gets the rows of lines in a string
* *
@ -65,8 +67,6 @@ export const sanitizeTextOrArray = (
return a.flat().map((x: string) => sanitizeText(x, config)); return a.flat().map((x: string) => sanitizeText(x, config));
}; };
export const lineBreakRegex = /<br\s*\/?>/gi;
/** /**
* Whether or not a text has any line breaks * Whether or not a text has any line breaks
* *

View File

@ -182,7 +182,7 @@ export const addVertices = async function (vert, svgId, root, doc, diagObj, pare
// Add the element to the DOM // Add the element to the DOM
if (node.type !== 'group') { if (node.type !== 'group') {
nodeEl = insertNode(nodes, node, vertex.dir); nodeEl = await insertNode(nodes, node, vertex.dir);
boundingBox = nodeEl.node().getBBox(); boundingBox = nodeEl.node().getBBox();
} else { } else {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');

View File

@ -56,7 +56,6 @@ const getStyles = (options) =>
font-size: 18px; font-size: 18px;
fill: ${options.textColor}; fill: ${options.textColor};
} }
}
`; `;
export default getStyles; export default getStyles;

View File

@ -21,7 +21,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [Swimm](https://swimm.io) (**Native support**) - [Swimm](https://swimm.io) (**Native support**)
- [Notion](https://notion.so) (**Native support**) - [Notion](https://notion.so) (**Native support**)
- [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**) - [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**)
- [Obsidian](https://help.obsidian.md/How+to/Format+your+notes#Diagram) (**Native support**) - [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) (**Native support**)
- [GitBook](https://gitbook.com) - [GitBook](https://gitbook.com)
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid) - [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
- [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli) - [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli)
@ -155,6 +155,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin) - [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin)
- [mdbook](https://rust-lang.github.io/mdBook/index.html) - [mdbook](https://rust-lang.github.io/mdBook/index.html)
- [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid) - [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid)
- [Quarto](https://quarto.org/)
## Browser Extensions ## Browser Extensions

View File

@ -74,7 +74,7 @@ classDiagram
Vehicle <|-- Car Vehicle <|-- Car
``` ```
Naming convention: a class name should be composed only of alphanumeric characters (including unicode), and underscores. Naming convention: a class name should be composed only of alphanumeric characters (including unicode), underscores, and dashes (-).
### Class labels ### Class labels
@ -171,12 +171,12 @@ To describe the visibility (or encapsulation) of an attribute or method/function
- `#` Protected - `#` Protected
- `~` Package/Internal - `~` Package/Internal
> _note_ you can also include additional _classifiers_ to a method definition by adding the following notation to the _end_ of the method, i.e.: after the `()`: > _note_ you can also include additional _classifiers_ to a method definition by adding the following notation to the _end_ of the method, i.e.: after the `()` or after the return type:
> >
> - `*` Abstract e.g.: `someAbstractMethod()*` > - `*` Abstract e.g.: `someAbstractMethod()*` or `someAbstractMethod() int*`
> - `$` Static e.g.: `someStaticMethod()$` > - `$` Static e.g.: `someStaticMethod()$` or `someStaticMethod() String$`
> _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the end of its name: > _note_ you can also include additional _classifiers_ to a field definition by adding the following notation to the very end:
> >
> - `$` Static e.g.: `String someField$` > - `$` Static e.g.: `String someField$`
@ -420,10 +420,18 @@ click className href "url" "tooltip"
## Notes ## Notes
It is possible to add notes on diagram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"` It is possible to add notes on the diagram using `note "line1\nline2"`. A note can be added for a specific class using `note for <CLASS NAME> "line1\nline2"`.
### Examples ### Examples
```mermaid
classDiagram
note "This is a general note"
note for MyClass "This is a note for a class"
class MyClass{
}
```
_URL Link:_ _URL Link:_
```mmd ```mmd

View File

@ -465,9 +465,9 @@ end
Formatting: Formatting:
- For bold text, use double asterisks \*\* before and after the text. - For bold text, use double asterisks (`**`) before and after the text.
- For italics, use single asterisks \* before and after the text. - For italics, use single asterisks (`*`) before and after the text.
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag. - With traditional strings, you needed to add `<br>` tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a `<br>` tag.
This feature is applicable to node labels, edge labels, and subgraph labels. This feature is applicable to node labels, edge labels, and subgraph labels.

View File

@ -12,24 +12,24 @@ vi.mock('dagre-d3');
// mermaidAPI.spec.ts: // mermaidAPI.spec.ts:
import * as accessibility from './accessibility.js'; // Import it this way so we can use spyOn(accessibility,...) import * as accessibility from './accessibility.js'; // Import it this way so we can use spyOn(accessibility,...)
vi.mock('./accessibility', () => ({ vi.mock('./accessibility.js', () => ({
setA11yDiagramInfo: vi.fn(), setA11yDiagramInfo: vi.fn(),
addSVGa11yTitleDescription: vi.fn(), addSVGa11yTitleDescription: vi.fn(),
})); }));
// Mock the renderers specifically so we can test render(). Need to mock draw() for each renderer // Mock the renderers specifically so we can test render(). Need to mock draw() for each renderer
vi.mock('./diagrams/c4/c4Renderer'); vi.mock('./diagrams/c4/c4Renderer.js');
vi.mock('./diagrams/class/classRenderer'); vi.mock('./diagrams/class/classRenderer.js');
vi.mock('./diagrams/class/classRenderer-v2'); vi.mock('./diagrams/class/classRenderer-v2.js');
vi.mock('./diagrams/er/erRenderer'); vi.mock('./diagrams/er/erRenderer.js');
vi.mock('./diagrams/flowchart/flowRenderer-v2'); vi.mock('./diagrams/flowchart/flowRenderer-v2.js');
vi.mock('./diagrams/git/gitGraphRenderer'); vi.mock('./diagrams/git/gitGraphRenderer.js');
vi.mock('./diagrams/gantt/ganttRenderer'); vi.mock('./diagrams/gantt/ganttRenderer.js');
vi.mock('./diagrams/user-journey/journeyRenderer'); vi.mock('./diagrams/user-journey/journeyRenderer.js');
vi.mock('./diagrams/pie/pieRenderer'); vi.mock('./diagrams/pie/pieRenderer.js');
vi.mock('./diagrams/requirement/requirementRenderer'); vi.mock('./diagrams/requirement/requirementRenderer.js');
vi.mock('./diagrams/sequence/sequenceRenderer'); vi.mock('./diagrams/sequence/sequenceRenderer.js');
vi.mock('./diagrams/state/stateRenderer-v2'); vi.mock('./diagrams/state/stateRenderer-v2.js');
// ------------------------------------- // -------------------------------------
@ -52,7 +52,7 @@ import assignWithDepth from './assignWithDepth.js';
// -------------- // --------------
// Mocks // Mocks
// To mock a module, first define a mock for it, then (if used explicitly in the tests) import it. Be sure the path points to exactly the same file as is imported in mermaidAPI (the module being tested) // To mock a module, first define a mock for it, then (if used explicitly in the tests) import it. Be sure the path points to exactly the same file as is imported in mermaidAPI (the module being tested)
vi.mock('./styles', () => { vi.mock('./styles.js', () => {
return { return {
addStylesForDiagram: vi.fn(), addStylesForDiagram: vi.fn(),
default: vi.fn().mockReturnValue(' .userStyle { font-weight:bold; }'), default: vi.fn().mockReturnValue(' .userStyle { font-weight:bold; }'),

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
"extends": [ "extends": [
"config:base", "config:base",
":rebaseStalePrs", ":rebaseStalePrs",
"group:allNonMajor",
"schedule:earlyMondays", "schedule:earlyMondays",
":automergeMinor", ":automergeMinor",
":automergeTesters", ":automergeTesters",
@ -14,6 +13,18 @@
{ {
"matchUpdateTypes": ["minor", "patch", "digest"], "matchUpdateTypes": ["minor", "patch", "digest"],
"automerge": true "automerge": true
},
{
"groupName": "all patch dependencies",
"groupSlug": "all-patch",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["patch"]
},
{
"groupName": "all minor dependencies",
"groupSlug": "all-minor",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor"]
} }
], ],
"dependencyDashboard": true, "dependencyDashboard": true,

View File

@ -14,12 +14,17 @@ const lint = async (file: string): Promise<boolean> => {
console.log(`Linting ${file}`); console.log(`Linting ${file}`);
const jisonCode = await readFile(file, 'utf8'); const jisonCode = await readFile(file, 'utf8');
// @ts-ignore no typings // @ts-ignore no typings
const jsCode = new jison.Generator(jisonCode, { moduleType: 'amd' }).generate(); const generator = new jison.Generator(jisonCode, { moduleType: 'amd' });
const jsCode = generator.generate();
const [result] = await linter.lintText(jsCode); const [result] = await linter.lintText(jsCode);
if (result.errorCount > 0) { if (result.errorCount > 0) {
console.error(`Linting failed for ${file}`); console.error(`Linting failed for ${file}`);
console.error(result.messages); console.error(result.messages);
} }
if (generator.conflicts > 0) {
console.error(`Linting failed for ${file}. Conflicts found in grammar`);
return false;
}
return result.errorCount === 0; return result.errorCount === 0;
}; };