Compare commits

..

85 Commits

Author SHA1 Message Date
Sidharth Vinod
927217d77c 5043 Register internal diagrams before external 2023-11-19 08:35:05 +05:30
Sidharth Vinod
b5f3cdc0b0 feat: 5043 Add priority support for registered diagrams
Allows external diagrams to override internal diagrams, if necessary.
This will help move ELK to a different package, without completely breaking rendering, by falling back to dagre, and supporting ELK if it's registered as an external diagram.
2023-11-18 21:12:30 +05:30
Sidharth Vinod
510549f365 Merge branch 'develop' into next
* develop:
  fix text-decoration for abstract attibutes
  ci(pr-labeler): add required `template` option
  ci(pr-labeler): replace TimonVS/pr-labeler-action
  style(pr-labeler): format .github/pr-labeler.yml
  docs(ci/pr-labeler): warn about security issues
  ci(release-draft): handle new release-drafter name
  ci(release-drafter): remove unused `branch` config
  ci(pr-labeler): limit GITHUB_TOKEN permissions
  ci(release-draft): limit GITHUB_TOKEN permissions
2023-11-15 09:10:13 +05:30
Sidharth Vinod
6ce543e118 Merge branch 'develop' into next
* develop: (126 commits)
  Update all patch dependencies
  Fix docs
  Update packages/mermaid/src/docs/community/questions-and-suggestions.md
  Update packages/mermaid/src/diagrams/class/classRenderer-v2.ts
  update edge ids
  draw top actors with lines  first followed by messages
  Bump GitHub workflow actions to latest versions
  Update docs
  Documentation: clarify sentence
  Fix lint
  Fix typo
  fix typo
  Add new Atlassian integrations
  chore(deps): update all patch dependencies
  Update demos/sequence.html
  chore: release v10.6.1
  fix
  fix
  fix: render the participants in same order as they are created
  fix(flow): fix invalid ellipseText regex
  ...
2023-11-14 11:20:27 +05:30
Sidharth Vinod
49a197eaa8 chore: Update pnpm-lock 2023-10-06 11:12:50 +05:30
Sidharth Vinod
3abe7cfc45 Merge branch 'develop' into next
* develop: (61 commits)
  Revert "fix: Reduce gantt exclude days length"
  Commented out broken test (#4913)
  fix: Reduce gantt exclude days length
  Update docs
  Fix lint issue
  Fix release version
  Fix TopBar
  Add MC to integrations
  Add TopBar
  Fix docs
  Docs: Add Product Hunt info (#4900)
  Update docs
  Merge branch 'release/10.5.0'
  Mermaid release v10.5.0
  docs: typo fixed
  docs: typo fixed
  Fix for issue with backticks in ids in classDiagrams
  more fixes
  fix typo
  more link fixes
  ...
2023-10-06 11:11:34 +05:30
Reda Al Sulais
91eb824c21 Merge branch 'develop' into next
Signed-off-by: Reda Al Sulais <u.yokozuna@gmail.com>
2023-09-20 17:55:20 +03:00
Reda Al Sulais
3c90894e38 Merge branch 'develop' into next 2023-09-15 01:57:20 +03:00
Sidharth Vinod
271b779995 refactor: Simplify TokenBuilder and ValueConverter 2023-09-14 17:32:44 +05:30
Sidharth Vinod
52b33f6f47 chore: Fix pnpm-lock 2023-09-14 14:25:22 +05:30
Sidharth Vinod
5aee43d05b Merge branch 'develop' into next
* develop:
  Bump version
  chore: Fix type in 'getLineFunctionsWithOffset'
  Update cypress/platform/marker_unique_id.html
  refactor: Add getLineFunctionsWithOffset function
  refactor: Move EdgeData to types
  fix: PointStart marker refX
  Added cypress test
  chore(deps): update all patch dependencies
  refactor: Fix typings in utils.ts
  Give markers unique id's per graph
  chore: Add @internal to createCSSStyles
  chore: Bump version
  refactor: Remove unused variables
  fix: #4818 support `getClasses` in external diagrams.
2023-09-14 14:19:31 +05:30
Sidharth Vinod
7b29a380fc chore: Fix type 2023-09-08 16:42:08 +05:30
Sidharth Vinod
997c23befa Merge branch 'develop' into next
* develop:
  Remove unnecessary tests
  Remove optional chaining
  refactor: Use `||` instead of `??`
  core: Adapt changes from 3f7bafb2d7
  Update cypress/helpers/util.js
  chore: Add deprecation notices, improve types
  chore: Cleanup gitGraph tests
  Update README.md
  refactor: Move setWrap to individual diagrams as necessary.
  refactor: Remove directives from grammar
  refactor: Update DBs to remove directive handling
  refactor: Move directive processing before parsing
  I refactored the code to improve its time complexity by removing unnecessary code and optimizing the existing code.
2023-09-08 16:36:45 +05:30
Sidharth Vinod
4ce26296d6 Merge branch 'develop' into next
* develop:
  chore: Update docs
2023-09-06 23:25:57 +05:30
Sidharth Vinod
4342759da7 Merge branch 'develop' into next
* develop:
  Update flowchart.md (#4798)
  Update flowchart.md (#4792)
  chore: Update docs
  Added missing integration tests and release version in docs.
  chore: Fix warning formatting
  docs: Disable showValues in sankey example
  Added support for millisecond and second to gantt tickInterval
  Use utf8 encoding in Jupyter example
2023-09-06 23:16:03 +05:30
Sidharth Vinod
25f2d224f1 Merge branch 'develop' into next
* develop:
  fix: Add support for `~test Array~string~`
2023-09-06 16:53:18 +05:30
Reda Al Sulais
0abb4f8c6f Merge branch develop into next 2023-09-06 02:16:25 +03:00
Sidharth Vinod
697ac18872 Merge branch 'develop' into next
* develop:
  chore: Align with convention
  add additional test case
  added test case
  add sanitize text
  Update docs
  modifications to generic parser
  improvements to parseGenericTypes
  Update packages/mermaid/src/diagrams/class/svgDraw.js
  return comment
  add tsdoc comments
  update tests
  apply suggesitons
  Update packages/mermaid/src/diagrams/class/classTypes.ts
  Update packages/mermaid/src/diagrams/class/classTypes.ts
  update tests and db name
  Fix tests
  spec changes
  update classes to handle , in generic
  Update and add tests
  Create new type for member handling
2023-09-03 12:27:44 +05:30
Sidharth Vinod
fc229cf274 Merge pull request #4803 from aloisklink/refactor/mermaid-config-limit-enum-types
[v11] Limit `MermaidConfig` enum TypesScript types to certain values
2023-09-03 05:29:25 +00:00
Alois Klink
b48136d994 refactor!: remove MermaidConfig type enum fallback
Currently (in Mermaid v10), pretty much all enum types in the
MermaidConfig have generic `string` or `number` fallbacks,
for backwards compatibility.

This commit drops this. The MermaidConfig TypeScript types now expects
a limited amount of values.

BREAKING-CHANGE: Remove `MermaidConfig` generic type fallbacks for
                 enum values.
2023-09-02 19:33:40 +01:00
Alois Klink
77ba7c987a test: rewrite some config vals to tighten types
We're planning on limiting some of MermaidConfig's types to specific
values (e.g. `0 | 1` instead of `number`).
2023-09-02 19:33:40 +01:00
Reda Al Sulais
84f3baf013 Merge branch 'develop' into next
Signed-off-by: Reda Al Sulais <u.yokozuna@gmail.com>
2023-09-02 18:57:00 +03:00
Sidharth Vinod
44b93c039a Merge pull request #4727 from Yokozuna59/add-info-langium-parser
feat: add `@mermaid-js/parser` package and `info` langium parser
2023-08-28 08:10:30 +00:00
Sidharth Vinod
4d5313699e chore: Add comment for yy. 2023-08-28 13:39:37 +05:30
Sidharth Vinod
cd198290d7 Merge branch 'next' into pr/Yokozuna59/4727
* next:
  chore: Increase heap size when building
  fix(er): bug if relationship is declared first
  test(er): add cypress test on entity name alias
  feat(er): use square brackets to add aliases
  docs(er): add release version for entity name aliases
  feat(er): add entity name alias
2023-08-28 13:32:27 +05:30
Sidharth Vinod
60ed7d3273 chore: Increase heap size when building 2023-08-26 23:43:27 +05:30
Sidharth Vinod
9bcfba6620 Merge branch 'develop' into next
* develop:
  fix(er): bug if relationship is declared first
  test(er): add cypress test on entity name alias
  feat(er): use square brackets to add aliases
  docs(er): add release version for entity name aliases
  feat(er): add entity name alias
2023-08-26 23:09:05 +05:30
Reda Al Sulais
7ea3c64268 chore: increase test-util.ts converage by returning undefined 2023-08-26 14:37:36 +03:00
Reda Al Sulais
2b6a34e9e0 chore: add vitest imports to test-util.ts 2023-08-26 14:20:22 +03:00
Reda Al Sulais
458b90c78d chore: run pnpm lint:fix 2023-08-26 14:06:41 +03:00
Reda Al Sulais
dd284c0986 Merge branch 'add-info-langium-parser' of https://github.com/Yokozuna59/mermaid into add-info-langium-parser 2023-08-26 14:02:19 +03:00
Reda Al Sulais
21539dfb6a create noErrorsOrAlternatives parser helper function 2023-08-26 14:01:56 +03:00
Reda Al Sulais
91785b8284 Merge branch 'next' into add-info-langium-parser 2023-08-26 13:48:31 +03:00
Reda Al Sulais
f202770b70 Merge branch develop into next 2023-08-26 13:44:24 +03:00
Reda Al Sulais
8186a54962 chore: export InfoModule from infoModule.ts 2023-08-26 13:39:17 +03:00
Sidharth Vinod
866909b803 Merge branch 'develop' into next
* develop:
  chore: Update editor.bash to build latest version
  chore: Build after clone
  chore: Force install npm to avoid cache.
  fix: live editor exists error
  chore: Add netlify.toml
  chore: Update editor script
  chore: Add live editor build script for previews
  docs: Fix sankey demo
  feat(sankey): Show values (#4674)
2023-08-25 10:07:46 +05:30
Sidharth Vinod
408910e6e8 Merge branch 'develop' into next
* develop:
  Fixed docs according review
  chore: Fix type imports
  Fixed links
  Split development documentation in several separate pages, fixes for the sidebar menu
2023-08-23 13:19:12 +05:30
Reda Al Sulais
24c8e575f4 docs(parser): create packages/parser README.md file 2023-08-22 20:39:19 +03:00
Reda Al Sulais
8d0ca2c876 build: build .langium file using generate from langium-cli 2023-08-22 13:38:23 +03:00
Reda Al Sulais
fc96ebefd4 build: update langium and langium-cli to v2.0.1 2023-08-22 13:19:52 +03:00
Reda Al Sulais
394330175f Merge remote-tracking branch 'upstream/next' into add-info-langium-parser
Signed-off-by: Reda Al Sulais <u.yokozuna@gmail.com>
2023-08-22 13:13:03 +03:00
Sidharth Vinod
f946c3da06 Merge branch 'develop' into next
* develop:
  chore: Remove circular dependency
  Update docs
  docs: Add frontmatter config demos
  docs: Add frontmatter config docs
  fix: XSS vulnerability
  chore: Minor typo fixes
  chore: Add test with both frontmatter and directive
  Update docs
  feat: Add support for config in frontmatter
  chore: Fix type in assignWithDepth
  refactor: Move `sanitizeDirective` into `addDirective`
  refactor: Rename and cleanup `directiveSanitizer`
2023-08-22 13:58:24 +05:30
Sidharth Vinod
156fbd1958 Merge branch 'develop' into next
* develop:
  chore: Remove duplicate CI action
  chore: Add circular dependency check in CI
  refactor: Remove circular dependencies
2023-08-22 13:31:59 +05:30
Sidharth Vinod
7dd0d126e2 Merge branch 'develop' into next
* develop:
  deps: Update unocss and webpack to address vulnerability.
  chore(deps): update all patch dependencies
  ci(release-drafter): add more release notes categories
2023-08-22 10:21:13 +05:30
Reda Al Sulais
205360c109 fix: fix if statment logic checks if parser is not undefined 2023-08-21 03:09:05 +03:00
Reda Al Sulais
984a0e6d06 chore: add a comment illustrate why we build packages sequentially 2023-08-21 03:05:23 +03:00
Reda Al Sulais
eb63568ceb chore: refactore && into if in populateCommonDb 2023-08-21 03:04:39 +03:00
Reda Al Sulais
cc6f896b69 chore: remove ./* part from exports in parser/package.json
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-08-21 03:03:50 +03:00
Reda Al Sulais
83e47a7216 fix: use execFileSync instead of execSync in generateLangium 2023-08-21 02:54:30 +03:00
Reda Al Sulais
1d64549cce fix(mermaid): mark mermaid-parser dependecy with ^
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-08-21 02:54:13 +03:00
Reda Al Sulais
4ae361bd1f reorder packages/parser after packages/mermaid/src/vitepress 2023-08-20 18:45:05 +03:00
Reda Al Sulais
6502496a2c Merge remote-tracking branch 'upstream/next' into add-info-langium-parser 2023-08-20 18:41:32 +03:00
Sidharth Vinod
8678ceeb3c Merge pull request #4749 from Yokozuna59/remove-duplicate-dev-dependency
remove duplicate `@types/d3-scale` dev dependency
2023-08-20 15:38:50 +00:00
Reda Al Sulais
9cb62f4d2e remove duplicate @types/d3-scale dev dependency 2023-08-20 18:20:06 +03:00
Reda Al Sulais
6c0ef54e18 Merge branch 'next' into add-info-langium-parser 2023-08-20 18:01:37 +03:00
Sidharth Vinod
fd731c5ccd Merge branch 'develop' into next
* develop: (56 commits)
  chore: Add comments on redirectMaps
  remove `chart` from `pie.spec.ts` description
  Update docs
  change `defaultConfig` type to `RequiredDeep` and use it in `pieDb`
  use `DiagramStylesProvider` in `pieStyles.ts`
  remove `setConfig` and `resetConfig` in pie
  add `structuredClone` in pie `getConfig`
  cleanAndMerge pieConfig
  remove cleanClone
  feat: Add cleanAndMerge and tests
  chore: Rename utils.spec.ts
  move db assignment from `beforeEach` to `beforeAll`
  create `structuredCleanClone` helper function
  add more types to pieRenderer
  add `resetConfig` to `clear` in pieDb
  rename `reset` to `resetConfig`
  use `structedClone` in `pieDb`
  remove `PieDiagramConfig` and import generated one
  remove unnecessary lines in pie files
  remove unused `HTML` import in pieRenderer
  ...
2023-08-20 20:28:52 +05:30
Reda Al Sulais
cbe9490dc0 feat!: integrate info parser into mermaid package
BREAKING CHANGE: remove `showInfo` from `infoDb`.
2023-08-20 17:25:49 +03:00
Reda Al Sulais
82054bfabc chore: make parser as optional in ParserDefinition 2023-08-20 17:22:13 +03:00
Reda Al Sulais
963dd75c39 chore(parser): build parser package using esbuild and vite
Co-authored-by: Sidharth Vinod <sidharthv96@gmail.com>
2023-08-20 17:16:12 +03:00
Reda Al Sulais
1c24617f98 feat(parser): create info parser with exporting parser internals 2023-08-20 15:38:46 +03:00
Reda Al Sulais
1559c2ca21 feat(parser): create common directory for langium parsers 2023-08-20 15:36:02 +03:00
Reda Al Sulais
6141722b1f feat: create parser package in packages directory 2023-08-20 15:31:40 +03:00
Reda Al Sulais
222d8eed4e Merge remote-tracking branch 'upstream/develop' into next
Signed-off-by: Reda Al Sulais <u.yokozuna@gmail.com>
2023-08-19 16:20:13 +03:00
Sidharth Vinod
718d52a72c chore: Move liveReload code into script. 2023-08-17 14:30:47 +05:30
Sidharth Vinod
fe1a06271a Fix minify undefined 2023-08-17 12:55:25 +05:30
Sidharth Vinod
bd2370555b Merge branch 'develop' into next
* develop:
  chore: Move SVG import to comment.
  build docs
  Remove whitespace on empty line
  Documentation for #2509
2023-08-17 12:18:40 +05:30
Sidharth Vinod
86c9ee4e90 Merge pull request #4733 from mermaid-js/sidv/splitChunks
Split chunks into individual dirs
2023-08-17 06:47:23 +00:00
Sidharth Vinod
b26bcf1343 chore: Fix minify 2023-08-17 08:22:00 +05:30
Sidharth Vinod
5d5c6275f9 Merge branch 'develop' into next
* develop:
  Update all minor dependencies
  Update all patch dependencies
  make more `RectData` required and remove optional assignment
  use lineBreakRegex in `svgDrawCommon`
  fix svgDrawCommon import by adding `.js`
  add types to `svgDrawCommon.ts`
  convert `svgDrawCommon` to TS
2023-08-17 08:20:11 +05:30
Sidharth Vinod
9c1a47d1fc Merge pull request #4729 from mermaid-js/sidv/esbuildV11
Use ESBuild (replaces UMD with IIFE bundle)
2023-08-16 12:06:07 +00:00
Sidharth Vinod
13852b7f4e Fix import 2023-08-14 09:24:34 +05:30
Sidharth Vinod
4fd7a88a15 chore: Fix outfile names 2023-08-14 08:52:56 +05:30
Sidharth Vinod
5c2a6b5eb1 chore: Add analyzer comment 2023-08-14 08:37:02 +05:30
Sidharth Vinod
9cbebbb8a0 chore: Split chunks into folders 2023-08-14 08:35:49 +05:30
Sidharth Vinod
e26d987c4e chore: Split chunks into folders 2023-08-14 08:34:11 +05:30
Sidharth Vinod
53669efaf8 chore: Add defaultOptions to server 2023-08-14 08:30:51 +05:30
Sidharth Vinod
b68f45ef59 chore: Split chunks into folders 2023-08-14 08:27:14 +05:30
Sidharth Vinod
8f44de651b chore: IIFE to cSpell 2023-08-14 00:55:48 +05:30
Sidharth Vinod
2ede244da0 chore: Minor comments
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-08-14 00:55:48 +05:30
Sidharth Vinod
77a181978e chore: Replace Date.now with console.time
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-08-14 00:55:48 +05:30
Sidharth Vinod
170bbce0d3 chore: Build at start 2023-08-14 00:55:41 +05:30
Sidharth Vinod
fc99d9be41 chore: Add build times to live reload 2023-08-14 00:55:41 +05:30
Sidharth Vinod
9fb9bed806 chore: Add live-reload 2023-08-14 00:55:34 +05:30
Sidharth Vinod
01b2f80a95 chore: Remove @vitest/coverage-c8 2023-08-14 00:54:33 +05:30
Sidharth Vinod
da7ff777d1 chore: Add esbuild (Breaking change)
mermaid.min.js and mermaid.js will now be IIFE instead of UMD.
2023-08-14 00:52:45 +05:30
156 changed files with 3299 additions and 4114 deletions

25
.build/common.ts Normal file
View File

@@ -0,0 +1,25 @@
/**
* Shared common options for both ESBuild and Vite
*/
export const packageOptions = {
parser: {
name: 'mermaid-parser',
packageName: 'parser',
file: 'index.ts',
},
mermaid: {
name: 'mermaid',
packageName: 'mermaid',
file: 'mermaid.ts',
},
'mermaid-example-diagram': {
name: 'mermaid-example-diagram',
packageName: 'mermaid-example-diagram',
file: 'detector.ts',
},
'mermaid-zenuml': {
name: 'mermaid-zenuml',
packageName: 'mermaid-zenuml',
file: 'detector.ts',
},
} as const;

View File

@@ -0,0 +1,5 @@
import { generate } from 'langium-cli';
export async function generateLangium() {
await generate({ file: `./packages/parser/langium-config.json` });
}

123
.build/jsonSchema.ts Normal file
View File

@@ -0,0 +1,123 @@
import { load, JSON_SCHEMA } from 'js-yaml';
import assert from 'node:assert';
import Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
import type { MermaidConfig, BaseDiagramConfig } from '../packages/mermaid/src/config.type.js';
/**
* All of the keys in the mermaid config that have a mermaid diagram config.
*/
const MERMAID_CONFIG_DIAGRAM_KEYS = [
'flowchart',
'sequence',
'gantt',
'journey',
'class',
'state',
'er',
'pie',
'quadrantChart',
'xyChart',
'requirement',
'mindmap',
'timeline',
'gitGraph',
'c4',
'sankey',
] as const;
/**
* Generate default values from the JSON Schema.
*
* AJV does not support nested default values yet (or default values with $ref),
* so we need to manually find them (this may be fixed in ajv v9).
*
* @param mermaidConfigSchema - The Mermaid JSON Schema to use.
* @returns The default mermaid config object.
*/
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
const ajv = new Ajv2019({
useDefaults: true,
allowUnionTypes: true,
strict: true,
});
ajv.addKeyword({
keyword: 'meta:enum', // used by jsonschema2md
errors: false,
});
ajv.addKeyword({
keyword: 'tsType', // used by json-schema-to-typescript
errors: false,
});
// ajv currently doesn't support nested default values, see https://github.com/ajv-validator/ajv/issues/1718
// (may be fixed in v9) so we need to manually use sub-schemas
const mermaidDefaultConfig = {};
assert.ok(mermaidConfigSchema.$defs);
const baseDiagramConfig = mermaidConfigSchema.$defs.BaseDiagramConfig;
for (const key of MERMAID_CONFIG_DIAGRAM_KEYS) {
const subSchemaRef = mermaidConfigSchema.properties[key].$ref;
const [root, defs, defName] = subSchemaRef.split('/');
assert.strictEqual(root, '#');
assert.strictEqual(defs, '$defs');
const subSchema = {
$schema: mermaidConfigSchema.$schema,
$defs: mermaidConfigSchema.$defs,
...mermaidConfigSchema.$defs[defName],
} as JSONSchemaType<BaseDiagramConfig>;
const validate = ajv.compile(subSchema);
mermaidDefaultConfig[key] = {};
for (const required of subSchema.required ?? []) {
if (subSchema.properties[required] === undefined && baseDiagramConfig.properties[required]) {
mermaidDefaultConfig[key][required] = baseDiagramConfig.properties[required].default;
}
}
if (!validate(mermaidDefaultConfig[key])) {
throw new Error(
`schema for subconfig ${key} does not have valid defaults! Errors were ${JSON.stringify(
validate.errors,
undefined,
2
)}`
);
}
}
const validate = ajv.compile(mermaidConfigSchema);
if (!validate(mermaidDefaultConfig)) {
throw new Error(
`Mermaid config JSON Schema does not have valid defaults! Errors were ${JSON.stringify(
validate.errors,
undefined,
2
)}`
);
}
return mermaidDefaultConfig;
}
export const loadSchema = (src: string, filename: string): JSONSchemaType<MermaidConfig> => {
const jsonSchema = load(src, {
filename,
// only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
// e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
schema: JSON_SCHEMA,
}) as JSONSchemaType<MermaidConfig>;
return jsonSchema;
};
export const getDefaults = (schema: JSONSchemaType<MermaidConfig>) => {
return `export default ${JSON.stringify(generateDefaults(schema), undefined, 2)};`;
};
export const getSchema = (schema: JSONSchemaType<MermaidConfig>) => {
return `export default ${JSON.stringify(schema, undefined, 2)};`;
};

9
.build/langium-cli.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
declare module 'langium-cli' {
export interface GenerateOptions {
file?: string;
mode?: 'development' | 'production';
watch?: boolean;
}
export function generate(options: GenerateOptions): Promise<boolean>;
}

65
.esbuild/build.ts Normal file
View File

@@ -0,0 +1,65 @@
import { build } from 'esbuild';
import { mkdir, writeFile } from 'node:fs/promises';
import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
const shouldVisualize = process.argv.includes('--visualize');
const buildPackage = async (entryName: keyof typeof packageOptions) => {
const commonOptions = { ...defaultOptions, entryName } as const;
const buildConfigs = [
// package.mjs
{ ...commonOptions },
// package.min.mjs
{
...commonOptions,
minify: true,
metafile: shouldVisualize,
},
// package.core.mjs
{ ...commonOptions, core: true },
];
if (entryName === 'mermaid') {
const iifeOptions: MermaidBuildOptions = { ...commonOptions, format: 'iife' };
buildConfigs.push(
// mermaid.js
{ ...iifeOptions },
// mermaid.min.js
{ ...iifeOptions, minify: true, metafile: shouldVisualize }
);
}
const results = await Promise.all(buildConfigs.map((option) => build(getBuildConfig(option))));
if (shouldVisualize) {
for (const { metafile } of results) {
if (!metafile) {
continue;
}
const fileName = Object.keys(metafile.outputs)
.filter((file) => !file.includes('chunks') && file.endsWith('js'))[0]
.replace('dist/', '');
// Upload metafile into https://esbuild.github.io/analyze/
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));
}
}
};
const handler = (e) => {
console.error(e);
process.exit(1);
};
const main = async () => {
await generateLangium();
await mkdir('stats').catch(() => {});
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
// it should build `parser` before `mermaid` because it's a dependecy
for (const pkg of packageNames) {
await buildPackage(pkg).catch(handler);
}
};
void main();

15
.esbuild/jisonPlugin.ts Normal file
View File

@@ -0,0 +1,15 @@
import { readFile } from 'node:fs/promises';
import { transformJison } from '../.build/jisonTransformer.js';
import { Plugin } from 'esbuild';
export const jisonPlugin: Plugin = {
name: 'jison',
setup(build) {
build.onLoad({ filter: /\.jison$/ }, async (args) => {
// Load the file from the file system
const source = await readFile(args.path, 'utf8');
const contents = transformJison(source);
return { contents, warnings: [] };
});
},
};

View File

@@ -0,0 +1,35 @@
import type { JSONSchemaType } from 'ajv/dist/2019.js';
import type { MermaidConfig } from '../packages/mermaid/src/config.type.js';
import { readFile } from 'node:fs/promises';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
/**
* ESBuild plugin that handles JSON Schemas saved as a `.schema.yaml` file.
*
* Use `my-example.schema.yaml?only-defaults=true` to only load the default values.
*/
export const jsonSchemaPlugin = {
name: 'json-schema-plugin',
setup(build) {
let schema: JSONSchemaType<MermaidConfig> | undefined = undefined;
let content = '';
build.onLoad({ filter: /config\.schema\.yaml$/ }, async (args) => {
// Load the file from the file system
const source = await readFile(args.path, 'utf8');
const resolvedSchema: JSONSchemaType<MermaidConfig> =
content === source && schema ? schema : loadSchema(source, args.path);
if (content !== source) {
content = source;
schema = resolvedSchema;
}
const contents = args.suffix.includes('only-defaults')
? getDefaults(resolvedSchema)
: getSchema(resolvedSchema);
return { contents, warnings: [] };
});
},
};
export default jsonSchemaPlugin;

116
.esbuild/server.ts Normal file
View File

@@ -0,0 +1,116 @@
import express from 'express';
import type { NextFunction, Request, Response } from 'express';
import cors from 'cors';
import { getBuildConfig, defaultOptions } from './util.js';
import { context } from 'esbuild';
import chokidar from 'chokidar';
import { generateLangium } from '../.build/generateLangium.js';
const parserCtx = await context(
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'parser' })
);
const mermaidCtx = await context(
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' })
);
const mermaidIIFECtx = await context(
getBuildConfig({
...defaultOptions,
minify: false,
core: false,
entryName: 'mermaid',
format: 'iife',
})
);
const externalCtx = await context(
getBuildConfig({
...defaultOptions,
minify: false,
core: false,
entryName: 'mermaid-example-diagram',
})
);
const zenumlCtx = await context(
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' })
);
const contexts = [parserCtx, mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
const rebuildAll = async () => {
console.time('Rebuild time');
await Promise.all(contexts.map((ctx) => ctx.rebuild()));
console.timeEnd('Rebuild time');
};
let clients: { id: number; response: Response }[] = [];
function eventsHandler(request: Request, response: Response, next: NextFunction) {
const headers = {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
'Cache-Control': 'no-cache',
};
response.writeHead(200, headers);
const clientId = Date.now();
clients.push({
id: clientId,
response,
});
request.on('close', () => {
clients = clients.filter((client) => client.id !== clientId);
});
}
let timeoutId: NodeJS.Timeout | undefined = undefined;
/**
* Debounce file change events to avoid rebuilding multiple times.
*/
function handleFileChange() {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(async () => {
await rebuildAll();
sendEventsToAll();
timeoutId = undefined;
}, 100);
}
function sendEventsToAll() {
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
}
async function createServer() {
await generateLangium();
handleFileChange();
const app = express();
chokidar
.watch('**/src/**/*.{js,ts,langium,yaml,json}', {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
.on('all', async (event, path) => {
// Ignore other events.
if (!['add', 'change'].includes(event)) {
return;
}
if (/\.langium$/.test(path)) {
await generateLangium();
}
console.log(`${path} changed. Rebuilding...`);
handleFileChange();
});
app.use(cors());
app.get('/events', eventsHandler);
app.use(express.static('./packages/parser/dist'));
app.use(express.static('./packages/mermaid/dist'));
app.use(express.static('./packages/mermaid-zenuml/dist'));
app.use(express.static('./packages/mermaid-example-diagram/dist'));
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));
app.listen(9000, () => {
console.log(`Listening on http://localhost:9000`);
});
}
createServer();

98
.esbuild/util.ts Normal file
View File

@@ -0,0 +1,98 @@
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import type { BuildOptions } from 'esbuild';
import { readFileSync } from 'fs';
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
import { packageOptions } from '../.build/common.js';
import { jisonPlugin } from './jisonPlugin.js';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
export interface MermaidBuildOptions {
minify: boolean;
core: boolean;
metafile: boolean;
format: 'esm' | 'iife';
entryName: keyof typeof packageOptions;
}
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName'> = {
minify: false,
metafile: false,
core: false,
format: 'esm',
} as const;
const buildOptions = (override: BuildOptions): BuildOptions => {
return {
bundle: true,
minify: true,
keepNames: true,
platform: 'browser',
tsconfig: 'tsconfig.json',
resolveExtensions: ['.ts', '.js', '.json', '.jison', '.yaml'],
external: ['require', 'fs', 'path'],
outdir: 'dist',
plugins: [jisonPlugin, jsonSchemaPlugin],
sourcemap: 'external',
...override,
};
};
const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOptions) => {
if (core) {
fileName += '.core';
} else if (format === 'esm') {
fileName += '.esm';
}
if (minify) {
fileName += '.min';
}
return fileName;
};
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const { core, entryName, metafile, format, minify } = options;
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
const outFileName = getFileName(name, options);
let output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[outFileName]: `src/${file}`,
},
metafile,
minify,
logLevel: 'info',
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
});
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies
external.push(...Object.keys(dependencies));
output.external = external;
}
if (format === 'iife') {
output.format = 'iife';
output.splitting = false;
output.globalName = '__esbuild_esm_mermaid';
// Workaround for removing the .default access in esbuild IIFE.
// https://github.com/mermaid-js/mermaid/pull/4109#discussion_r1292317396
output.footer = {
js: 'globalThis.mermaid = globalThis.__esbuild_esm_mermaid.default;',
};
output.outExtension = { '.js': '.js' };
} else {
output.format = 'esm';
output.splitting = true;
output.outExtension = { '.js': '.mjs' };
}
return output;
};

View File

@@ -6,3 +6,6 @@ cypress/plugins/index.js
coverage coverage
*.json *.json
node_modules node_modules
# autogenereated by langium-cli
generated/

View File

@@ -28,7 +28,7 @@ jobs:
- if: ${{ ! env.USE_APPLI }} - if: ${{ ! env.USE_APPLI }}
name: Warn if not using Applitools name: Warn if not using Applitools
run: | run: |
echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run." echo "::error,title=Not using Applitols::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
- uses: actions/checkout@v4 - uses: actions/checkout@v4

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.9.1 uses: lycheeverse/lychee-action@v1.8.0
with: with:
args: >- args: >-
--config .github/lychee.toml --config .github/lychee.toml

View File

@@ -3,7 +3,7 @@ name: Draft Release
on: on:
push: push:
branches: branches:
- master - develop
permissions: permissions:
contents: read contents: read

4
.gitignore vendored
View File

@@ -46,3 +46,7 @@ stats/
demos/dev/** demos/dev/**
!/demos/dev/example.html !/demos/dev/example.html
!/demos/dev/reload.js
# autogenereated by langium-cli
generated/

View File

@@ -6,6 +6,6 @@ export default {
// https://prettier.io/docs/en/cli.html#--cache // https://prettier.io/docs/en/cli.html#--cache
'prettier --write', 'prettier --write',
], ],
'cSpell.json': ['tsx scripts/fixCSpell.ts'], 'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'], '**/*.jison': ['pnpm -w run lint:jison'],
}; };

1
.npmrc
View File

@@ -1,3 +1,2 @@
registry=https://registry.npmjs.org
auto-install-peers=true auto-install-peers=true
strict-peer-dependencies=false strict-peer-dependencies=false

View File

@@ -10,6 +10,6 @@ stats
.nyc_output .nyc_output
# Autogenerated by `pnpm run --filter mermaid types:build-config` # Autogenerated by `pnpm run --filter mermaid types:build-config`
packages/mermaid/src/config.type.ts packages/mermaid/src/config.type.ts
# Ignore the files creates in /demos/dev except for example.html
demos/dev/** # autogenereated by langium-cli
!/demos/dev/example.html generated/

View File

@@ -3,11 +3,12 @@ import { resolve } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js'; import jisonPlugin from './jisonPlugin.js';
import jsonSchemaPlugin from './jsonSchemaPlugin.js'; import jsonSchemaPlugin from './jsonSchemaPlugin.js';
import { readFileSync } from 'fs';
import typescript from '@rollup/plugin-typescript'; import typescript from '@rollup/plugin-typescript';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js'; import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js';
import istanbul from 'vite-plugin-istanbul'; import istanbul from 'vite-plugin-istanbul';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
const visualize = process.argv.includes('--visualize'); const visualize = process.argv.includes('--visualize');
const watch = process.argv.includes('--watch'); const watch = process.argv.includes('--watch');
@@ -36,24 +37,6 @@ const visualizerOptions = (packageName: string, core = false): PluginOption[] =>
); );
}; };
const packageOptions = {
mermaid: {
name: 'mermaid',
packageName: 'mermaid',
file: 'mermaid.ts',
},
'mermaid-example-diagram': {
name: 'mermaid-example-diagram',
packageName: 'mermaid-example-diagram',
file: 'detector.ts',
},
'mermaid-zenuml': {
name: 'mermaid-zenuml',
packageName: 'mermaid-zenuml',
file: 'detector.ts',
},
};
interface BuildOptions { interface BuildOptions {
minify: boolean | 'esbuild'; minify: boolean | 'esbuild';
core?: boolean; core?: boolean;
@@ -72,34 +55,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
sourcemap, sourcemap,
entryFileNames: `${name}.esm${minify ? '.min' : ''}.mjs`, entryFileNames: `${name}.esm${minify ? '.min' : ''}.mjs`,
}, },
{
name,
format: 'umd',
sourcemap,
entryFileNames: `${name}${minify ? '.min' : ''}.js`,
},
]; ];
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies
// Adapted from the RegEx used by `rollup-plugin-node`
external.push(new RegExp('^(?:' + Object.keys(dependencies).join('|') + ')(?:/.+)?$'));
// This needs to be an array. Otherwise vite will build esm & umd with same name and overwrite esm with umd.
output = [
{
name,
format: 'esm',
sourcemap,
entryFileNames: `${name}.core.mjs`,
},
];
}
const config: InlineConfig = { const config: InlineConfig = {
configFile: false, configFile: false,
build: { build: {
@@ -117,9 +74,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
output, output,
}, },
}, },
define: {
'import.meta.vitest': 'undefined',
},
resolve: { resolve: {
extensions: [], extensions: [],
}, },
@@ -129,7 +83,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
// @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite // @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
typescript({ compilerOptions: { declaration: false } }), typescript({ compilerOptions: { declaration: false } }),
istanbul({ istanbul({
exclude: ['node_modules', 'test/', '__mocks__'], exclude: ['node_modules', 'test/', '__mocks__', 'generated'],
extension: ['.js', '.ts'], extension: ['.js', '.ts'],
requireEnv: true, requireEnv: true,
forceBuildInstrument: coverage, forceBuildInstrument: coverage,
@@ -149,24 +103,28 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
const buildPackage = async (entryName: keyof typeof packageOptions) => { const buildPackage = async (entryName: keyof typeof packageOptions) => {
await build(getBuildConfig({ minify: false, entryName })); await build(getBuildConfig({ minify: false, entryName }));
await build(getBuildConfig({ minify: 'esbuild', entryName }));
await build(getBuildConfig({ minify: false, core: true, entryName }));
}; };
const main = async () => { const main = async () => {
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[]; const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
for (const pkg of packageNames.filter((pkg) => !mermaidOnly || pkg === 'mermaid')) { for (const pkg of packageNames.filter(
(pkg) => !mermaidOnly || pkg === 'mermaid' || pkg === 'parser'
)) {
await buildPackage(pkg); await buildPackage(pkg);
} }
}; };
await generateLangium();
if (watch) { if (watch) {
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' })); build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
if (!mermaidOnly) { if (!mermaidOnly) {
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' })); build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' })); build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
} }
} else if (visualize) { } else if (visualize) {
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' })); await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' })); await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' }));
} else { } else {

View File

@@ -1,10 +1,10 @@
import { transformJison } from './jisonTransformer.js'; import { transformJison } from '../.build/jisonTransformer.js';
const fileRegex = /\.(jison)$/; const fileRegex = /\.(jison)$/;
export default function jison() { export default function jison() {
return { return {
name: 'jison', name: 'jison',
transform(src: string, id: string) { transform(src: string, id: string) {
if (fileRegex.test(id)) { if (fileRegex.test(id)) {
return { return {

View File

@@ -1,109 +1,5 @@
import { load, JSON_SCHEMA } from 'js-yaml';
import assert from 'node:assert';
import Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
import { PluginOption } from 'vite'; import { PluginOption } from 'vite';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
import type { MermaidConfig, BaseDiagramConfig } from '../packages/mermaid/src/config.type.js';
/**
* All of the keys in the mermaid config that have a mermaid diagram config.
*/
const MERMAID_CONFIG_DIAGRAM_KEYS = [
'flowchart',
'sequence',
'gantt',
'journey',
'class',
'state',
'er',
'pie',
'quadrantChart',
'xyChart',
'requirement',
'mindmap',
'timeline',
'gitGraph',
'c4',
'sankey',
] as const;
/**
* Generate default values from the JSON Schema.
*
* AJV does not support nested default values yet (or default values with $ref),
* so we need to manually find them (this may be fixed in ajv v9).
*
* @param mermaidConfigSchema - The Mermaid JSON Schema to use.
* @returns The default mermaid config object.
*/
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
const ajv = new Ajv2019({
useDefaults: true,
allowUnionTypes: true,
strict: true,
});
ajv.addKeyword({
keyword: 'meta:enum', // used by jsonschema2md
errors: false,
});
ajv.addKeyword({
keyword: 'tsType', // used by json-schema-to-typescript
errors: false,
});
// ajv currently doesn't support nested default values, see https://github.com/ajv-validator/ajv/issues/1718
// (may be fixed in v9) so we need to manually use sub-schemas
const mermaidDefaultConfig = {};
assert.ok(mermaidConfigSchema.$defs);
const baseDiagramConfig = mermaidConfigSchema.$defs.BaseDiagramConfig;
for (const key of MERMAID_CONFIG_DIAGRAM_KEYS) {
const subSchemaRef = mermaidConfigSchema.properties[key].$ref;
const [root, defs, defName] = subSchemaRef.split('/');
assert.strictEqual(root, '#');
assert.strictEqual(defs, '$defs');
const subSchema = {
$schema: mermaidConfigSchema.$schema,
$defs: mermaidConfigSchema.$defs,
...mermaidConfigSchema.$defs[defName],
} as JSONSchemaType<BaseDiagramConfig>;
const validate = ajv.compile(subSchema);
mermaidDefaultConfig[key] = {};
for (const required of subSchema.required ?? []) {
if (subSchema.properties[required] === undefined && baseDiagramConfig.properties[required]) {
mermaidDefaultConfig[key][required] = baseDiagramConfig.properties[required].default;
}
}
if (!validate(mermaidDefaultConfig[key])) {
throw new Error(
`schema for subconfig ${key} does not have valid defaults! Errors were ${JSON.stringify(
validate.errors,
undefined,
2
)}`
);
}
}
const validate = ajv.compile(mermaidConfigSchema);
if (!validate(mermaidDefaultConfig)) {
throw new Error(
`Mermaid config JSON Schema does not have valid defaults! Errors were ${JSON.stringify(
validate.errors,
undefined,
2
)}`
);
}
return mermaidDefaultConfig;
}
/** /**
* Vite plugin that handles JSON Schemas saved as a `.schema.yaml` file. * Vite plugin that handles JSON Schemas saved as a `.schema.yaml` file.
@@ -120,32 +16,13 @@ export default function jsonSchemaPlugin(): PluginOption {
return; return;
} }
if (idAsUrl.searchParams.get('only-defaults')) { const jsonSchema = loadSchema(src, idAsUrl.pathname);
const jsonSchema = load(src, {
filename: idAsUrl.pathname,
// only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
// e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
schema: JSON_SCHEMA,
}) as JSONSchemaType<MermaidConfig>;
return { return {
code: `export default ${JSON.stringify(generateDefaults(jsonSchema), undefined, 2)};`, code: idAsUrl.searchParams.get('only-defaults')
? getDefaults(jsonSchema)
: getSchema(jsonSchema),
map: null, // no source map map: null, // no source map
}; };
} else {
return {
code: `export default ${JSON.stringify(
load(src, {
filename: idAsUrl.pathname,
// only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
// e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
schema: JSON_SCHEMA,
}),
undefined,
2
)};`,
map: null, // provide source map if available
};
}
}, },
}; };
} }

View File

@@ -14,6 +14,7 @@ async function createServer() {
}); });
app.use(cors()); app.use(cors());
app.use(express.static('./packages/parser/dist'));
app.use(express.static('./packages/mermaid/dist')); app.use(express.static('./packages/mermaid/dist'));
app.use(express.static('./packages/mermaid-zenuml/dist')); app.use(express.static('./packages/mermaid-zenuml/dist'));
app.use(express.static('./packages/mermaid-example-diagram/dist')); app.use(express.static('./packages/mermaid-example-diagram/dist'));

3
.vscode/launch.json vendored
View File

@@ -18,8 +18,7 @@
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"args": ["scripts/docs.cli.mts"], "args": ["scripts/docs.cli.mts"],
// we'll need to change this to --import in Node.JS v20.6.0 and up "runtimeArgs": ["--loader", "ts-node/esm"],
"runtimeArgs": ["--loader", "tsx/esm"],
"cwd": "${workspaceRoot}/packages/mermaid", "cwd": "${workspaceRoot}/packages/mermaid",
"skipFiles": ["<node_internals>/**", "**/node_modules/**"], "skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"smartStep": true, "smartStep": true,

View File

@@ -26,7 +26,6 @@
"città", "città",
"classdef", "classdef",
"codedoc", "codedoc",
"codemia",
"colour", "colour",
"commitlint", "commitlint",
"cpettitt", "cpettitt",
@@ -62,6 +61,7 @@
"gzipped", "gzipped",
"huynh", "huynh",
"huynhicode", "huynhicode",
"iife",
"inkdrop", "inkdrop",
"jaoude", "jaoude",
"jgreywolf", "jgreywolf",
@@ -75,6 +75,7 @@
"knut", "knut",
"knutsveidqvist", "knutsveidqvist",
"laganeckas", "laganeckas",
"langium",
"linetype", "linetype",
"lintstagedrc", "lintstagedrc",
"logmsg", "logmsg",
@@ -86,6 +87,7 @@
"mdbook", "mdbook",
"mermaidjs", "mermaidjs",
"mermerd", "mermerd",
"metafile",
"mindaugas", "mindaugas",
"mindmap", "mindmap",
"mindmaps", "mindmaps",
@@ -99,6 +101,7 @@
"nirname", "nirname",
"npmjs", "npmjs",
"orlandoni", "orlandoni",
"outdir",
"pathe", "pathe",
"pbrolin", "pbrolin",
"phpbb", "phpbb",

View File

@@ -10,7 +10,7 @@ interface CypressConfig {
type CypressMermaidConfig = MermaidConfig & CypressConfig; type CypressMermaidConfig = MermaidConfig & CypressConfig;
interface CodeObject { interface CodeObject {
code: string | string[]; code: string;
mermaid: CypressMermaidConfig; mermaid: CypressMermaidConfig;
} }
@@ -25,7 +25,7 @@ const batchId: string =
: Cypress.env('CYPRESS_COMMIT') || Date.now().toString()); : Cypress.env('CYPRESS_COMMIT') || Date.now().toString());
export const mermaidUrl = ( export const mermaidUrl = (
graphStr: string | string[], graphStr: string,
options: CypressMermaidConfig, options: CypressMermaidConfig,
api: boolean api: boolean
): string => { ): string => {
@@ -82,7 +82,7 @@ export const urlSnapshotTest = (
}; };
export const renderGraph = ( export const renderGraph = (
graphStr: string | string[], graphStr: string,
options: CypressMermaidConfig = {}, options: CypressMermaidConfig = {},
api = false api = false
): void => { ): void => {

View File

@@ -0,0 +1,11 @@
describe('IIFE', () => {
beforeEach(() => {
cy.visit('http://localhost:9000/iife.html');
});
it('should render when using mermaid.min.js', () => {
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
cy.get('#d2').should('contain', 'Hello');
});
});

View File

@@ -1,16 +0,0 @@
describe('Sequencediagram', () => {
it('should render a simple sequence diagrams', () => {
const url = 'http://localhost:9000/webpackUsage.html';
cy.visit(url);
cy.get('body').find('svg').should('have.length', 1);
});
it('should handle html escapings properly', () => {
const url = 'http://localhost:9000/webpackUsage.html?test-html-escaping=true';
cy.visit(url);
cy.get('body').find('svg').should('have.length', 1);
cy.get('g.label > foreignobject > div').should('not.contain.text', '<b>');
});
});

View File

@@ -571,14 +571,4 @@ class C13["With Città foreign language"]
{ logLevel: 1, flowchart: { htmlLabels: false } } { logLevel: 1, flowchart: { htmlLabels: false } }
); );
}); });
it('should render a simple class diagram with style definition', () => {
imgSnapshotTest(
`
classDiagram-v2
class Class10
style Class10 fill:#f9f,stroke:#333,stroke-width:4px
`,
{ logLevel: 1, flowchart: { htmlLabels: false } }
);
});
}); });

View File

@@ -729,18 +729,6 @@ A ~~~ B
{} {}
); );
}); });
it('5064: Should render when subgraph child has links to outside node and subgraph', () => {
imgSnapshotTest(
`flowchart TB
Out --> In
subgraph Sub
In
end
Sub --> In`
);
});
describe('Markdown strings flowchart (#4220)', () => { describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => { describe('html labels', () => {
it('With styling and classes', () => { it('With styling and classes', () => {

View File

@@ -701,129 +701,4 @@ gitGraph TB:
{} {}
); );
}); });
it('34: should render a simple gitgraph with two branches from same commit', () => {
imgSnapshotTest(
`gitGraph
commit id:"1-abcdefg"
commit id:"2-abcdefg"
branch feature-001
commit id:"3-abcdefg"
commit id:"4-abcdefg"
checkout main
branch feature-002
commit id:"5-abcdefg"
checkout feature-001
merge feature-002
`,
{}
);
});
it('35: should render a simple gitgraph with two branches from same commit | Vertical Branch', () => {
imgSnapshotTest(
`gitGraph TB:
commit id:"1-abcdefg"
commit id:"2-abcdefg"
branch feature-001
commit id:"3-abcdefg"
commit id:"4-abcdefg"
checkout main
branch feature-002
commit id:"5-abcdefg"
checkout feature-001
merge feature-002
`,
{}
);
});
it('36: should render GitGraph with branch that is not used immediately', () => {
imgSnapshotTest(
`gitGraph LR:
commit id:"1-abcdefg"
branch x
checkout main
commit id:"2-abcdefg"
checkout x
commit id:"3-abcdefg"
checkout main
merge x
`,
{}
);
});
it('37: should render GitGraph with branch that is not used immediately | Vertical Branch', () => {
imgSnapshotTest(
`gitGraph TB:
commit id:"1-abcdefg"
branch x
checkout main
commit id:"2-abcdefg"
checkout x
commit id:"3-abcdefg"
checkout main
merge x
`,
{}
);
});
it('38: should render GitGraph with branch and sub-branch neither of which used immediately', () => {
imgSnapshotTest(
`gitGraph LR:
commit id:"1-abcdefg"
branch x
checkout main
commit id:"2-abcdefg"
checkout x
commit id:"3-abcdefg"
checkout main
merge x
checkout x
branch y
checkout x
commit id:"4-abcdefg"
checkout y
commit id:"5-abcdefg"
checkout x
merge y
`,
{}
);
});
it('39: should render GitGraph with branch and sub-branch neither of which used immediately | Vertical Branch', () => {
imgSnapshotTest(
`gitGraph TB:
commit id:"1-abcdefg"
branch x
checkout main
commit id:"2-abcdefg"
checkout x
commit id:"3-abcdefg"
checkout main
merge x
checkout x
branch y
checkout x
commit id:"4-abcdefg"
checkout y
commit id:"5-abcdefg"
checkout x
merge y
`,
{}
);
});
it('40: should render a simple gitgraph with cherry pick merge commit', () => {
imgSnapshotTest(
`gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
cherry-pick id: "M" parent:"B"`
);
});
}); });

View File

@@ -44,7 +44,7 @@ describe('pie chart', () => {
const style = svg.attr('style'); const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/); expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join('')); const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(590, 600); // depends on installed fonts: 596.2 on my PC, 597.5 on CI expect(maxWidthValue).to.eq(984);
}); });
}); });
@@ -59,7 +59,7 @@ describe('pie chart', () => {
); );
cy.get('svg').should((svg) => { cy.get('svg').should((svg) => {
const width = parseFloat(svg.attr('width')); const width = parseFloat(svg.attr('width'));
expect(width).to.be.within(590, 600); // depends on installed fonts: 596.2 on my PC, 597.5 on CI expect(width).to.eq(984);
expect(svg).to.not.have.attr('style'); expect(svg).to.not.have.attr('style');
}); });
}); });

View File

@@ -930,36 +930,4 @@ context('Sequence diagram', () => {
}); });
}); });
}); });
context('render after error', () => {
it('should render diagram after fixing destroy participant error', () => {
cy.on('uncaught:exception', (err) => {
return false;
});
renderGraph([
`sequenceDiagram
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bo
Bob->>Alice: I agree`,
`sequenceDiagram
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bob
Bob->>Alice: I agree`,
]);
});
});
}); });

View File

@@ -1,7 +1,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<script src="./viewer.js" type="module"></script> <script type="module" src="./viewer.js"></script>
<link <link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap" href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet" rel="stylesheet"

View File

@@ -11,8 +11,7 @@ example-diagram
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> --> <!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
<!-- <script type="module" src="./external-diagrams-mindmap.mjs" /> --> <!-- <script type="module" src="./external-diagrams-mindmap.mjs" /> -->
<script type="module"> <script type="module">
import exampleDiagram from '../../packages/mermaid-example-diagram/dist/mermaid-example-diagram.core.mjs'; import exampleDiagram from './mermaid-example-diagram.esm.mjs';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from './mermaid.esm.mjs'; import mermaid from './mermaid.esm.mjs';
await mermaid.registerExternalDiagrams([exampleDiagram]); await mermaid.registerExternalDiagrams([exampleDiagram]);

View File

@@ -0,0 +1,29 @@
<html>
<body>
<pre id="diagram" class="mermaid">
graph TB
a --> b
a --> c
b --> d
c --> d
</pre>
<div id="d2"></div>
<script src="/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
});
const value = `graph TD\nHello --> World`;
const el = document.getElementById('d2');
mermaid.render('did', value).then(({ svg }) => {
console.log(svg);
el.innerHTML = svg;
if (window.Cypress) {
window.rendered = true;
}
});
</script>
</body>
</html>

View File

@@ -17,20 +17,20 @@
graph TB graph TB
Function-->URL Function-->URL
click Function clickByFlow "Add a div" click Function clickByFlow "Add a div"
click URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>" click URL "http://localhost:9000/info.html" "Visit <strong>mermaid docs</strong>"
</pre> </pre>
<pre id="FirstLine" class="mermaid2"> <pre id="FirstLine" class="mermaid2">
graph TB graph TB
1Function-->2URL 1Function-->2URL
click 1Function clickByFlow "Add a div" click 1Function clickByFlow "Add a div"
click 2URL "http://localhost:9000/webpackUsage.html" "Visit <strong>mermaid docs</strong>" click 2URL "http://localhost:9000/info.html" "Visit <strong>mermaid docs</strong>"
</pre> </pre>
<pre id="FirstLine" class="mermaid2"> <pre id="FirstLine" class="mermaid2">
classDiagram classDiagram
class Test class Test
class ShapeLink class ShapeLink
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link" link ShapeLink "http://localhost:9000/info.html" "This is a tooltip for a link"
class ShapeCallback class ShapeCallback
callback ShapeCallback "clickByClass" "This is a tooltip for a callback" callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
</pre> </pre>
@@ -42,7 +42,7 @@
<pre id="FirstLine" class="mermaid"> <pre id="FirstLine" class="mermaid">
classDiagram-v2 classDiagram-v2
class ShapeLink class ShapeLink
link ShapeLink "http://localhost:9000/webpackUsage.html" "This is a tooltip for a link" link ShapeLink "http://localhost:9000/info.html" "This is a tooltip for a link"
</pre> </pre>
</div> </div>
@@ -77,7 +77,7 @@
Calling a Callback (look at the console log) :cl2, after cl1, 3d Calling a Callback (look at the console log) :cl2, after cl1, 3d
Calling a Callback with args :cl3, after cl1, 3d Calling a Callback with args :cl3, after cl1, 3d
click cl1 href "http://localhost:9000/webpackUsage.html" click cl1 href "http://localhost:9000/info.html"
click cl2 call clickByGantt() click cl2 call clickByGantt()
click cl3 call clickByGantt("test1", test2, test3) click cl3 call clickByGantt("test1", test2, test3)
@@ -102,9 +102,15 @@
div.className = 'created-by-gant-click'; div.className = 'created-by-gant-click';
div.style = 'padding: 20px; background: green; color: white;'; div.style = 'padding: 20px; background: green; color: white;';
div.innerText = 'Clicked By Gant'; div.innerText = 'Clicked By Gant';
if (arg1) div.innerText += ' ' + arg1; if (arg1) {
if (arg2) div.innerText += ' ' + arg2; div.innerText += ' ' + arg1;
if (arg3) div.innerText += ' ' + arg3; }
if (arg2) {
div.innerText += ' ' + arg2;
}
if (arg3) {
div.innerText += ' ' + arg3;
}
document.getElementsByTagName('body')[0].appendChild(div); document.getElementsByTagName('body')[0].appendChild(div);
} }

View File

@@ -1,6 +1,6 @@
import mermaid2 from './mermaid.esm.mjs'; import mermaid from './mermaid.esm.mjs';
import externalExample from '../../packages/mermaid-example-diagram/dist/mermaid-example-diagram.core.mjs'; import externalExample from './mermaid-example-diagram.esm.mjs';
import zenUml from '../../packages/mermaid-zenuml/dist/mermaid-zenuml.core.mjs'; import zenUml from './mermaid-zenuml.esm.mjs';
function b64ToUtf8(str) { function b64ToUtf8(str) {
return decodeURIComponent(escape(window.atob(str))); return decodeURIComponent(escape(window.atob(str)));
@@ -45,9 +45,9 @@ const contentLoaded = async function () {
document.getElementsByTagName('body')[0].appendChild(div); document.getElementsByTagName('body')[0].appendChild(div);
} }
await mermaid2.registerExternalDiagrams([externalExample, zenUml]); await mermaid.registerExternalDiagrams([externalExample, zenUml]);
mermaid2.initialize(graphObj.mermaid); mermaid.initialize(graphObj.mermaid);
await mermaid2.run(); await mermaid.run();
} }
}; };
@@ -95,18 +95,14 @@ const contentLoadedApi = async function () {
divs[i] = div; divs[i] = div;
} }
const defaultE2eCnf = { theme: 'forest' }; const defaultE2eCnf = { theme: 'forest', startOnLoad: false };
const cnf = merge(defaultE2eCnf, graphObj.mermaid); const cnf = merge(defaultE2eCnf, graphObj.mermaid);
mermaid2.initialize(cnf); mermaid.initialize(cnf);
for (let i = 0; i < numCodes; i++) { for (let i = 0; i < numCodes; i++) {
const { svg, bindFunctions } = await mermaid2.render( const { svg, bindFunctions } = await mermaid.render('newid' + i, graphObj.code[i], divs[i]);
'newid' + i,
graphObj.code[i],
divs[i]
);
div.innerHTML = svg; div.innerHTML = svg;
bindFunctions(div); bindFunctions(div);
} }
@@ -114,18 +110,21 @@ const contentLoadedApi = async function () {
const div = document.createElement('div'); const div = document.createElement('div');
div.id = 'block'; div.id = 'block';
div.className = 'mermaid'; div.className = 'mermaid';
console.warn('graphObj.mermaid', graphObj.mermaid); console.warn('graphObj', graphObj);
document.getElementsByTagName('body')[0].appendChild(div); document.getElementsByTagName('body')[0].appendChild(div);
mermaid2.initialize(graphObj.mermaid); mermaid.initialize(graphObj.mermaid);
const { svg, bindFunctions } = await mermaid.render('newid', graphObj.code, div);
const { svg, bindFunctions } = await mermaid2.render('newid', graphObj.code, div);
div.innerHTML = svg; div.innerHTML = svg;
console.log(div.innerHTML);
bindFunctions(div); bindFunctions(div);
} }
} }
}; };
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
mermaid.initialize({
startOnLoad: false,
});
/*! /*!
* Wait for document loaded before starting the execution * Wait for document loaded before starting the execution
*/ */

View File

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style>
/* .mermaid {
font-family: "trebuchet ms", verdana, arial;;
} */
/* .mermaid {
font-family: 'arial';
} */
</style>
</head>
<body>
<div id="graph-to-be"></div>
<script type="module" charset="utf-8">
import './bundle-test.js';
</script>
</body>
</html>

View File

@@ -1,6 +1,5 @@
<html> <html>
<head> <head>
<script src="./viewer.js" type="module"></script>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<style> <style>
.malware { .malware {
@@ -33,12 +32,6 @@
</script> </script>
</head> </head>
<body> <body>
<script type="module"> <script type="module" src="./viewer.js"></script>
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
startOnLoad: false,
useMaxWidth: true,
});
</script>
</body> </body>
</html> </html>

View File

@@ -5,6 +5,8 @@
<title>Mermaid development page</title> <title>Mermaid development page</title>
</head> </head>
<body> <body>
<pre class="mermaid">info</pre>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
graph TB graph TB
a --> b a --> b
@@ -30,5 +32,7 @@ graph TB
console.log(svg); console.log(svg);
el.innerHTML = svg; el.innerHTML = svg;
</script> </script>
<script src="/dev/reload.js"></script>
</body> </body>
</html> </html>

22
demos/dev/reload.js Normal file
View File

@@ -0,0 +1,22 @@
// Connect to the server and reload the page if the server sends a reload message
const connectToEvents = () => {
const events = new EventSource('/events');
const loadTime = Date.now();
events.onmessage = (event) => {
const time = JSON.parse(event.data);
if (time && time > loadTime) {
location.reload();
}
};
events.onerror = (error) => {
console.error(error);
events.close();
// Try to reconnect after 1 second in case of errors
setTimeout(connectToEvents, 1000);
};
events.onopen = () => {
console.log('Connected to live reload server');
};
};
setTimeout(connectToEvents, 500);

View File

@@ -14,364 +14,30 @@
</head> </head>
<body> <body>
<h1>Git graph demo</h1> <h1>Git diagram demo</h1>
<h2>Simple "branch and merge" graph</h2>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: Simple "branch and merge" (left-to-right) title: Simple Git diagram
--- ---
gitGraph LR: gitGraph:
options
{
"nodeSpacing": 50,
"nodeRadius": 5
}
end
branch master
commit commit
branch newbranch branch newbranch
checkout newbranch checkout newbranch
commit commit
checkout main
merge newbranch
</pre>
<pre class="mermaid">
---
title: Simple "branch and merge" (top-to-bottom)
---
gitGraph TB:
commit commit
branch newbranch checkout master
checkout newbranch
commit commit
checkout main
merge newbranch
</pre>
<h2>Continuous development graph</h2>
<pre class="mermaid">
---
title: Continuous development (left-to-right)
---
gitGraph LR:
commit
branch develop
checkout develop
commit
checkout main
merge develop
checkout develop
commit
checkout main
merge develop
</pre>
<pre class="mermaid">
---
title: Continuous development (top-to-bottom)
---
gitGraph TB:
commit
branch develop
checkout develop
commit
checkout main
merge develop
checkout develop
commit
checkout main
merge develop
</pre>
<h2>Merge feature to advanced main graph</h2>
<pre class="mermaid">
---
title: Merge feature to advanced main (left-to-right)
---
gitGraph LR:
commit
branch newbranch
checkout newbranch
commit
checkout main
commit commit
merge newbranch merge newbranch
</pre> </pre>
<pre class="mermaid">
---
title: Merge feature to advanced main (top-to-bottom)
---
gitGraph TB:
commit
branch newbranch
checkout newbranch
commit
checkout main
commit
merge newbranch
</pre>
<h2>Two-way merges</h2>
<pre class="mermaid">
---
title: Two-way merges (left-to-right)
---
gitGraph LR:
commit
branch develop
checkout develop
commit
checkout main
merge develop
commit
checkout develop
merge main
commit
checkout main
merge develop
</pre>
<pre class="mermaid">
---
title: Two-way merges (top-to-bottom)
---
gitGraph TB:
commit
branch develop
checkout develop
commit
checkout main
merge develop
commit
checkout develop
merge main
commit
checkout main
merge develop
</pre>
<h2>Cherry-pick from branch graph</h2>
<pre class="mermaid">
---
title: Cherry-pick from branch (left-to-right)
---
gitGraph LR:
commit
branch newbranch
checkout newbranch
commit id: "Pick me"
checkout main
commit
checkout newbranch
commit
checkout main
cherry-pick id: "Pick me"
</pre>
<pre class="mermaid">
---
title: Cherry-pick from branch (top-to-bottom)
---
gitGraph TB:
commit
branch newbranch
checkout newbranch
commit id: "Pick me"
checkout main
commit
checkout newbranch
commit
checkout main
cherry-pick id: "Pick me"
</pre>
<h2>Cherry-pick from main graph</h2>
<pre class="mermaid">
---
title: Cherry-pick from main (left-to-right)
---
gitGraph LR:
commit
branch develop
commit
checkout main
commit id:"A"
checkout develop
commit
cherry-pick id: "A"
</pre>
<pre class="mermaid">
---
title: Cherry-pick from main (top-to-bottom)
---
gitGraph TB:
commit
branch develop
commit
checkout main
commit id:"A"
checkout develop
commit
cherry-pick id: "A"
</pre>
<h2>Cherry-pick then merge graph</h2>
<pre class="mermaid">
---
title: Cherry-pick then merge (left-to-right)
---
gitGraph LR:
commit
branch newbranch
checkout newbranch
commit id: "Pick me"
checkout main
commit
checkout newbranch
commit
checkout main
cherry-pick id: "Pick me"
merge newbranch
</pre>
<pre class="mermaid">
---
title: Cherry-pick then merge (top-to-bottom)
---
gitGraph TB:
commit
branch newbranch
checkout newbranch
commit id: "Pick me"
checkout main
commit
checkout newbranch
commit
checkout main
cherry-pick id: "Pick me"
merge newbranch
</pre>
<h2>Merge from main onto undeveloped branch graph</h2>
<pre class="mermaid">
---
title: Merge from main onto undeveloped branch (left-to-right)
---
gitGraph LR:
commit
branch develop
commit
checkout main
commit
checkout develop
merge main
</pre>
<pre class="mermaid">
---
title: Merge from main onto undeveloped branch (top-to-bottom)
---
gitGraph TB:
commit
branch develop
commit
checkout main
commit
checkout develop
merge main
</pre>
<h2>Merge from main onto developed branch graph</h2>
<pre class="mermaid">
---
title: Merge from main onto developed branch (left-to-right)
---
gitGraph LR:
commit
branch develop
commit
checkout main
commit
checkout develop
commit
merge main
</pre>
<pre class="mermaid">
---
title: Merge from main onto developed branch (top-to-bottom)
---
gitGraph TB:
commit
branch develop
commit
checkout main
commit
checkout develop
commit
merge main
</pre>
<h2>Two branches from same commit graph</h2>
<pre class="mermaid">
---
title: Two branches from same commit (left-to-right)
---
gitGraph LR:
commit
commit
branch feature-001
commit
commit
checkout main
branch feature-002
commit
checkout feature-001
merge feature-002
</pre>
<pre class="mermaid">
---
title: Two branches from same commit (top-to-bottom)
---
gitGraph TB:
commit
commit
branch feature-001
commit
commit
checkout main
branch feature-002
commit
checkout feature-001
merge feature-002
</pre>
<h2>Three branches and a cherry-pick from each graph</h2>
<pre class="mermaid">
---
title: Three branches and a cherry-pick from each (left-to-right)
---
gitGraph LR:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
branch featureA
commit id:"FIX"
commit id: "FIX-2"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
cherry-pick id:"FIX"
checkout develop
commit id:"C"
merge featureA
</pre>
<pre class="mermaid">
---
title: Three branches and a cherry-pick from each (top-to-bottom)
---
gitGraph TB:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
branch featureA
commit id:"FIX"
commit id: "FIX-2"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
cherry-pick id:"FIX"
checkout develop
commit id:"C"
merge featureA
</pre>
<script type="module"> <script type="module">
import mermaid from './mermaid.esm.mjs'; import mermaid from './mermaid.esm.mjs';
const ALLOWED_TAGS = [ const ALLOWED_TAGS = [

View File

@@ -37,7 +37,7 @@
</pre> </pre>
<script type="module"> <script type="module">
import mermaid from './mermaid.esm.mjs'; import mermaid from '/mermaid.esm.mjs';
mermaid.initialize({ mermaid.initialize({
theme: 'forest', theme: 'forest',
logLevel: 3, logLevel: 3,

View File

@@ -33,7 +33,6 @@
--- ---
config: config:
sankey: sankey:
useMaxWidth: true
showValues: false showValues: false
width: 1200 width: 1200
height: 600 height: 600

View File

@@ -164,6 +164,13 @@
end end
</pre> </pre>
<pre class="mermaid">
sequenceDiagram
actor Alice
actor John
Alice-xJohn: Hello John, how are you?
John--xAlice: Great!
</pre>
<script type="module"> <script type="module">
import mermaid from './mermaid.esm.mjs'; import mermaid from './mermaid.esm.mjs';
mermaid.initialize({ mermaid.initialize({

View File

@@ -1,7 +1,7 @@
version: '3.9' version: '3.9'
services: services:
mermaid: mermaid:
image: node:18.19.0-alpine3.18 image: node:18.18.2-alpine3.18
stdin_open: true stdin_open: true
tty: true tty: true
working_dir: /mermaid working_dir: /mermaid

View File

@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:608](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L608) [mermaidAPI.ts:603](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L603)
## Functions ## Functions

View File

@@ -64,7 +64,7 @@ Example:
```html ```html
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
</script> </script>
``` ```
@@ -83,7 +83,7 @@ Example:
B-->D(fa:fa-spinner); B-->D(fa:fa-spinner);
</pre> </pre>
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
</script> </script>
</body> </body>
</html> </html>

View File

@@ -6,27 +6,18 @@
# Integrations # Integrations
## Official integration ## Official integration: [Mermaid Chart](./mermaid-chart.md)
### Mermaid Chart We're excited about the growth of the Mermaid community, and the number of plugins and integrations that have been created with Mermaid.
Mermaid Chart is built by the team behind Mermaid JS.
For more details, visit the [Mermaid Chart page](./mermaid-chart.md), or visit the [Mermaid Chart website](https://www.mermaidchart.com) .
## Community integrations ## Community integrations
We're excited about the growth of the Mermaid community, and the number of plugins and integrations that have been created by the community. Below are a list of community plugins and integrations created with Mermaid.
See the list below of community plugins and integrations created with Mermaid.
> **Note**
> A ✅ indicates Native support for Mermaid on the respective platform.
To add an integration to this list, see the [Integrations - create page](./integrations-create.md).
### Productivity tools ### Productivity tools
✅ = Native support
- [GitHub](https://github.com) ✅ - [GitHub](https://github.com) ✅
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) ✅ - [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) ✅
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action) - [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
@@ -70,7 +61,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid) - [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [mermerd](https://github.com/KarnerTh/mermerd) - [mermerd](https://github.com/KarnerTh/mermerd)
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive) - Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
- Codemia [a tool to practice system design problems](https://codemia.io)
### CRM/ERP ### CRM/ERP
@@ -121,8 +111,6 @@ Communication tools and platforms
### Wikis ### Wikis
- [PmWiki](https://www.pmwiki.org)
- [MermaidJs Cookbook recipe](https://www.pmwiki.org/wiki/Cookbook/MermaidJs)
- [MediaWiki](https://www.mediawiki.org) - [MediaWiki](https://www.mediawiki.org)
- [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid) - [Mermaid Extension](https://www.mediawiki.org/wiki/Extension:Mermaid)
- [Flex Diagrams Extension](https://www.mediawiki.org/wiki/Extension:Flex_Diagrams) - [Flex Diagrams Extension](https://www.mediawiki.org/wiki/Extension:Flex_Diagrams)
@@ -153,6 +141,7 @@ Communication tools and platforms
- [Textual UML Parser](https://github.com/manastalukdar/markdown-it-textual-uml) - [Textual UML Parser](https://github.com/manastalukdar/markdown-it-textual-uml)
- [Mermaid Plugin](https://github.com/tylingsoft/markdown-it-mermaid) - [Mermaid Plugin](https://github.com/tylingsoft/markdown-it-mermaid)
- [md-it-mermaid](https://github.com/iamcco/md-it-mermaid) - [md-it-mermaid](https://github.com/iamcco/md-it-mermaid)
- [markdown-it-mermaid-fence-new](https://github.com/Revomatico/markdown-it-mermaid-fence-new)
- [markdown-it-mermaid-less](https://github.com/searKing/markdown-it-mermaid-less) - [markdown-it-mermaid-less](https://github.com/searKing/markdown-it-mermaid-less)
- Atom _(Atom has been [archived.](https://github.blog/2022-06-08-sunsetting-atom/))_ - Atom _(Atom has been [archived.](https://github.blog/2022-06-08-sunsetting-atom/))_
- [Markdown Preview Enhanced](https://github.com/shd101wyy/markdown-preview-enhanced) - [Markdown Preview Enhanced](https://github.com/shd101wyy/markdown-preview-enhanced)
@@ -190,8 +179,7 @@ Communication tools and platforms
### Document Generation ### Document Generation
- [Docusaurus](https://docusaurus.io/docs/markdown-features/diagrams) ✅ - [Docusaurus](https://docusaurus.io/docs/markdown-features/diagrams) ✅
- [Unison programming language](https://www.unison-lang.org/docs/usage-topics/documentation/) ✅ - [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/Features/diagrams-and-charts)
- [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/features/diagrams-and-charts/#mermaid--swimm--up-to-date-diagrams-)
- [Sphinx](https://www.sphinx-doc.org/en/master/) - [Sphinx](https://www.sphinx-doc.org/en/master/)
- [sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid) - [sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid)
- [remark](https://remark.js.org/) - [remark](https://remark.js.org/)
@@ -207,14 +195,13 @@ Communication tools and platforms
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/) - [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
- [Type Doc](https://typedoc.org/) - [Type Doc](https://typedoc.org/)
- [typedoc-plugin-mermaid](https://www.npmjs.com/package/typedoc-plugin-mermaid) - [typedoc-plugin-mermaid](https://www.npmjs.com/package/typedoc-plugin-mermaid)
- [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) - [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) (native support in theme)
- [Codedoc](https://codedoc.cc/) - [Codedoc](https://codedoc.cc/)
- [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/) - [Quarto](https://quarto.org/)
- [Typora](https://typora.io/) - [Typora](https://typora.io/) ([native support](https://support.typora.io/Draw-Diagrams-With-Markdown/#mermaid))
- [See docs](https://support.typora.io/Draw-Diagrams-With-Markdown/#mermaid)
### Browser Extensions ### Browser Extensions
@@ -246,6 +233,6 @@ Communication tools and platforms
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server) - [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)
- [ExDoc](https://github.com/elixir-lang/ex_doc) - [ExDoc](https://github.com/elixir-lang/ex_doc)
- [Rendering Mermaid graphs](https://github.com/elixir-lang/ex_doc#rendering-mermaid-graphs) - [Rendering Mermaid graphs](https://github.com/elixir-lang/ex_doc#rendering-mermaid-graphs)
- [NiceGUI: Let any browser be the frontend of your Python code](https://nicegui.io) - [NiceGUI: Let any browser be the frontend of your Python code](https://nicegui.io)
- [ui.mermaid(...)](https://nicegui.io/documentation/section_text_elements#markdown_element) - [ui.mermaid(...)](https://nicegui.io/reference#mermaid_diagrams)
- [ui.markdown(..., extras=\['mermaid'\])](https://nicegui.io/documentation/section_text_elements#mermaid_diagrams) - [ui.markdown(..., extras=\['mermaid'\])](https://nicegui.io/reference#markdown_element)

View File

@@ -22,16 +22,9 @@
- **Collaboration** - A web based collaboration feature for multi-user editing on Mermaid diagrams in real-time (Pro plan). - **Collaboration** - A web based collaboration feature for multi-user editing on Mermaid diagrams in real-time (Pro plan).
- **Plugins** - A plugin system for extending the functionality of Mermaid. - **Plugins** - A plugin system for extending the functionality of Mermaid. Currently includes [VS Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart) and [ChatGPT](https://www.mermaidchart.com/plugins/chatgpt).
Plugins are available for: - **AI** - An AI chatbot that can generate Mermaid diagrams from text (Pro plan).
- [ChatGPT](https://docs.mermaidchart.com/plugins/chatgpt)
- [JetBrains IDE](https://plugins.jetbrains.com/plugin/23043-mermaid-chart)
- [Microsoft PowerPoint and Word](https://appsource.microsoft.com/en-us/product/office/WA200006214?tab=Overview)
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart)
- **AI diagramming** - A feature for generating Mermaid diagrams from text using AI (Pro plan).
- **More** - To learn more, visit our [Product](https://www.mermaidchart.com/product) page. - **More** - To learn more, visit our [Product](https://www.mermaidchart.com/product) page.
@@ -47,7 +40,7 @@
Sign up for a free account at [Mermaid Chart](https://www.mermaidchart.com/app/sign-up). Sign up for a free account at [Mermaid Chart](https://www.mermaidchart.com/app/sign-up).
Mermaid Chart is currently offering a 14-day free trial of our newly-launched Pro tier. To learn more, visit our [Pricing](https://mermaidchart.com/pricing) page. Mermaid Chart is currently offering a 30-day free trial of our newly-launched Pro tier. To learn more, visit our [Pricing](https://mermaidchart.com/pricing) page.
## Mermaid JS contributions ## Mermaid JS contributions

View File

@@ -4,44 +4,30 @@
> >
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/intro/getting-started.md](../../packages/mermaid/src/docs/intro/getting-started.md). > ## Please edit the corresponding file in [/packages/mermaid/src/docs/intro/getting-started.md](../../packages/mermaid/src/docs/intro/getting-started.md).
# Mermaid User Guide # A Mermaid User-Guide for Beginners
## Mermaid is composed of three parts Mermaid is composed of three parts: Deployment, Syntax and Configuration.
1. Deployment This section talks about the different ways to deploy Mermaid. Learning the [Syntax](syntax-reference.md) would be of great help to the beginner.
2. Syntax
3. Configuration
This section talks about the different ways to **deploy** Mermaid. > Generally the live editor is enough for most general uses of mermaid, and is a good place to start learning.
If you are a beginner: **Absolute beginners are advised to view the Video [Tutorials](../config/Tutorials.md) on the Live Editor, to gain a better understanding of mermaid.**
- Check out the [Diagram Syntax](syntax-reference.md) page ## Four ways of using mermaid:
- Check out the [Tutorials](../config/Tutorials.md) page
## Ways to use Mermaid 1. Using the Mermaid Live Editor at [mermaid.live](https://mermaid.live).
2. Using [mermaid plugins](../ecosystem/integrations-community.md) with programs you are familiar with.
3. Calling the Mermaid JavaScript API.
4. Deploying Mermaid as a dependency.
1. [Using the Mermaid Live Editor](getting-started.md#_1-using-the-mermaid-live-editor) **Note: It is our recommendation that you review all approaches, and choose the one that is best for your project.**
2. [Using the Mermaid Chart Editor](getting-started.md#_2-using-the-mermaid-chart-editor)
3. [Using Mermaid Plugins and Integrations](getting-started.md#_3-using-mermaid-plugins)
4. [Calling the Mermaid JavaScript API](getting-started.md#_4-calling-the-mermaid-javascript-api)
5. [Adding Mermaid as a dependency](getting-started.md#_5-adding-mermaid-as-a-dependency)
To learn more, visit the [Usage](../config/usage.md) page. > More in depth information can be found at [Usage](../config/usage.md).
## 1. Using the Mermaid Live Editor ## 1. Using the Live Editor
Available at the [Mermaid Live Editor](https://mermaid.live) website. Available at [mermaid.live](https://mermaid.live)
### Features
<br />
#### • Diagram Code
In the `Code` panel, write or edit Mermaid code, and instantly `Preview` the rendered result in the diagram panel.
Here is an example of Mermaid code and its rendered result:
```mermaid-example ```mermaid-example
graph TD graph TD
@@ -65,141 +51,61 @@ graph TD
F --> B F --> B
``` ```
<br /> In the `Code` section one can write or edit raw mermaid code, and instantly `Preview` the rendered result on the panel beside it.
#### • Configurations The `Configuration` Section is for changing the appearance and behavior of mermaid diagrams. An easy introduction to mermaid configuration is found in the [Advanced usage](../config/advanced.md) section. A complete configuration reference cataloging the default values can be found on the [mermaidAPI](../config/setup/README.md) page.
Configuration options are available in the `Configuration` panel. The options are applied to the diagram in the `Preview` panel.
For learn more, visit the [Configuration Reference](../config/setup/README.md) page
![Code,Config and Preview](./img/Code-Preview-Config.png) ![Code,Config and Preview](./img/Code-Preview-Config.png)
<br /> ### Editing History
#### • Editing History Your code will be autosaved every minute into the Timeline tab of History which shows the most recent 30 items.
Your code will be autosaved and appear in the `Timeline` tab of the `History` section. Edits are saved every minute and only the last 30 edits are viewable. You can manually save code by clicking the Save icon in the History section. It can also be accessed in the Saved tab. This is stored in the browser storage only.
Alternatively, you can manually save code by clicking on the `Save` icon from the `History` section. ### Saving a Diagram:
> **Note** You may choose any of the methods below, to save it
> History is stored in the browser storage only.
<br /> **We recommend that you save your diagram code on top of any method you choose, in order to make edits and modifications further down the line.**
#### • Saving a diagram
There are multiple ways of saving your diagram from the `Actions` section:
- export PNG
- export SVG
- export as Markdown
![Flowchart](./img/Live-Editor-Choices.png) ![Flowchart](./img/Live-Editor-Choices.png)
<br /> ### Editing your diagrams
#### • Editing your diagrams Editing is as easy as pasting your **Diagram code**, into the `code` section of the `Live Editor`.
To edit your diagram, you can copy paste existing Mermaid diagram code into the `Code` section of the `Live Editor`. ### Loading from Gists
Or: The Gist you create should have a code.mmd file and optionally a config.json. [Example](https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a)
- create a new diagram from scratch To load a gist into the Editor, you can use <https://mermaid.live/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a>
- use a Sample Diagram from the `Sample Diagrams` section
<br /> and to View, <https://mermaid.live/view?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a>
#### • Loading from Gists ## 2. Using Mermaid Plugins:
The Gist you create should have a `code.mmd` file and optionally a `config.json`, similar to this [example](https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a). You can generate mermaid diagrams from within popular applications using plug-ins. It can be done in the same way, you would use the Live Editor. Here's a list of [Mermaid Plugins](../ecosystem/integrations-community.md).
> **Note** **This is covered in greater detail in the [Usage section](../config/usage.md)**
> To learn about Gists, visit the GitHub documentation page on [Creating gists](https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists).
Once you have created a Gist, copy paste the Gist URL into the respective field in the `Actions` section and click on the `Load Gist` button. ## 3. Calling the JavaScript API
Here is an example of a Gist being loaded into the Editor: This method can be used with any common web server like Apache, IIS, nginx, node express.
<https://mermaid.live/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a> You will also need a text editing tool like Notepad++ to generate a .html file. It is then deployed by a web browser (such as Firefox, Chrome, Safari, but not Internet Explorer).
And, here is the diagram view from the above example:
<https://mermaid.live/view?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a>
## 2. Using the Mermaid Chart Editor
Available at the [Mermaid Chart](https://www.mermaidchart.com/) website.
Mermaid Chart is a web-based diagram editor that allows you to create and edit diagrams in your browser. It is built by the team behind Mermaid.
Features include:
- AI diagramming
- Collaboration & multi-user editing
- Storage
- and more
To learn more, visit the [Mermaid Chart page](/ecosystem/mermaid-chart.html) in the Ecosystem section of the documentation.
Or go to the [Mermaid Chart website](https://www.mermaidchart.com/app/sign-up) to sign up for a Free account.
## 3. Using Mermaid Plugins
### Mermaid Plugins
You can generate Mermaid diagrams from within popular applications using plug-ins.
For a list of Mermaid Plugins and Integrations, visit the [Integrations page](../ecosystem/integrations-community.md).
### Mermaid Chart Plugins
Mermaid Chart plugins are available for:
- [ChatGPT](https://docs.mermaidchart.com/plugins/chatgpt)
- [JetBrains IDE](https://docs.mermaidchart.com/plugins/jetbrains-ide)
- [Microsoft PowerPoint](https://docs.mermaidchart.com/plugins/microsoft-powerpoint)
- [Microsoft Word](https://docs.mermaidchart.com/plugins/microsoft-word)
- [Visual Studio Code](https://docs.mermaidchart.com/plugins/visual-studio-code)
To learn more, visit the [Mermaid Chart Plugins](https://www.mermaidchart.com/plugins) page.
### Native Mermaid Support
For apps that support markdown (e.g. [GitHub](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams) and [GitLab](https://handbook.gitlab.com/handbook/tools-and-tips/mermaid/)), you can add Mermaid diagrams by making a `mermaid` code block.
````markdown
The following code-block will be rendered as a Mermaid diagram:
```mermaid
flowchart LR
A --> B
```
````
## 4. Calling the Mermaid JavaScript API
This method can be used with any common web server like `Apache`, `IIS`, `Nginx`, and `Node Express`.
You will also need a text editing tool like `Notepad++` to generate an `html` file. It is then deployed by a web browser, i.e. `Firefox`, `Chrome`, `Safari`.
> **Note**
> Internet Explorer is not supported.
The API works by pulling rendering instructions from the source `mermaid.js` in order to render diagrams on the page. The API works by pulling rendering instructions from the source `mermaid.js` in order to render diagrams on the page.
### Requirements for the Mermaid API ### Requirements for the Mermaid API.
When writing the `html` file, we give two instructions inside the `html code` to the `web browser`: When writing the .html file, we give two instructions inside the html code to the web browser:
a. The Mermaid code for the diagram we want to create. a. The mermaid code for the diagram we want to create.
b. The importing of the Mermaid library through the `mermaid.esm.mjs` or `mermaid.esm.min.mjs`, and the `mermaid.initialize()` call, which dictates the appearance of diagrams and also starts the rendering process. b. The importing of mermaid library through the `mermaid.esm.mjs` or `mermaid.esm.min.mjs` and the `mermaid.initialize()` call, which dictates the appearance of diagrams and also starts the rendering process.
#### Examples **a. The embedded mermaid diagram definition inside a `<pre class="mermaid">`:**
- This is an example of an embedded Mermaid diagram definition inside a `<pre class="mermaid">`:
```html ```html
<body> <body>
@@ -213,25 +119,23 @@ b. The importing of the Mermaid library through the `mermaid.esm.mjs` or `mermai
</body> </body>
``` ```
> **Note** **Notes**: Every Mermaid chart/graph/diagram definition, should have separate `<pre>` tags.
> Every Mermaid chart/graph/diagram definition should have separate `<pre>` tags.
- This is an example of a Mermaid import and the `mermaid.initialize()` call. **b. The import of mermaid and the `mermaid.initialize()` call.**
> **Note** `mermaid.initialize()` call takes all the definitions contained in all the `<pre class="mermaid">` tags that it finds in the html body and renders them into diagrams. Example:
> A `mermaid.initialize()` call takes all the definitions contained within `<pre class="mermaid">` tags and renders them into diagrams.
```html ```html
<body> <body>
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true }); mermaid.initialize({ startOnLoad: true });
</script> </script>
</body> </body>
``` ```
> **Note** **Notes**:
> Rendering in Mermaid is initialized by the `mermaid.initialize()` call. However, doing the opposite lets you control when it starts looking for `<pre>` tags inside the web page with `mermaid.initialize()`. This is useful when you think that not all `<pre>` tags may have loaded on the execution of `mermaid.esm.min.mjs` file. Rendering in Mermaid is initialized by `mermaid.initialize()` call. However, doing the opposite lets you control when it starts looking for `<pre>` tags inside the web page with `mermaid.initialize()`. This is useful when you think that not all `<pre>` tags may have loaded on the execution of `mermaid.esm.min.mjs` file.
`startOnLoad` is one of the parameters that can be defined by `mermaid.initialize()` `startOnLoad` is one of the parameters that can be defined by `mermaid.initialize()`
@@ -239,7 +143,9 @@ b. The importing of the Mermaid library through the `mermaid.esm.mjs` or `mermai
| ----------- | --------------------------------- | ------- | ----------- | | ----------- | --------------------------------- | ------- | ----------- |
| startOnLoad | Toggle for Rendering upon loading | Boolean | true, false | | startOnLoad | Toggle for Rendering upon loading | Boolean | true, false |
In this example, the `mermaidAPI` is being called through the `CDN`: ### Working Examples
**Here is a full working example of the mermaidAPI being called through the CDN:**
```html ```html
<html> <html>
@@ -262,14 +168,15 @@ In this example, the `mermaidAPI` is being called through the `CDN`:
</pre> </pre>
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true }); mermaid.initialize({ startOnLoad: true });
</script> </script>
</body> </body>
</html> </html>
``` ```
In this example, `mermaid.js` is referenced in `src` as a separate JavaScript file: **Another Option:**
In this example mermaid.js is referenced in `src` as a separate JavaScript file, in an example Path.
```html ```html
<html lang="en"> <html lang="en">
@@ -297,30 +204,21 @@ In this example, `mermaid.js` is referenced in `src` as a separate JavaScript fi
</html> </html>
``` ```
## 5. Adding Mermaid as a dependency ---
Below are the steps for adding Mermaid as a dependency: ## 4. Adding Mermaid as a dependency.
1. Install `node v16` 1. install node v16, which would have npm
> **Note** 2. download yarn using npm by entering the command below:
> To learn more about downloading and installing `Node.js` and `npm`, visit the [npm Docs website](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). npm install -g yarn
1. Install `yarn` using `npm` with this command: 3. After yarn installs, enter the following command:
yarn add mermaid
`npm install -g yarn` 4. To add Mermaid as a Dev Dependency
yarn add --dev mermaid
2. After yarn installs, enter this command: **Comments from Knut Sveidqvist, creator of mermaid:**
`yarn add mermaid` - In early versions of mermaid, the `<script>` tag was invoked in the `<head>` part of the web page. Nowadays we can place it in the `<body>` as seen above. Older parts of the documentation frequently reflect the previous way which still works.
3. To add Mermaid as a dev dependency, enter this command:
`yarn add --dev mermaid`
## Closing note
> **Note**
> Comments from Knut Sveidqvist, creator of Mermaid:
>
> - In early versions of Mermaid, the `<script>` tag was invoked in the `<head>` part of the web page. Nowadays, we can place it in the `<body>` as seen above. Older parts of the documentation frequently reflect the previous way, which still works.

View File

@@ -317,7 +317,7 @@ To select a version:
Replace `<version>` with the desired version number. Replace `<version>` with the desired version number.
Latest Version: <https://cdn.jsdelivr.net/npm/mermaid@10> Latest Version: <https://cdn.jsdelivr.net/npm/mermaid@11>
## Deploying Mermaid ## Deploying Mermaid
@@ -335,7 +335,7 @@ To Deploy Mermaid:
```html ```html
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true }); mermaid.initialize({ startOnLoad: true });
</script> </script>
``` ```

View File

@@ -6,27 +6,18 @@
# Announcements # Announcements
## 🚀 Mermaid Chart's Visual Editor for Flowcharts <br />
The Mermaid Chart team is excited to introduce a new Visual Editor for flowcharts, enabling users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options. <a href="https://www.producthunt.com/posts/mermaid-chart?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=416671&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
Create flowchart nodes, connect them with edges, update shapes, change colors, and edit labels with just a few clicks that automatically reflect in your diagrams code for easy customizability. ## Calling all fans of Mermaid and Mermaid Chart! 🎉
Read more about it in our latest [BLOG POST](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts) and watch a [DEMO VIDEO](https://www.youtube.com/watch?v=5aja0gijoO0) on our YouTube page. Weve officially made our Product Hunt debut, and would love any and all support from the community!
## 🎉 Mermaid Chart is running a Holiday promotion [Click here](https://www.producthunt.com/posts/mermaid-chart?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid-chart) to check out our Product Hunt launch.
### Use <span class="text-[#FE3470]">HOLIDAYS2023</span> to get a 14-day free trial and 25% off a Pro subscription Feel free to drop us a comment and let us know what you think. All new sign ups will receive a 30-day free trial of our Pro subscription, plus 25% off your first year.
With a Pro subscription, you get access to: Were on a mission to make text-based diagramming fun again. And we need your help to make that happen.
- AI functionality Your support means the world to us. Thank you for being part of the diagramming movement.
- Team collaboration and multi-user editing
- Unlimited diagrams and presentations
- And more!
Redeem the promo code on the [Mermaid Chart website](https://www.mermaidchart.com/app/user/billing/checkout?coupon=HOLIDAYS2023).
## 📖 Blog posts
Visit our [Blog](./blog.md) to see the latest blog posts.

View File

@@ -6,42 +6,6 @@
# Blog # Blog
## [Introducing Mermaid Charts JetBrains IDE Extension](https://www.mermaidchart.com/blog/posts/introducing-mermaid-charts-jetbrains-ide-extension/)
20 December 2023 · 5 mins
Diagrams are essential for documenting your code.
## [Mermaid Chart Releases New Visual Editor For Flowcharts](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts/)
14 December 2023 · 5 mins
Mermaid Chart introduces a new Visual Editor for flowcharts, enabling users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options.
## [7 best practices (+ examples) for good developer documentation](https://www.mermaidchart.com/blog/posts/7-best-practices-for-good-documentation/)
4 December 2023 · 11 min
Essential strategies for crafting grate developer documentation, with practical examples and insights from leading tech companies.
## [5 Reasons You Should Be Using Mermaid Chart As Your Diagram Generator](https://www.mermaidchart.com/blog/posts/5-reasons-you-should-be-using-mermaid-chart-as-your-diagram-generator/)
14 November 2023 · 5 mins
Mermaid Chart, a user-friendly, code-based diagram generator with AI integrations, templates, collaborative tools, and plugins for developers, streamlines the process of creating and sharing diagrams, enhancing both creativity and collaboration.
## [How to Use Mermaid Chart as an AI Diagram Generator](https://www.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator/)
1 November 2023 · 5 mins
Would an AI diagram generator make your life easier?
## [Diagrams, Made Even Easier: Introducing “Code Snippets” in the Mermaid Chart Editor](https://www.mermaidchart.com/blog/posts/easier-diagram-editing-with-code-snippets/)
12 October 2023 · 4 mins
Mermaid Chart introduces Code Snippets in its editor, streamlining the diagramming process for developers and professionals.
## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart/) ## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart/)
22 September 2023 · 7 mins 22 September 2023 · 7 mins

View File

@@ -240,9 +240,9 @@ class BankAccount{
#### Generic Types #### Generic Types
Generics can be representated as part of a class definition, and for class members/return types. In order to denote an item as generic, you enclose that type within `~` (**tilde**). **Nested** type declarations such as `List<List<int>>` are supported, though generics that include a comma are currently not supported. (such as `List<List<K, V>>`) Members can be defined using generic types, such as `List<int>`, for fields, parameters, and return types by enclosing the type within `~` (**tilde**). **Nested** type declarations such as `List<List<int>>` are supported.
> _note_ when a generic is used within a class definition, the generic type is NOT considered part of the class name. i.e.: for any syntax which required you to reference the class name, you need to drop the type part of the definition. This also means that mermaid does not currently support having two classes with the same name, but different generic types. Generics can be represented as part of a class definition and also in the parameters or the return value of a method/function:
```mermaid-example ```mermaid-example
classDiagram classDiagram
@@ -459,9 +459,9 @@ The different cardinality options are :
- `0..1` Zero or One - `0..1` Zero or One
- `1..*` One or more - `1..*` One or more
- `*` Many - `*` Many
- `n` n (where n>1) - `n` n {where n>1}
- `0..n` zero to n (where n>1) - `0..n` zero to n {where n>1}
- `1..n` one to n (where n>1) - `1..n` one to n {where n>1}
Cardinality can be easily defined by placing the text option within quotes `"` before or after a given arrow. For example: Cardinality can be easily defined by placing the text option within quotes `"` before or after a given arrow. For example:
@@ -766,30 +766,9 @@ Beginner's tip—a full example using interactive links in an HTML page:
## Styling ## Styling
### Styling a node (v10.7.0+) ### Styling a node
It is possible to apply specific styles such as a thicker border or a different background color to an individual node using the `style` keyword. It is possible to apply specific styles such as a thicker border or a different background color to individual nodes. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand.
```mermaid-example
classDiagram
class Animal
class Mineral
style Animal fill:#f9f,stroke:#333,stroke-width:4px
style Mineral fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
```
```mermaid
classDiagram
class Animal
class Mineral
style Animal fill:#f9f,stroke:#333,stroke-width:4px
style Mineral fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
```
#### Classes
More convenient than defining the style every time is to define a class of styles and attach this class to the nodes that
should have a different look. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand.
```html ```html
<style> <style>

View File

@@ -11,13 +11,6 @@ Flowcharts are composed of **nodes** (geometric shapes) and **edges** (arrows or
> **Warning** > **Warning**
> If you are using the word "end" in a Flowchart node, capitalize the entire word or any of the letters (e.g., "End" or "END"), or apply this [workaround](https://github.com/mermaid-js/mermaid/issues/1444#issuecomment-639528897). Typing "end" in all lowercase letters will break the Flowchart. > If you are using the word "end" in a Flowchart node, capitalize the entire word or any of the letters (e.g., "End" or "END"), or apply this [workaround](https://github.com/mermaid-js/mermaid/issues/1444#issuecomment-639528897). Typing "end" in all lowercase letters will break the Flowchart.
> **Warning**
> If you are using the letter "o" or "x" as the first letter in a connecting Flowchart node, add a space before the letter or capitalize the letter (e.g., "dev--- ops", "dev---Ops").
>
> Typing "A---oB" will create a [circle edge](#circle-edge-example).
>
> Typing "A---xB" will create a [cross edge](#cross-edge-example).
### A node (default) ### A node (default)
```mermaid-example ```mermaid-example
@@ -495,38 +488,23 @@ flowchart TB
B --> D B --> D
``` ```
## New arrow types ### New arrow types
There are new types of arrows supported: There are new types of arrows supported as per below:
- circle edge
- cross edge
### Circle edge example
```mermaid-example ```mermaid-example
flowchart LR flowchart LR
A --o B A --o B
B --x C
``` ```
```mermaid ```mermaid
flowchart LR flowchart LR
A --o B A --o B
B --x C
``` ```
### Cross edge example ### Multi directional arrows
```mermaid-example
flowchart LR
A --x B
```
```mermaid
flowchart LR
A --x B
```
## Multi directional arrows
There is the possibility to use multidirectional arrows. There is the possibility to use multidirectional arrows.

View File

@@ -366,8 +366,6 @@ A few important rules to note here are:
1. You need to provide the `id` for an existing commit to be cherry-picked. If given commit id does not exist it will result in an error. For this, make use of the `commit id:$value` format of declaring commits. See the examples from above. 1. You need to provide the `id` for an existing commit to be cherry-picked. If given commit id does not exist it will result in an error. For this, make use of the `commit id:$value` format of declaring commits. See the examples from above.
2. The given commit must not exist on the current branch. The cherry-picked commit must always be a different branch than the current branch. 2. The given commit must not exist on the current branch. The cherry-picked commit must always be a different branch than the current branch.
3. Current branch must have at least one commit, before you can cherry-pick, otherwise it will cause an error is throw. 3. Current branch must have at least one commit, before you can cherry-pick, otherwise it will cause an error is throw.
4. When cherry-picking a merge commit, providing a parent commit ID is mandatory. If the parent attribute is omitted or an invalid parent commit ID is provided, an error will be thrown.
5. The specified parent commit must be an immediate parent of the merge commit being cherry-picked.
Let see an example: Let see an example:
@@ -375,17 +373,14 @@ Let see an example:
gitGraph gitGraph
commit id: "ZERO" commit id: "ZERO"
branch develop branch develop
branch release
commit id:"A" commit id:"A"
checkout main checkout main
commit id:"ONE" commit id:"ONE"
checkout develop checkout develop
commit id:"B" commit id:"B"
checkout main checkout main
merge develop id:"MERGE"
commit id:"TWO" commit id:"TWO"
checkout release cherry-pick id:"A"
cherry-pick id:"MERGE" parent:"B"
commit id:"THREE" commit id:"THREE"
checkout develop checkout develop
commit id:"C" commit id:"C"
@@ -395,17 +390,14 @@ Let see an example:
gitGraph gitGraph
commit id: "ZERO" commit id: "ZERO"
branch develop branch develop
branch release
commit id:"A" commit id:"A"
checkout main checkout main
commit id:"ONE" commit id:"ONE"
checkout develop checkout develop
commit id:"B" commit id:"B"
checkout main checkout main
merge develop id:"MERGE"
commit id:"TWO" commit id:"TWO"
checkout release cherry-pick id:"A"
cherry-pick id:"MERGE" parent:"B"
commit id:"THREE" commit id:"THREE"
checkout develop checkout develop
commit id:"C" commit id:"C"

View File

@@ -300,7 +300,7 @@ From version 9.4.0 you can simplify this code to:
```html ```html
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
</script> </script>
``` ```

View File

@@ -131,14 +131,6 @@ sequenceDiagram
Bob->>Alice: I agree Bob->>Alice: I agree
``` ```
#### Unfixable actor/participant creation/deletion error
If an error of the following type occurs when creating or deleting an actor/participant:
> The destroyed participant **participant-name** does not have an associated destroying message after its declaration. Please check the sequence diagram.
And fixing diagram code does not get rid of this error and rendering of all other diagrams results in the same error, then you need to update the mermaid version to (v10.7.0+).
### Grouping / Box ### Grouping / Box
The actor(s) can be grouped in vertical boxes. You can define a color (if not, it will be transparent) and/or a descriptive label using the following notation: The actor(s) can be grouped in vertical boxes. You can define a color (if not, it will be transparent) and/or a descriptive label using the following notation:

View File

@@ -469,7 +469,7 @@ You can use this method to add mermaid including the timeline diagram to a web p
```html ```html
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
</script> </script>
``` ```

View File

@@ -4,7 +4,7 @@
"version": "10.2.4", "version": "10.2.4",
"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@8.14.1", "packageManager": "pnpm@8.10.4",
"keywords": [ "keywords": [
"diagram", "diagram",
"markdown", "markdown",
@@ -15,26 +15,26 @@
"git graph" "git graph"
], ],
"scripts": { "scripts": {
"build:vite": "tsx .vite/build.ts", "build": "pnpm build:esbuild && pnpm build:types",
"build:mermaid": "pnpm build:vite --mermaid", "build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts",
"build:viz": "pnpm build:mermaid --visualize", "build:mermaid": "pnpm build:esbuild --mermaid",
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly", "build:viz": "pnpm build:esbuild --visualize",
"build:types": "tsc -p ./packages/parser/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
"build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch", "build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch",
"build:watch": "pnpm build:vite --watch", "dev": "ts-node-esm --transpileOnly .esbuild/server.ts",
"build": "pnpm run -r clean && pnpm build:types && pnpm build:vite", "dev:vite": "ts-node-esm --transpileOnly .vite/server.ts",
"dev": "concurrently \"pnpm build:vite --watch\" \"tsx .vite/server.ts\"", "dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev",
"release": "pnpm build", "release": "pnpm build",
"lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .", "lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .",
"lint:fix": "eslint --cache --cache-strategy content --fix --ignore-path .gitignore . && prettier --write . && tsx scripts/fixCSpell.ts", "lint:fix": "eslint --cache --cache-strategy content --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts",
"lint:jison": "tsx ./scripts/jison/lint.mts", "lint:jison": "ts-node-esm ./scripts/jison/lint.mts",
"contributors": "tsx scripts/updateContributors.ts", "contributors": "ts-node-esm scripts/updateContributors.ts",
"cypress": "cypress run", "cypress": "cypress run",
"cypress:open": "cypress open", "cypress:open": "cypress open",
"e2e": "start-server-and-test dev http://localhost:9000/ cypress", "e2e": "start-server-and-test dev http://localhost:9000/ cypress",
"e2e:coverage": "start-server-and-test dev:coverage http://localhost:9000/ cypress",
"coverage:cypress:clean": "rimraf .nyc_output coverage/cypress", "coverage:cypress:clean": "rimraf .nyc_output coverage/cypress",
"e2e:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm e2e", "coverage:merge": "ts-node-esm scripts/coverage.ts",
"coverage:merge": "tsx scripts/coverage.ts",
"coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge", "coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge",
"ci": "vitest run", "ci": "vitest run",
"test": "pnpm lint && vitest run", "test": "pnpm lint && vitest run",
@@ -83,6 +83,7 @@
"@vitest/spy": "^0.34.0", "@vitest/spy": "^0.34.0",
"@vitest/ui": "^0.34.0", "@vitest/ui": "^0.34.0",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"chokidar": "^3.5.3",
"concurrently": "^8.0.1", "concurrently": "^8.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"cypress": "^12.10.0", "cypress": "^12.10.0",
@@ -107,6 +108,7 @@
"jison": "^0.4.18", "jison": "^0.4.18",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsdom": "^22.0.0", "jsdom": "^22.0.0",
"langium-cli": "2.0.1",
"lint-staged": "^13.2.1", "lint-staged": "^13.2.1",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
@@ -116,14 +118,14 @@
"rimraf": "^5.0.0", "rimraf": "^5.0.0",
"rollup-plugin-visualizer": "^5.9.2", "rollup-plugin-visualizer": "^5.9.2",
"start-server-and-test": "^2.0.0", "start-server-and-test": "^2.0.0",
"tsx": "^4.6.2", "ts-node": "^10.9.1",
"typescript": "^5.1.3", "typescript": "^5.1.3",
"vite": "^4.4.12", "vite": "^4.3.9",
"vite-plugin-istanbul": "^4.1.0", "vite-plugin-istanbul": "^4.1.0",
"vitest": "^0.34.0" "vitest": "^0.34.0"
}, },
"volta": { "volta": {
"node": "18.19.0" "node": "18.18.2"
}, },
"nyc": { "nyc": {
"report-dir": "coverage/cypress" "report-dir": "coverage/cypress"

View File

@@ -43,8 +43,7 @@
"cytoscape-cose-bilkent": "^4.1.0", "cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0", "cytoscape-fcose": "^2.1.0",
"d3": "^7.0.0", "d3": "^7.0.0",
"khroma": "^2.0.0", "khroma": "^2.0.0"
"non-layered-tidy-tree-layout": "^2.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/cytoscape": "^3.19.9", "@types/cytoscape": "^3.19.9",

View File

@@ -0,0 +1,12 @@
{
"name": "@mermaid-js/flowchart-elk",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "MIT"
}

View File

@@ -19,6 +19,7 @@
"mermaid" "mermaid"
], ],
"scripts": { "scripts": {
"clean": "rimraf dist",
"prepublishOnly": "pnpm -w run build" "prepublishOnly": "pnpm -w run build"
}, },
"repository": { "repository": {

View File

@@ -5,7 +5,6 @@
* This is a dummy parser that satisfies the mermaid API logic. * This is a dummy parser that satisfies the mermaid API logic.
*/ */
export default { export default {
parser: { yy: {} },
parse: () => { parse: () => {
// no op // no op
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "10.7.0", "version": "11.0.0-alpha.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",
@@ -26,18 +26,18 @@
"clean": "rimraf dist", "clean": "rimraf dist",
"dev": "pnpm -w dev", "dev": "pnpm -w dev",
"docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup", "docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup",
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && tsx scripts/docs.cli.mts", "docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm scripts/docs.cli.mts",
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && tsx scripts/docs.cli.mts --verify", "docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm 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:pre:vitepress": "pnpm --filter ./src/docs prefetch && rimraf src/vitepress && pnpm docs:code && ts-node-esm 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", "docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing",
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev\" \"tsx scripts/docs.cli.mts --watch --vitepress\"", "docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev\" \"ts-node-esm scripts/docs.cli.mts --watch --vitepress\"",
"docs:dev:docker": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev:docker\" \"tsx scripts/docs.cli.mts --watch --vitepress\"", "docs:dev:docker": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev:docker\" \"ts-node-esm scripts/docs.cli.mts --watch --vitepress\"",
"docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress", "docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress",
"docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"", "docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"",
"docs:release-version": "tsx scripts/update-release-version.mts", "docs:release-version": "ts-node-esm scripts/update-release-version.mts",
"docs:verify-version": "tsx scripts/update-release-version.mts --verify", "docs:verify-version": "ts-node-esm scripts/update-release-version.mts --verify",
"types:build-config": "tsx scripts/create-types-from-json-schema.mts", "types:build-config": "ts-node-esm --transpileOnly scripts/create-types-from-json-schema.mts",
"types:verify-config": "tsx scripts/create-types-from-json-schema.mts --verify", "types:verify-config": "ts-node-esm scripts/create-types-from-json-schema.mts --verify",
"checkCircle": "npx madge --circular ./src", "checkCircle": "npx madge --circular ./src",
"release": "pnpm build", "release": "pnpm build",
"prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm docs:release-version && pnpm -w run build" "prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm docs:release-version && pnpm -w run build"
@@ -60,8 +60,6 @@
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^6.0.1", "@braintree/sanitize-url": "^6.0.1",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.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",
@@ -70,15 +68,14 @@
"dagre-d3-es": "7.0.10", "dagre-d3-es": "7.0.10",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"dompurify": "^3.0.5", "dompurify": "^3.0.5",
"elkjs": "^0.9.0", "elkjs": "^0.8.2",
"khroma": "^2.0.0", "khroma": "^2.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mdast-util-from-markdown": "^1.3.0", "mdast-util-from-markdown": "^1.3.0",
"non-layered-tidy-tree-layout": "^2.0.2", "mermaid-parser": "workspace:^",
"stylis": "^4.1.3", "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"
}, },
"devDependencies": { "devDependencies": {
"@adobe/jsonschema2md": "^7.1.4", "@adobe/jsonschema2md": "^7.1.4",
@@ -86,6 +83,7 @@
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/d3-sankey": "^0.12.1", "@types/d3-sankey": "^0.12.1",
"@types/d3-scale": "^4.0.3", "@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"@types/d3-selection": "^3.0.5", "@types/d3-selection": "^3.0.5",
"@types/d3-shape": "^3.1.1", "@types/d3-shape": "^3.1.1",
"@types/dompurify": "^3.0.2", "@types/dompurify": "^3.0.2",

View File

@@ -1,7 +1,7 @@
import * as configApi from './config.js'; import * as configApi from './config.js';
import { log } from './logger.js'; import { log } from './logger.js';
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js'; import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
import { detectType, getDiagramLoader } from './diagram-api/detectType.js'; import { detectType, getDiagramLoaderAndPriority } from './diagram-api/detectType.js';
import { UnknownDiagramError } from './errors.js'; import { UnknownDiagramError } from './errors.js';
import { encodeEntities } from './utils.js'; import { encodeEntities } from './utils.js';
@@ -38,7 +38,10 @@ export class Diagram {
this.db = diagram.db; this.db = diagram.db;
this.renderer = diagram.renderer; this.renderer = diagram.renderer;
this.parser = diagram.parser; this.parser = diagram.parser;
if (this.parser.parser) {
// The parser.parser.yy is only present in JISON parsers. So, we'll only set if required.
this.parser.parser.yy = this.db; this.parser.parser.yy = this.db;
}
this.init = diagram.init; this.init = diagram.init;
this.parse(); this.parse();
} }
@@ -89,14 +92,14 @@ export const getDiagramFromText = async (
// Trying to find the diagram // Trying to find the diagram
getDiagram(type); getDiagram(type);
} catch (error) { } catch (error) {
const loader = getDiagramLoader(type); const { loader, priority } = getDiagramLoaderAndPriority(type);
if (!loader) { if (!loader) {
throw new UnknownDiagramError(`Diagram ${type} not found.`); throw new UnknownDiagramError(`Diagram ${type} not found.`);
} }
// Diagram not available, loading it. // Diagram not available, loading it.
// new diagram will try getDiagram again and if fails then it is a valid throw // new diagram will try getDiagram again and if fails then it is a valid throw
const { id, diagram } = await loader(); const { id, diagram } = await loader();
registerDiagram(id, diagram); registerDiagram(id, diagram, priority);
} }
return new Diagram(text, metadata); return new Diagram(text, metadata);
}; };

View File

@@ -61,18 +61,13 @@ export interface MermaidConfig {
* You may also use `themeCSS` to override this value. * You may also use `themeCSS` to override this value.
* *
*/ */
theme?: string | 'default' | 'forest' | 'dark' | 'neutral' | 'null'; theme?: 'default' | 'forest' | 'dark' | 'neutral' | 'null';
themeVariables?: any; themeVariables?: any;
themeCSS?: string; themeCSS?: string;
/** /**
* The maximum allowed size of the users text diagram * The maximum allowed size of the users text diagram
*/ */
maxTextSize?: number; maxTextSize?: number;
/**
* Defines the maximum number of edges that can be drawn in a graph.
*
*/
maxEdges?: number;
darkMode?: boolean; darkMode?: boolean;
htmlLabels?: boolean; htmlLabels?: boolean;
/** /**
@@ -87,26 +82,11 @@ export interface MermaidConfig {
* This option decides the amount of logging to be used by mermaid. * This option decides the amount of logging to be used by mermaid.
* *
*/ */
logLevel?: logLevel?: 'trace' | 0 | 'debug' | 1 | 'info' | 2 | 'warn' | 3 | 'error' | 4 | 'fatal' | 5;
| number
| string
| 0
| 2
| 1
| 'trace'
| 'debug'
| 'info'
| 'warn'
| 'error'
| 'fatal'
| 3
| 4
| 5
| undefined;
/** /**
* Level of trust for parsed diagram * Level of trust for parsed diagram
*/ */
securityLevel?: string | 'strict' | 'loose' | 'antiscript' | 'sandbox' | undefined; securityLevel?: 'strict' | 'loose' | 'antiscript' | 'sandbox';
/** /**
* Dictates whether mermaid starts on Page load * Dictates whether mermaid starts on Page load
*/ */
@@ -917,7 +897,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
/** /**
* Directional bias for layout of entities * Directional bias for layout of entities
*/ */
layoutDirection?: string | 'TB' | 'BT' | 'LR' | 'RL'; layoutDirection?: 'TB' | 'BT' | 'LR' | 'RL';
/** /**
* The minimum width of an entity box. Expressed in pixels. * The minimum width of an entity box. Expressed in pixels.
*/ */
@@ -982,7 +962,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig {
* Decides which rendering engine that is to be used for the rendering. * Decides which rendering engine that is to be used for the rendering.
* *
*/ */
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk'; defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk';
} }
/** /**
* This interface was referenced by `MermaidConfig`'s JSON-Schema * This interface was referenced by `MermaidConfig`'s JSON-Schema
@@ -1006,7 +986,7 @@ export interface ClassDiagramConfig extends BaseDiagramConfig {
* Decides which rendering engine that is to be used for the rendering. * Decides which rendering engine that is to be used for the rendering.
* *
*/ */
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk'; defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk';
nodeSpacing?: number; nodeSpacing?: number;
rankSpacing?: number; rankSpacing?: number;
/** /**
@@ -1066,7 +1046,7 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
/** /**
* Multiline message alignment * Multiline message alignment
*/ */
messageAlign?: string | 'left' | 'center' | 'right'; messageAlign?: 'left' | 'center' | 'right';
/** /**
* Prolongs the edge of the diagram downwards. * Prolongs the edge of the diagram downwards.
* *
@@ -1145,7 +1125,7 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
/** /**
* Multiline message alignment * Multiline message alignment
*/ */
messageAlign?: string | 'left' | 'center' | 'right'; messageAlign?: 'left' | 'center' | 'right';
/** /**
* Prolongs the edge of the diagram downwards. * Prolongs the edge of the diagram downwards.
* *
@@ -1256,7 +1236,7 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
* Controls the display mode. * Controls the display mode.
* *
*/ */
displayMode?: string | 'compact'; displayMode?: '' | 'compact';
/** /**
* On which day a week-based interval should start * On which day a week-based interval should start
* *
@@ -1315,7 +1295,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
/** /**
* Multiline message alignment * Multiline message alignment
*/ */
messageAlign?: string | 'left' | 'center' | 'right'; messageAlign?: 'left' | 'center' | 'right';
/** /**
* Mirror actors under diagram * Mirror actors under diagram
* *
@@ -1372,7 +1352,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
/** /**
* This sets the text alignment of actor-attached notes * This sets the text alignment of actor-attached notes
*/ */
noteAlign?: string | 'left' | 'center' | 'right'; noteAlign?: 'left' | 'center' | 'right';
/** /**
* This sets the font size of actor messages * This sets the font size of actor messages
*/ */
@@ -1448,7 +1428,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
* Defines how mermaid renders curves for flowcharts. * Defines how mermaid renders curves for flowcharts.
* *
*/ */
curve?: string | 'basis' | 'linear' | 'cardinal'; curve?: 'basis' | 'linear' | 'cardinal';
/** /**
* Represents the padding between the labels and the shape * Represents the padding between the labels and the shape
* *
@@ -1460,7 +1440,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
* Decides which rendering engine that is to be used for the rendering. * Decides which rendering engine that is to be used for the rendering.
* *
*/ */
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk'; defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk';
/** /**
* Width of nodes where text is wrapped. * Width of nodes where text is wrapped.
* *

View File

@@ -1,79 +0,0 @@
import type { Mocked } from 'vitest';
import type { SVG } from '../diagram-api/types.js';
import { addEdgeMarkers } from './edgeMarker.js';
describe('addEdgeMarker', () => {
const svgPath = {
attr: vitest.fn(),
} as unknown as Mocked<SVG>;
const url = 'http://example.com';
const id = 'test';
const diagramType = 'test';
beforeEach(() => {
svgPath.attr.mockReset();
});
it('should add markers for arrow_cross:arrow_point', () => {
const arrowTypeStart = 'arrow_cross';
const arrowTypeEnd = 'arrow_point';
addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-start',
`url(${url}#${id}_${diagramType}-crossStart)`
);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-end',
`url(${url}#${id}_${diagramType}-pointEnd)`
);
});
it('should add markers for aggregation:arrow_point', () => {
const arrowTypeStart = 'aggregation';
const arrowTypeEnd = 'arrow_point';
addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-start',
`url(${url}#${id}_${diagramType}-aggregationStart)`
);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-end',
`url(${url}#${id}_${diagramType}-pointEnd)`
);
});
it('should add markers for arrow_point:aggregation', () => {
const arrowTypeStart = 'arrow_point';
const arrowTypeEnd = 'aggregation';
addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-start',
`url(${url}#${id}_${diagramType}-pointStart)`
);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-end',
`url(${url}#${id}_${diagramType}-aggregationEnd)`
);
});
it('should add markers for aggregation:composition', () => {
const arrowTypeStart = 'aggregation';
const arrowTypeEnd = 'composition';
addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-start',
`url(${url}#${id}_${diagramType}-aggregationStart)`
);
expect(svgPath.attr).toHaveBeenCalledWith(
'marker-end',
`url(${url}#${id}_${diagramType}-compositionEnd)`
);
});
it('should not add invalid markers', () => {
const arrowTypeStart = 'this is an invalid marker';
const arrowTypeEnd = ') url(https://my-malicious-site.example)';
addEdgeMarkers(svgPath, { arrowTypeStart, arrowTypeEnd }, url, id, diagramType);
expect(svgPath.attr).not.toHaveBeenCalled();
});
});

View File

@@ -1,57 +0,0 @@
import type { SVG } from '../diagram-api/types.js';
import { log } from '../logger.js';
import type { EdgeData } from '../types.js';
/**
* Adds SVG markers to a path element based on the arrow types specified in the edge.
*
* @param svgPath - The SVG path element to add markers to.
* @param edge - The edge data object containing the arrow types.
* @param url - The URL of the SVG marker definitions.
* @param id - The ID prefix for the SVG marker definitions.
* @param diagramType - The type of diagram being rendered.
*/
export const addEdgeMarkers = (
svgPath: SVG,
edge: Pick<EdgeData, 'arrowTypeStart' | 'arrowTypeEnd'>,
url: string,
id: string,
diagramType: string
) => {
if (edge.arrowTypeStart) {
addEdgeMarker(svgPath, 'start', edge.arrowTypeStart, url, id, diagramType);
}
if (edge.arrowTypeEnd) {
addEdgeMarker(svgPath, 'end', edge.arrowTypeEnd, url, id, diagramType);
}
};
const arrowTypesMap = {
arrow_cross: 'cross',
arrow_point: 'point',
arrow_barb: 'barb',
arrow_circle: 'circle',
aggregation: 'aggregation',
extension: 'extension',
composition: 'composition',
dependency: 'dependency',
lollipop: 'lollipop',
} as const;
const addEdgeMarker = (
svgPath: SVG,
position: 'start' | 'end',
arrowType: string,
url: string,
id: string,
diagramType: string
) => {
const endMarkerType = arrowTypesMap[arrowType as keyof typeof arrowTypesMap];
if (!endMarkerType) {
log.warn(`Unknown arrow type: ${arrowType}`);
return; // unknown arrow type, ignore
}
const suffix = position === 'start' ? 'Start' : 'End';
svgPath.attr(`marker-${position}`, `url(${url}#${id}_${diagramType}-${endMarkerType}${suffix})`);
};

View File

@@ -6,7 +6,6 @@ import { getConfig } from '../diagram-api/diagramAPI.js';
import utils from '../utils.js'; import utils from '../utils.js';
import { evaluate } from '../diagrams/common/common.js'; import { evaluate } from '../diagrams/common/common.js';
import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js'; import { getLineFunctionsWithOffset } from '../utils/lineWithOffset.js';
import { addEdgeMarkers } from './edgeMarker.js';
let edgeLabels = {}; let edgeLabels = {};
let terminalLabels = {}; let terminalLabels = {};
@@ -507,8 +506,108 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph
log.info('arrowTypeStart', edge.arrowTypeStart); log.info('arrowTypeStart', edge.arrowTypeStart);
log.info('arrowTypeEnd', edge.arrowTypeEnd); log.info('arrowTypeEnd', edge.arrowTypeEnd);
addEdgeMarkers(svgPath, edge, url, id, diagramType); switch (edge.arrowTypeStart) {
case 'arrow_cross':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-crossStart' + ')'
);
break;
case 'arrow_point':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-pointStart' + ')'
);
break;
case 'arrow_barb':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-barbStart' + ')'
);
break;
case 'arrow_circle':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-circleStart' + ')'
);
break;
case 'aggregation':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationStart' + ')'
);
break;
case 'extension':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-extensionStart' + ')'
);
break;
case 'composition':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-compositionStart' + ')'
);
break;
case 'dependency':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyStart' + ')'
);
break;
case 'lollipop':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopStart' + ')'
);
break;
default:
}
switch (edge.arrowTypeEnd) {
case 'arrow_cross':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-crossEnd' + ')');
break;
case 'arrow_point':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-pointEnd' + ')');
break;
case 'arrow_barb':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-barbEnd' + ')');
break;
case 'arrow_circle':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-circleEnd' + ')');
break;
case 'aggregation':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationEnd' + ')'
);
break;
case 'extension':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-extensionEnd' + ')'
);
break;
case 'composition':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-compositionEnd' + ')'
);
break;
case 'dependency':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyEnd' + ')'
);
break;
case 'lollipop':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopEnd' + ')'
);
break;
default:
}
let paths = {}; let paths = {};
if (pointsHasChanged) { if (pointsHasChanged) {
paths.updatedPath = points; paths.updatedPath = points;

View File

@@ -1013,7 +1013,6 @@ const class_box = (parent, node) => {
}); });
rect rect
.attr('style', node.style)
.attr('class', 'outer title-state') .attr('class', 'outer title-state')
.attr('x', -maxWidth / 2 - halfPadding) .attr('x', -maxWidth / 2 - halfPadding)
.attr('y', -(maxHeight / 2) - halfPadding) .attr('y', -(maxHeight / 2) - halfPadding)

View File

@@ -80,9 +80,7 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
? getConfig().fontSize ? getConfig().fontSize
: window.getComputedStyle(document.body).fontSize; : window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5; const enlargingFactor = 5;
const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px'; img.style.width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';
img.style.minWidth = width;
img.style.maxWidth = width;
} else { } else {
img.style.width = '100%'; img.style.width = '100%';
} }

View File

@@ -61,23 +61,37 @@ export const detectType = function (text: string, config?: MermaidConfig): strin
* The first detector to return `true` is the diagram that will be loaded * The first detector to return `true` is the diagram that will be loaded
* and used, so put more specific detectors at the beginning! * and used, so put more specific detectors at the beginning!
* *
* If two diagrams are registered with the same id,
* the one with higher `priority` property will be used.
*
* @param diagrams - Diagrams to lazy load, and their detectors, in order of importance. * @param diagrams - Diagrams to lazy load, and their detectors, in order of importance.
*/ */
export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => { export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => {
for (const { id, detector, loader } of diagrams) { for (const { id, detector, priority, loader } of diagrams) {
addDetector(id, detector, loader); addDetector(id, detector, priority ?? 0, loader);
} }
}; };
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => { export const addDetector = (
if (detectors[key]) { key: string,
log.error(`Detector with key ${key} already exists`); detector: DiagramDetector,
} else { priority: number,
detectors[key] = { detector, loader }; loader?: DiagramLoader
) => {
if (detectors[key] && priority <= detectors[key].priority) {
log.error(
`Detector with key ${key} already exists with priority ${detectors[key].priority}. Cannot add new detector with priority ${priority}`
);
return;
} }
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
detectors[key] = { detector, loader, priority };
log.debug(
`Detector with key ${key} added with priority ${priority} ${loader ? 'and loader' : ''}`
);
}; };
export const getDiagramLoader = (key: string) => { export const getDiagramLoaderAndPriority = (key: string) => {
return detectors[key].loader; const { loader, priority } = detectors[key];
return { loader, priority };
}; };

View File

@@ -31,7 +31,7 @@ export const addDiagrams = () => {
// This is added here to avoid race-conditions. // This is added here to avoid race-conditions.
// We could optimize the loading logic somehow. // We could optimize the loading logic somehow.
hasLoadedDiagrams = true; hasLoadedDiagrams = true;
registerDiagram('error', errorDiagram, (text) => { registerDiagram('error', errorDiagram, 0, (text) => {
return text.toLowerCase().trim() === 'error'; return text.toLowerCase().trim() === 'error';
}); });
registerDiagram( registerDiagram(
@@ -50,7 +50,6 @@ export const addDiagrams = () => {
}, },
}, },
parser: { parser: {
parser: { yy: {} },
parse: () => { parse: () => {
throw new Error( throw new Error(
'Diagrams beginning with --- are not valid. ' + 'Diagrams beginning with --- are not valid. ' +
@@ -61,6 +60,7 @@ export const addDiagrams = () => {
}, },
init: () => null, // no op init: () => null, // no op
}, },
0,
(text) => { (text) => {
return text.toLowerCase().trimStart().startsWith('---'); return text.toLowerCase().trimStart().startsWith('---');
} }

View File

@@ -39,7 +39,6 @@ describe('DiagramAPI', () => {
parse: (_text) => { parse: (_text) => {
return; return;
}, },
parser: { yy: {} },
}, },
renderer: { renderer: {
draw: () => { draw: () => {
@@ -48,6 +47,7 @@ describe('DiagramAPI', () => {
}, },
styles: {}, styles: {},
}, },
0,
detector detector
); );
expect(getDiagram('loki')).not.toBeNull(); expect(getDiagram('loki')).not.toBeNull();

View File

@@ -29,7 +29,7 @@ export const getCommonDb = () => {
return _commonDb; return _commonDb;
}; };
const diagrams: Record<string, DiagramDefinition> = {}; const diagrams: Record<string, DiagramDefinition & { priority: number }> = {};
export interface Detectors { export interface Detectors {
[key: string]: DiagramDetector; [key: string]: DiagramDetector;
} }
@@ -37,7 +37,8 @@ export interface Detectors {
/** /**
* Registers the given diagram with Mermaid. * Registers the given diagram with Mermaid.
* *
* Can be used for third-party custom diagrams. * To be used internally by Mermaid.
* Use `mermaid.registerExternalDiagrams` to register external diagrams.
* *
* @param id - A unique ID for the given diagram. * @param id - A unique ID for the given diagram.
* @param diagram - The diagram definition. * @param diagram - The diagram definition.
@@ -46,14 +47,17 @@ export interface Detectors {
export const registerDiagram = ( export const registerDiagram = (
id: string, id: string,
diagram: DiagramDefinition, diagram: DiagramDefinition,
priority: number,
detector?: DiagramDetector detector?: DiagramDetector
) => { ) => {
if (diagrams[id]) { if (diagrams[id] && priority <= diagrams[id].priority) {
throw new Error(`Diagram ${id} already registered.`); throw new Error(
`Diagram ${id} already registered with priority ${diagrams[id].priority}. Cannot add new diagram with priority ${priority}`
);
} }
diagrams[id] = diagram; diagrams[id] = { ...diagram, priority };
if (detector) { if (detector) {
addDetector(id, detector); addDetector(id, detector, priority);
} }
addStylesForDiagram(id, diagram.styles); addStylesForDiagram(id, diagram.styles);

View File

@@ -1,4 +1,4 @@
import type { MermaidConfig } from '../config.type.js'; import type { GanttDiagramConfig, MermaidConfig } from '../config.type.js';
import { frontMatterRegex } from './regexes.js'; import { frontMatterRegex } from './regexes.js';
// The "* as yaml" part is necessary for tree-shaking // The "* as yaml" part is necessary for tree-shaking
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
@@ -6,7 +6,7 @@ import * as yaml from 'js-yaml';
interface FrontMatterMetadata { interface FrontMatterMetadata {
title?: string; title?: string;
// Allows custom display modes. Currently used for compact mode in gantt charts. // Allows custom display modes. Currently used for compact mode in gantt charts.
displayMode?: string; displayMode?: GanttDiagramConfig['displayMode'];
config?: MermaidConfig; config?: MermaidConfig;
} }
@@ -44,7 +44,7 @@ export function extractFrontMatter(text: string): FrontMatterResult {
// Only add properties that are explicitly supported, if they exist // Only add properties that are explicitly supported, if they exist
if (parsed.displayMode) { if (parsed.displayMode) {
metadata.displayMode = parsed.displayMode.toString(); metadata.displayMode = parsed.displayMode.toString() as GanttDiagramConfig['displayMode'];
} }
if (parsed.title) { if (parsed.title) {
metadata.title = parsed.title.toString(); metadata.title = parsed.title.toString();

View File

@@ -6,7 +6,7 @@ export const loadRegisteredDiagrams = async () => {
log.debug(`Loading registered diagrams`); log.debug(`Loading registered diagrams`);
// Load all lazy loaded diagrams in parallel // Load all lazy loaded diagrams in parallel
const results = await Promise.allSettled( const results = await Promise.allSettled(
Object.entries(detectors).map(async ([key, { detector, loader }]) => { Object.entries(detectors).map(async ([key, { detector, loader, priority }]) => {
if (loader) { if (loader) {
try { try {
getDiagram(key); getDiagram(key);
@@ -14,7 +14,7 @@ export const loadRegisteredDiagrams = async () => {
try { try {
// Register diagram if it is not already registered // Register diagram if it is not already registered
const { diagram, id } = await loader(); const { diagram, id } = await loader();
registerDiagram(id, diagram, detector); registerDiagram(id, diagram, priority, detector);
} catch (err) { } catch (err) {
// Remove failed diagram from detectors // Remove failed diagram from detectors
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`); log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);

View File

@@ -76,13 +76,23 @@ export interface DiagramDefinition {
export interface DetectorRecord { export interface DetectorRecord {
detector: DiagramDetector; detector: DiagramDetector;
priority: number;
loader?: DiagramLoader; loader?: DiagramLoader;
} }
/**
* External diagrams, which are not bundled with mermaid should expose the following to be registered using the `mermaid.registerExternalDiagrams` function.
*
* @param id - An ID for the given diagram. If two diagrams are registered with the same ID, the one with the higher priority will be used.
* @param detector - Function that returns `true` if a given mermaid text satisfies with this diagram definition.
* @param loader - Function that returns a promise of the diagram definition.
* @param priority - The priority of the diagram. Optional, defaults to 0.
*/
export interface ExternalDiagramDefinition { export interface ExternalDiagramDefinition {
id: string; id: string;
detector: DiagramDetector; detector: DiagramDetector;
loader: DiagramLoader; loader: DiagramLoader;
priority?: number;
} }
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean; export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
@@ -105,7 +115,7 @@ export type DrawDefinition = (
export interface ParserDefinition { export interface ParserDefinition {
parse: (text: string) => void; parse: (text: string) => void;
parser: { yy: DiagramDB }; parser?: { yy: DiagramDB };
} }
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>; export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>;

View File

@@ -21,6 +21,7 @@ describe('diagram detection', () => {
addDetector( addDetector(
'loki', 'loki',
(str) => str.startsWith('loki'), (str) => str.startsWith('loki'),
0,
() => () =>
Promise.resolve({ Promise.resolve({
id: 'loki', id: 'loki',
@@ -30,9 +31,6 @@ describe('diagram detection', () => {
parse: () => { parse: () => {
// no-op // no-op
}, },
parser: {
yy: {},
},
}, },
renderer: { renderer: {
draw: () => { draw: () => {
@@ -48,6 +46,37 @@ describe('diagram detection', () => {
expect(diagram.type).toBe('loki'); expect(diagram.type).toBe('loki');
}); });
test('should allow external diagrams to override internal ones with same ID', async () => {
addDetector(
'flowchart-elk',
(str) => str.startsWith('flowchart-elk'),
1,
() =>
Promise.resolve({
id: 'flowchart-elk',
diagram: {
db: {
getDiagramTitle: () => 'overridden',
},
parser: {
parse: () => {
// no-op
},
},
renderer: {
draw: () => {
// no-op
},
},
styles: {},
},
})
);
const diagram = (await getDiagramFromText('flowchart-elk TD; A-->B')) as Diagram;
expect(diagram).toBeInstanceOf(Diagram);
expect(diagram.db.getDiagramTitle?.()).toBe('overridden');
});
test('should throw the right error for incorrect diagram', async () => { test('should throw the right error for incorrect diagram', async () => {
await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(` await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
"Parse error on line 2: "Parse error on line 2:

View File

@@ -84,7 +84,6 @@ export const addClass = function (_id: string) {
methods: [], methods: [],
members: [], members: [],
annotations: [], annotations: [],
styles: [],
domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter, domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter,
} as ClassNode; } as ClassNode;
@@ -215,7 +214,7 @@ export const cleanupLabel = function (label: string) {
}; };
/** /**
* Called by parser when assigning cssClass to a class * Called by parser when a special node is found, e.g. a clickable element.
* *
* @param ids - Comma separated list of ids * @param ids - Comma separated list of ids
* @param className - Class to add * @param className - Class to add
@@ -447,27 +446,11 @@ const getNamespaces = function (): NamespaceMap {
* @public * @public
*/ */
export const addClassesToNamespace = function (id: string, classNames: string[]) { export const addClassesToNamespace = function (id: string, classNames: string[]) {
if (namespaces[id] === undefined) { if (namespaces[id] !== undefined) {
return; classNames.map((className) => {
}
for (const name of classNames) {
const { className } = splitClassNameAndType(name);
classes[className].parent = id; classes[className].parent = id;
namespaces[id].classes[className] = classes[className]; namespaces[id].classes[className] = classes[className];
} });
};
export const setCssStyle = function (id: string, styles: string[]) {
const thisClass = classes[id];
if (!styles || !thisClass) {
return;
}
for (const s of styles) {
if (s.includes(',')) {
thisClass.styles.push(...s.split(','));
} else {
thisClass.styles.push(s);
}
} }
}; };
@@ -507,5 +490,4 @@ export default {
addClassesToNamespace, addClassesToNamespace,
getNamespace, getNamespace,
getNamespaces, getNamespaces,
setCssStyle,
}; };

View File

@@ -56,18 +56,5 @@ describe('class diagram, ', function () {
expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass'); expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass');
expect(parser.yy.getClass('Class02').cssClasses[0]).toBe('exClass'); expect(parser.yy.getClass('Class02').cssClasses[0]).toBe('exClass');
}); });
it('should be possible to apply a style to an individual node', function () {
const str =
'classDiagram\n' +
'class Class01\n class Class02\n style Class01 fill:#f9f,stroke:#333,stroke-width:4px';
parser.parse(str);
const styleElements = parser.yy.getClass('Class01').styles;
expect(styleElements[0]).toBe('fill:#f9f');
expect(styleElements[1]).toBe('stroke:#333');
expect(styleElements[2]).toBe('stroke-width:4px');
});
}); });
}); });

View File

@@ -409,7 +409,6 @@ class C13["With Città foreign language"]
}, },
], ],
"methods": [], "methods": [],
"styles": [],
"type": "", "type": "",
} }
`); `);
@@ -1044,19 +1043,6 @@ foo()
`; `;
parser.parse(str); parser.parse(str);
}); });
it('should handle namespace with generic types', () => {
parser.parse(`classDiagram
namespace space {
class Square~Shape~{
int id
List~int~ position
setPoints(List~int~ points)
getPoints() List~int~
}
}`);
});
}); });
}); });

View File

@@ -104,7 +104,7 @@ export const addClasses = function (
*/ */
const cssClassStr = vertex.cssClasses.join(' '); const cssClassStr = vertex.cssClasses.join(' ');
const styles = getStylesFromArray(vertex.styles); const styles = { labelStyle: '', style: '' }; //getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition // Use vertex id as text in the box if no text is provided by the graph definition
const vertexText = vertex.label ?? vertex.id; const vertexText = vertex.label ?? vertex.id;

View File

@@ -10,7 +10,6 @@ export interface ClassNode {
members: ClassMember[]; members: ClassMember[];
annotations: string[]; annotations: string[];
domId: string; domId: string;
styles: string[];
parent?: string; parent?: string;
link?: string; link?: string;
linkTarget?: string; linkTarget?: string;

View File

@@ -60,7 +60,6 @@ Function arguments are optional: 'call <callback_name>()' simply executes 'callb
<string>["] this.popState(); <string>["] this.popState();
<string>[^"]* return "STR"; <string>[^"]* return "STR";
<*>["] this.begin("string"); <*>["] this.begin("string");
"style" return 'STYLE';
<INITIAL,namespace>"namespace" { this.begin('namespace'); return 'NAMESPACE'; } <INITIAL,namespace>"namespace" { this.begin('namespace'); return 'NAMESPACE'; }
<namespace>\s*(\r?\n)+ { this.popState(); return 'NEWLINE'; } <namespace>\s*(\r?\n)+ { this.popState(); return 'NEWLINE'; }
@@ -128,10 +127,6 @@ line was introduced with 'click'.
<*>\- return 'MINUS'; <*>\- return 'MINUS';
<*>"." return 'DOT'; <*>"." return 'DOT';
<*>\+ return 'PLUS'; <*>\+ return 'PLUS';
":" return 'COLON';
"," return 'COMMA';
\# return 'BRKT';
"#" return 'BRKT';
<*>\% return 'PCT'; <*>\% return 'PCT';
<*>"=" return 'EQUALS'; <*>"=" return 'EQUALS';
<*>\= return 'EQUALS'; <*>\= return 'EQUALS';
@@ -203,7 +198,6 @@ line was introduced with 'click'.
[\uFFD2-\uFFD7\uFFDA-\uFFDC] [\uFFD2-\uFFD7\uFFDA-\uFFDC]
return 'UNICODE_TEXT'; return 'UNICODE_TEXT';
<*>\s return 'SPACE'; <*>\s return 'SPACE';
\s return 'SPACE';
<*><<EOF>> return 'EOF'; <*><<EOF>> return 'EOF';
/lex /lex
@@ -260,7 +254,6 @@ statement
| memberStatement | memberStatement
| annotationStatement | annotationStatement
| clickStatement | clickStatement
| styleStatement
| cssClassStatement | cssClassStatement
| noteStatement | noteStatement
| direction | direction
@@ -372,26 +365,10 @@ clickStatement
| CLICK className HREF STR STR LINK_TARGET {$$ = $1;yy.setLink($2, $4, $6);yy.setTooltip($2, $5);} | CLICK className HREF STR STR LINK_TARGET {$$ = $1;yy.setLink($2, $4, $6);yy.setTooltip($2, $5);}
; ;
styleStatement
:STYLE ALPHA stylesOpt {$$ = $STYLE;yy.setCssStyle($2,$stylesOpt);}
;
cssClassStatement cssClassStatement
: CSSCLASS STR ALPHA {yy.setCssClass($2, $3);} : CSSCLASS STR alphaNumToken {yy.setCssClass($2, $3);}
; ;
stylesOpt
: style {$$ = [$style]}
| stylesOpt COMMA style {$stylesOpt.push($style);$$ = $stylesOpt;}
;
style
: styleComponent
| style styleComponent {$$ = $style + $styleComponent;}
;
styleComponent: ALPHA | NUM | COLON | UNIT | SPACE | BRKT | STYLE | PCT | LABEL;
commentToken : textToken | graphCodeTokens ; commentToken : textToken | graphCodeTokens ;
textToken : textNoTagsToken | TAGSTART | TAGEND | '==' | '--' | PCT | DEFAULT; textToken : textNoTagsToken | TAGSTART | TAGEND | '==' | '--' | PCT | DEFAULT;

View File

@@ -145,7 +145,6 @@ g.classGroup line {
.edgeTerminals { .edgeTerminals {
font-size: 11px; font-size: 11px;
line-height: initial;
} }
.classTitleText { .classTitleText {

View File

@@ -0,0 +1,15 @@
import type { DiagramAST } from 'mermaid-parser';
import type { DiagramDB } from '../../diagram-api/types.js';
export function populateCommonDb(ast: DiagramAST, db: DiagramDB) {
if (ast.accDescr) {
db.setAccDescription?.(ast.accDescr);
}
if (ast.accTitle) {
db.setAccTitle?.(ast.accTitle);
}
if (ast.title) {
db.setDiagramTitle?.(ast.title);
}
}

View File

@@ -5,7 +5,6 @@ const diagram: DiagramDefinition = {
db: {}, db: {},
renderer, renderer,
parser: { parser: {
parser: { yy: {} },
parse: (): void => { parse: (): void => {
return; return;
}, },

View File

@@ -11,7 +11,6 @@ import common from '../../common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../../utils.js'; import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
import ELK from 'elkjs/lib/elk.bundled.js'; import ELK from 'elkjs/lib/elk.bundled.js';
import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js'; import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js';
import { addEdgeMarkers } from '../../../dagre-wrapper/edgeMarker.js';
const elk = new ELK(); const elk = new ELK();
@@ -587,7 +586,108 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
} }
// look in edge data and decide which marker to use // look in edge data and decide which marker to use
addEdgeMarkers(svgPath, edgeData, url, id, diagramType); switch (edgeData.arrowTypeStart) {
case 'arrow_cross':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-crossStart' + ')'
);
break;
case 'arrow_point':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-pointStart' + ')'
);
break;
case 'arrow_barb':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-barbStart' + ')'
);
break;
case 'arrow_circle':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-circleStart' + ')'
);
break;
case 'aggregation':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationStart' + ')'
);
break;
case 'extension':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-extensionStart' + ')'
);
break;
case 'composition':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-compositionStart' + ')'
);
break;
case 'dependency':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyStart' + ')'
);
break;
case 'lollipop':
svgPath.attr(
'marker-start',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopStart' + ')'
);
break;
default:
}
switch (edgeData.arrowTypeEnd) {
case 'arrow_cross':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-crossEnd' + ')');
break;
case 'arrow_point':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-pointEnd' + ')');
break;
case 'arrow_barb':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-barbEnd' + ')');
break;
case 'arrow_circle':
svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-circleEnd' + ')');
break;
case 'aggregation':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-aggregationEnd' + ')'
);
break;
case 'extension':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-extensionEnd' + ')'
);
break;
case 'composition':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-compositionEnd' + ')'
);
break;
case 'dependency':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-dependencyEnd' + ')'
);
break;
case 'lollipop':
svgPath.attr(
'marker-end',
'url(' + url + '#' + id + '_' + diagramType + '-lollipopEnd' + ')'
);
break;
default:
}
}; };
/** /**

View File

@@ -12,6 +12,7 @@ import {
setDiagramTitle, setDiagramTitle,
getDiagramTitle, getDiagramTitle,
} from '../common/commonDb.js'; } from '../common/commonDb.js';
import errorDiagram from '../error/errorDiagram.js';
const MERMAID_DOM_ID_PREFIX = 'flowchart-'; const MERMAID_DOM_ID_PREFIX = 'flowchart-';
let vertexCounter = 0; let vertexCounter = 0;
@@ -91,6 +92,7 @@ export const addVertex = function (_id, textObj, type, style, classes, dir, prop
if (txt[0] === '"' && txt[txt.length - 1] === '"') { if (txt[0] === '"' && txt[txt.length - 1] === '"') {
txt = txt.substring(1, txt.length - 1); txt = txt.substring(1, txt.length - 1);
} }
vertices[id].text = txt; vertices[id].text = txt;
} else { } else {
if (vertices[id].text === undefined) { if (vertices[id].text === undefined) {
@@ -158,17 +160,11 @@ export const addSingleLink = function (_start, _end, type) {
if (edge?.length > 10) { if (edge?.length > 10) {
edge.length = 10; edge.length = 10;
} }
if (edges.length < (config.maxEdges ?? 500)) { if (edges.length < 280) {
log.info('abc78 pushing edge...'); log.info('abc78 pushing edge...');
edges.push(edge); edges.push(edge);
} else { } else {
throw new Error( throw new Error('Too many edges');
`Edge limit exceeded. ${edges.length} edges found, but the limit is ${config.maxEdges}.
Initialize mermaid with maxEdges set to a higher number to allow more edges.
You cannot set this config via configuration inside the diagram as it is a secure config.
You have to call mermaid.initialize.`
);
} }
}; };
export const addLink = function (_start, _end, type) { export const addLink = function (_start, _end, type) {
@@ -464,7 +460,6 @@ export const clear = function (ver = 'gen-1') {
tooltips = {}; tooltips = {};
firstGraphFlag = true; firstGraphFlag = true;
version = ver; version = ver;
config = getConfig();
commonClear(); commonClear();
}; };
export const setGen = (ver) => { export const setGen = (ver) => {

View File

@@ -255,12 +255,11 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
log.debug('in mergeBranch'); log.debug('in mergeBranch');
}; };
export const cherryPick = function (sourceId, targetId, tag, parentCommitId) { export const cherryPick = function (sourceId, targetId, tag) {
log.debug('Entering cherryPick:', sourceId, targetId, tag); log.debug('Entering cherryPick:', sourceId, targetId, tag);
sourceId = common.sanitizeText(sourceId, getConfig()); sourceId = common.sanitizeText(sourceId, getConfig());
targetId = common.sanitizeText(targetId, getConfig()); targetId = common.sanitizeText(targetId, getConfig());
tag = common.sanitizeText(tag, getConfig()); tag = common.sanitizeText(tag, getConfig());
parentCommitId = common.sanitizeText(parentCommitId, getConfig());
if (!sourceId || commits[sourceId] === undefined) { if (!sourceId || commits[sourceId] === undefined) {
let error = new Error( let error = new Error(
@@ -275,21 +274,20 @@ export const cherryPick = function (sourceId, targetId, tag, parentCommitId) {
}; };
throw error; throw error;
} }
let sourceCommit = commits[sourceId]; let sourceCommit = commits[sourceId];
let sourceCommitBranch = sourceCommit.branch; let sourceCommitBranch = sourceCommit.branch;
if ( if (sourceCommit.type === commitType.MERGE) {
parentCommitId &&
!(Array.isArray(sourceCommit.parents) && sourceCommit.parents.includes(parentCommitId))
) {
let error = new Error( let error = new Error(
'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.' 'Incorrect usage of "cherryPick". Source commit should not be a merge commit'
);
throw error;
}
if (sourceCommit.type === commitType.MERGE && !parentCommitId) {
let error = new Error(
'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.'
); );
error.hash = {
text: 'cherryPick ' + sourceId + ' ' + targetId,
token: 'cherryPick ' + sourceId + ' ' + targetId,
line: '1',
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
expected: ['cherry-pick abc'],
};
throw error; throw error;
} }
if (!targetId || commits[targetId] === undefined) { if (!targetId || commits[targetId] === undefined) {
@@ -329,11 +327,7 @@ export const cherryPick = function (sourceId, targetId, tag, parentCommitId) {
parents: [head == null ? null : head.id, sourceCommit.id], parents: [head == null ? null : head.id, sourceCommit.id],
branch: curBranch, branch: curBranch,
type: commitType.CHERRY_PICK, type: commitType.CHERRY_PICK,
tag: tag: tag ?? 'cherry-pick:' + sourceCommit.id,
tag ??
`cherry-pick:${sourceCommit.id}${
sourceCommit.type === commitType.MERGE ? `|parent:${parentCommitId}` : ''
}`,
}; };
head = commit; head = commit;
commits[commit.id] = commit; commits[commit.id] = commit;

View File

@@ -673,145 +673,6 @@ describe('when parsing a gitGraph', function () {
expect(commits[cherryPickCommitID].branch).toBe('main'); expect(commits[cherryPickCommitID].branch).toBe('main');
}); });
it('should support cherry-picking of merge commits', function () {
const str = `gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
cherry-pick id: "M" parent:"B"
`;
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = Object.keys(commits)[4];
expect(commits[cherryPickCommitID].tag).toBe('cherry-pick:M|parent:B');
expect(commits[cherryPickCommitID].branch).toBe('release');
});
it('should support cherry-picking of merge commits with tag', function () {
const str = `gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
cherry-pick id: "M" parent:"ZERO" tag: "v1.0"
`;
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = Object.keys(commits)[4];
expect(commits[cherryPickCommitID].tag).toBe('v1.0');
expect(commits[cherryPickCommitID].branch).toBe('release');
});
it('should support cherry-picking of merge commits with additional commit', function () {
const str = `gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
commit id: "C"
cherry-pick id: "M" tag: "v2.1:ZERO" parent:"ZERO"
commit id: "D"
`;
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = Object.keys(commits)[5];
expect(commits[cherryPickCommitID].tag).toBe('v2.1:ZERO');
expect(commits[cherryPickCommitID].branch).toBe('release');
});
it('should support cherry-picking of merge commits with empty tag', function () {
const str = `gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
commit id: "C"
cherry-pick id:"M" parent: "ZERO" tag:""
commit id: "D"
cherry-pick id:"M" tag:"" parent: "B"
`;
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = Object.keys(commits)[5];
const cherryPickCommitID2 = Object.keys(commits)[7];
expect(commits[cherryPickCommitID].tag).toBe('');
expect(commits[cherryPickCommitID2].tag).toBe('');
expect(commits[cherryPickCommitID].branch).toBe('release');
});
it('should fail cherry-picking of merge commits if the parent of merge commits is not specified', function () {
expect(() =>
parser
.parse(
`gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
commit id: "C"
cherry-pick id:"M"
`
)
.toThrow(
'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.'
)
);
});
it('should fail cherry-picking of merge commits when the parent provided is not an immediate parent of cherry picked commit', function () {
expect(() =>
parser
.parse(
`gitGraph
commit id: "ZERO"
branch feature
branch release
checkout feature
commit id: "A"
commit id: "B"
checkout main
merge feature id: "M"
checkout release
commit id: "C"
cherry-pick id:"M" parent: "A"
`
)
.toThrow(
'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.'
)
);
});
it('should throw error when try to branch existing branch: main', function () { it('should throw error when try to branch existing branch: main', function () {
const str = `gitGraph const str = `gitGraph
commit commit

View File

@@ -338,34 +338,26 @@ const drawCommits = (svg, commits, modifyGraph) => {
}; };
/** /**
* Detect if there are commits * Detect if there are other commits between commit1's x-position and commit2's x-position on the
* between commitA's x-position * same branch as commit2.
* and commitB's x-position on the
* same branch as commitA, where
* commitA isn't main
* *
* @param {any} commitA * @param {any} commit1
* @param {any} commitB * @param {any} commit2
* @param branchToGetCurve
* @param p1
* @param p2
* @param allCommits * @param allCommits
* @returns {boolean} * @returns {boolean} If there are commits between commit1's x-position and commit2's x-position
* If there are commits between
* commitA's x-position
* and commitB's x-position
* on the source branch, where
* source branch is not main
* return true
*/ */
const shouldRerouteArrow = (commitA, commitB, p1, p2, allCommits) => { const hasOverlappingCommits = (commit1, commit2, allCommits) => {
const commitBIsFurthest = dir === 'TB' ? p1.x < p2.x : p1.y < p2.y; // Find commits on the same branch as commit2
const branchToGetCurve = commitBIsFurthest ? commitB.branch : commitA.branch; const keys = Object.keys(allCommits);
const isOnBranchToGetCurve = (x) => x.branch === branchToGetCurve; const overlappingComits = keys.filter((key) => {
const isBetweenCommits = (x) => x.seq > commitA.seq && x.seq < commitB.seq; return (
return Object.values(allCommits).some((commitX) => { allCommits[key].branch === commit2.branch &&
return isBetweenCommits(commitX) && isOnBranchToGetCurve(commitX); allCommits[key].seq > commit1.seq &&
allCommits[key].seq < commit2.seq
);
}); });
return overlappingComits.length > 0;
}; };
/** /**
@@ -396,61 +388,49 @@ const findLane = (y1, y2, depth = 0) => {
* Draw the lines between the commits. They were arrows initially. * Draw the lines between the commits. They were arrows initially.
* *
* @param {any} svg * @param {any} svg
* @param {any} commitA * @param {any} commit1
* @param {any} commitB * @param {any} commit2
* @param {any} allCommits * @param {any} allCommits
*/ */
const drawArrow = (svg, commitA, commitB, allCommits) => { const drawArrow = (svg, commit1, commit2, allCommits) => {
const p1 = commitPos[commitA.id]; // arrowStart const p1 = commitPos[commit1.id];
const p2 = commitPos[commitB.id]; // arrowEnd const p2 = commitPos[commit2.id];
const arrowNeedsRerouting = shouldRerouteArrow(commitA, commitB, p1, p2, allCommits); const overlappingCommits = hasOverlappingCommits(commit1, commit2, allCommits);
// log.debug('drawArrow', p1, p2, arrowNeedsRerouting, commitA.id, commitB.id); // log.debug('drawArrow', p1, p2, overlappingCommits, commit1.id, commit2.id);
// Lower-right quadrant logic; top-left is 0,0
let arc = ''; let arc = '';
let arc2 = ''; let arc2 = '';
let radius = 0; let radius = 0;
let offset = 0; let offset = 0;
let colorClassNum = branchPos[commitB.branch].index; let colorClassNum = branchPos[commit2.branch].index;
let lineDef; let lineDef;
if (arrowNeedsRerouting) { if (overlappingCommits) {
arc = 'A 10 10, 0, 0, 0,'; arc = 'A 10 10, 0, 0, 0,';
arc2 = 'A 10 10, 0, 0, 1,'; arc2 = 'A 10 10, 0, 0, 1,';
radius = 10; radius = 10;
offset = 10; offset = 10;
// Figure out the color of the arrow,arrows going down take the color from the destination branch
colorClassNum = branchPos[commit2.branch].index;
const lineY = p1.y < p2.y ? findLane(p1.y, p2.y) : findLane(p2.y, p1.y); const lineY = p1.y < p2.y ? findLane(p1.y, p2.y) : findLane(p2.y, p1.y);
const lineX = p1.x < p2.x ? findLane(p1.x, p2.x) : findLane(p2.x, p1.x); const lineX = p1.x < p2.x ? findLane(p1.x, p2.x) : findLane(p2.x, p1.x);
if (dir === 'TB') { if (dir === 'TB') {
if (p1.x < p2.x) { if (p1.x < p2.x) {
// Source commit is on branch position left of destination commit
// so render arrow rightward with colour of destination branch
colorClassNum = branchPos[commitB.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc2} ${lineX} ${ lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc2} ${lineX} ${
p1.y + offset p1.y + offset
} L ${lineX} ${p2.y - radius} ${arc} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`; } L ${lineX} ${p2.y - radius} ${arc} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`;
} else { } else {
// Source commit is on branch position right of destination commit
// so render arrow leftward with colour of source branch
colorClassNum = branchPos[commitA.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc} ${lineX} ${ lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc} ${lineX} ${
p1.y + offset p1.y + offset
} L ${lineX} ${p2.y - radius} ${arc2} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`; } L ${lineX} ${p2.y - radius} ${arc2} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`;
} }
} else { } else {
if (p1.y < p2.y) { if (p1.y < p2.y) {
// Source commit is on branch positioned above destination commit
// so render arrow downward with colour of destination branch
colorClassNum = branchPos[commitB.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY - radius} ${arc} ${ lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY - radius} ${arc} ${
p1.x + offset p1.x + offset
} ${lineY} L ${p2.x - radius} ${lineY} ${arc2} ${p2.x} ${lineY + offset} L ${p2.x} ${p2.y}`; } ${lineY} L ${p2.x - radius} ${lineY} ${arc2} ${p2.x} ${lineY + offset} L ${p2.x} ${p2.y}`;
} else { } else {
// Source commit is on branch positioned below destination commit
// so render arrow upward with colour of source branch
colorClassNum = branchPos[commitA.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY + radius} ${arc2} ${ lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${lineY + radius} ${arc2} ${
p1.x + offset p1.x + offset
} ${lineY} L ${p2.x - radius} ${lineY} ${arc} ${p2.x} ${lineY - offset} L ${p2.x} ${p2.y}`; } ${lineY} L ${p2.x - radius} ${lineY} ${arc} ${p2.x} ${lineY - offset} L ${p2.x} ${p2.y}`;
@@ -465,7 +445,7 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
offset = 20; offset = 20;
// Figure out the color of the arrow,arrows going down take the color from the destination branch // Figure out the color of the arrow,arrows going down take the color from the destination branch
colorClassNum = branchPos[commitB.branch].index; colorClassNum = branchPos[commit2.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${ lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc2} ${p2.x} ${
p1.y + offset p1.y + offset
@@ -478,14 +458,14 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
offset = 20; offset = 20;
// Arrows going up take the color from the source branch // Arrows going up take the color from the source branch
colorClassNum = branchPos[commitA.branch].index; colorClassNum = branchPos[commit1.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc2} ${p1.x - offset} ${ lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc2} ${p1.x - offset} ${
p2.y p2.y
} L ${p2.x} ${p2.y}`; } L ${p2.x} ${p2.y}`;
} }
if (p1.x === p2.x) { if (p1.x === p2.x) {
colorClassNum = branchPos[commitA.branch].index; colorClassNum = branchPos[commit1.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p1.x + radius} ${p1.y} ${arc} ${p1.x + offset} ${ lineDef = `M ${p1.x} ${p1.y} L ${p1.x + radius} ${p1.y} ${arc} ${p1.x + offset} ${
p2.y + radius p2.y + radius
} L ${p2.x} ${p2.y}`; } L ${p2.x} ${p2.y}`;
@@ -495,8 +475,10 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
arc = 'A 20 20, 0, 0, 0,'; arc = 'A 20 20, 0, 0, 0,';
radius = 20; radius = 20;
offset = 20; offset = 20;
// Arrows going up take the color from the target branch
colorClassNum = branchPos[commitB.branch].index; // Figure out the color of the arrow,arrows going down take the color from the destination branch
colorClassNum = branchPos[commit2.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${ lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${
p2.x p2.x
} ${p2.y}`; } ${p2.y}`;
@@ -505,15 +487,16 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
arc = 'A 20 20, 0, 0, 0,'; arc = 'A 20 20, 0, 0, 0,';
radius = 20; radius = 20;
offset = 20; offset = 20;
// Arrows going up take the color from the source branch // Arrows going up take the color from the source branch
colorClassNum = branchPos[commitA.branch].index; colorClassNum = branchPos[commit1.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${p1.y - offset} L ${ lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${p1.y - offset} L ${
p2.x p2.x
} ${p2.y}`; } ${p2.y}`;
} }
if (p1.y === p2.y) { if (p1.y === p2.y) {
colorClassNum = branchPos[commitA.branch].index; colorClassNum = branchPos[commit1.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${ lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc} ${p1.x + offset} ${p2.y} L ${
p2.x p2.x
} ${p2.y}`; } ${p2.y}`;

View File

@@ -39,7 +39,6 @@ branch(?=\s|$) return 'BRANCH';
"order:" return 'ORDER'; "order:" return 'ORDER';
merge(?=\s|$) return 'MERGE'; merge(?=\s|$) return 'MERGE';
cherry\-pick(?=\s|$) return 'CHERRY_PICK'; cherry\-pick(?=\s|$) return 'CHERRY_PICK';
"parent:" return 'PARENT_COMMIT'
// "reset" return 'RESET'; // "reset" return 'RESET';
checkout(?=\s|$) return 'CHECKOUT'; checkout(?=\s|$) return 'CHECKOUT';
"LR" return 'DIR'; "LR" return 'DIR';
@@ -110,17 +109,10 @@ branchStatement
cherryPickStatement cherryPickStatement
: CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)} : CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($3, '', undefined,$5)}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)} | CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR COMMIT_TAG STR {yy.cherryPick($3, '', $7,$5)}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR PARENT_COMMIT STR {yy.cherryPick($3, '', $5,$7)}
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR {yy.cherryPick($5, '', '')}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '')} | CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '')}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '',$5)} | CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR PARENT_COMMIT STR {yy.cherryPick($3, '', '',$7)} | CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR {yy.cherryPick($3, '', '')}
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($5, '', $3,$7)}
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR PARENT_COMMIT STR{yy.cherryPick($5, '', '',$7)}
; ;
mergeStatement mergeStatement

View File

@@ -1,24 +1,31 @@
// @ts-ignore - jison doesn't export types import { parser } from './infoParser.js';
import { parser } from './parser/info.jison';
import { db } from './infoDb.js';
describe('info diagram', () => {
beforeEach(() => {
parser.yy = db;
parser.yy.clear();
});
describe('info', () => {
it('should handle an info definition', () => { it('should handle an info definition', () => {
const str = `info`; const str = `info`;
expect(() => {
parser.parse(str); parser.parse(str);
}).not.toThrow();
expect(db.getInfo()).toBeFalsy();
}); });
it('should handle an info definition with showInfo', () => { it('should handle an info definition with showInfo', () => {
const str = `info showInfo`; const str = `info showInfo`;
expect(() => {
parser.parse(str); parser.parse(str);
}).not.toThrow();
});
expect(db.getInfo()).toBeTruthy(); it('should throw because of unsupported info grammar', () => {
const str = `info unsupported`;
expect(() => {
parser.parse(str);
}).toThrow('Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.');
});
it('should throw because of unsupported info grammar', () => {
const str = `info unsupported`;
expect(() => {
parser.parse(str);
}).toThrow('Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.');
}); });
}); });

View File

@@ -1,23 +1,10 @@
import type { InfoFields, InfoDB } from './infoTypes.js'; import type { InfoFields, InfoDB } from './infoTypes.js';
import { version } from '../../../package.json';
export const DEFAULT_INFO_DB: InfoFields = { export const DEFAULT_INFO_DB: InfoFields = { version } as const;
info: false,
} as const;
let info: boolean = DEFAULT_INFO_DB.info; export const getVersion = (): string => DEFAULT_INFO_DB.version;
export const setInfo = (toggle: boolean): void => {
info = toggle;
};
export const getInfo = (): boolean => info;
const clear = (): void => {
info = DEFAULT_INFO_DB.info;
};
export const db: InfoDB = { export const db: InfoDB = {
clear, getVersion,
setInfo,
getInfo,
}; };

View File

@@ -1,6 +1,5 @@
import type { DiagramDefinition } from '../../diagram-api/types.js'; import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore - jison doesn't export types import { parser } from './infoParser.js';
import parser from './parser/info.jison';
import { db } from './infoDb.js'; import { db } from './infoDb.js';
import { renderer } from './infoRenderer.js'; import { renderer } from './infoRenderer.js';

View File

@@ -0,0 +1,12 @@
import type { Info } from 'mermaid-parser';
import { parse } from 'mermaid-parser';
import { log } from '../../logger.js';
import type { ParserDefinition } from '../../diagram-api/types.js';
export const parser: ParserDefinition = {
parse: (input: string): void => {
const ast: Info = parse('info', input);
log.debug(ast);
},
};

Some files were not shown because too many files have changed in this diff Show More