Compare commits

..

31 Commits

Author SHA1 Message Date
Sidharth Vinod
ea7d28bf1c chore: Rename types to MermaidConfig and MermaidConfigWithDefaults 2024-05-22 23:56:30 +05:30
Sidharth Vinod
159d85e7f7 Merge branch 'develop' into sidv/ConfigType
* develop:
  chore: remove unrefSubSchemas
2024-05-13 12:16:05 +05:30
Sidharth Vinod
0326d899c4 Merge pull request #5514 from mermaid-js/sidv/removeUnrefSubschemas
chore: remove unrefSubSchemas
2024-05-13 12:12:14 +05:30
Sidharth Vinod
d7ce7aecf3 chore: Change where PartialMermaidConfig is defined 2024-05-13 10:54:42 +05:30
Sidharth Vinod
c993adfcdb chore: remove unrefSubSchemas 2024-05-13 10:14:28 +05:30
Sidharth Vinod
453802d4ce chore: Fix types of MermaidConfig 2024-05-13 09:59:01 +05:30
Sidharth Vinod
cee1cf0ce2 feat: Remove forced optionality of fields inside MermaidConfig 2024-05-13 09:56:50 +05:30
Sidharth Vinod
70198ffefa chore: Update json-schema-to-typescript 2024-05-13 09:19:56 +05:30
Sidharth Vinod
4885b311f8 chore: Build config types before building types 2024-05-13 08:47:23 +05:30
Sidharth Vinod
3274f673ab chore: Ignore vite.config.ts.timestamp- 2024-05-13 08:46:13 +05:30
Sidharth Vinod
4f642428de Merge pull request #5504 from mermaid-js/renovate/patch-all-patch
chore(deps): update all patch dependencies (patch)
2024-05-08 06:19:56 +00:00
renovate[bot]
954f5d7d42 chore(deps): update all patch dependencies 2024-05-08 06:04:26 +00:00
Sidharth Vinod
4a12c46350 Merge pull request #5503 from igorwessel/feat/add-from-to-id-in-edge
feat(state): add from, to ids for edge
2024-05-08 05:21:44 +00:00
Sidharth Vinod
9eb64cd6db Update docs 2024-05-08 10:59:18 +05:30
Sidharth Vinod
7b8e76c26f Merge pull request #5480 from mermaid-js/update-browserslist
Update Browserslist
2024-05-08 10:57:06 +05:30
Sidharth Vinod
665be1ecfe Merge pull request #5506 from conradagramont/patch-1
Added to "others" section on integration with Astro
2024-05-08 10:56:48 +05:30
Sidharth Vinod
edda73c7fc chore: Update ordering 2024-05-08 10:56:08 +05:30
Sidharth Vinod
6413529a6e Merge pull request #5490 from Timac/patch-1
Update integrations-community: Add MarkChart, a macOS app to preview …
2024-05-08 10:54:14 +05:30
Sidharth Vinod
f5e1df08a0 Merge pull request #5483 from NicolasNewman/5435/inconsistent-math-rendering
fix: inconsistent MathML rendering & erroneous <br />s being added
2024-05-08 10:53:52 +05:30
Conrad Agramont
bb2bbfdf92 Added to "others" section on integration with Astro 2024-05-07 13:24:04 -07:00
Nicolas Newman
22bd26272d Merge branch 'develop' into 5435/inconsistent-math-rendering 2024-05-06 09:53:11 -05:00
Igor Wessel
da150e8767 feat: use standard edge id function for class,flow,state diagram 2024-05-06 08:07:47 -03:00
Igor Wessel
1f64452716 feat(utils): create a standard edge id function 2024-05-06 07:46:42 -03:00
cmmoran
10871af93b chore: update browsers list 2024-05-06 07:06:55 +00:00
Igor Wessel
9986b023d7 feat(state): add from, to in edge 2024-05-04 06:21:12 -03:00
NicolasNewman
c4ccfec316 Update docs 2024-04-29 14:49:56 +00:00
NicolasNewman
1ac9244e68 style(mathml): linting 2024-04-29 09:46:50 -05:00
Alexandre Colucci
18defaae6d Update integrations-community: Add MarkChart, a macOS app to preview Mermaid diagrams 2024-04-26 08:17:40 +02:00
NicolasNewman
7f33ae0f40 fix(mathml): fixed flowchart equations being cut off 2024-04-22 17:15:10 -05:00
NicolasNewman
13aa3265e3 docs(mathml): updated docs to include forceLegacyMathML 2024-04-22 17:14:36 -05:00
NicolasNewman
3b0687e557 feat(mathml): added additional config option for forcing legacy rendering 2024-04-22 17:12:12 -05:00
52 changed files with 1678 additions and 2444 deletions

View File

@@ -1,7 +1,10 @@
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';
import type {
MermaidConfigWithDefaults,
BaseDiagramConfig,
} from '../packages/mermaid/src/config.type.js';
/**
* All of the keys in the mermaid config that have a mermaid diagram config.
@@ -36,7 +39,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
* @param mermaidConfigSchema - The Mermaid JSON Schema to use.
* @returns The default mermaid config object.
*/
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfigWithDefaults>) {
const ajv = new Ajv2019({
useDefaults: true,
allowUnionTypes: true,
@@ -105,20 +108,23 @@ function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
return mermaidDefaultConfig;
}
export const loadSchema = (src: string, filename: string): JSONSchemaType<MermaidConfig> => {
export const loadSchema = (
src: string,
filename: string
): JSONSchemaType<MermaidConfigWithDefaults> => {
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>;
}) as JSONSchemaType<MermaidConfigWithDefaults>;
return jsonSchema;
};
export const getDefaults = (schema: JSONSchemaType<MermaidConfig>) => {
export const getDefaults = (schema: JSONSchemaType<MermaidConfigWithDefaults>) => {
return `export default ${JSON.stringify(generateDefaults(schema), undefined, 2)};`;
};
export const getSchema = (schema: JSONSchemaType<MermaidConfig>) => {
export const getSchema = (schema: JSONSchemaType<MermaidConfigWithDefaults>) => {
return `export default ${JSON.stringify(schema, undefined, 2)};`;
};

View File

@@ -83,6 +83,7 @@ NODIR
NSTR
outdir
Qcontrolx
regexes
reinit
rels
reqs

View File

@@ -1,5 +1,5 @@
import type { JSONSchemaType } from 'ajv/dist/2019.js';
import type { MermaidConfig } from '../packages/mermaid/src/config.type.js';
import type { MermaidConfigWithDefaults } from '../packages/mermaid/src/config.type.js';
import { readFile } from 'node:fs/promises';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
@@ -12,13 +12,13 @@ import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
export const jsonSchemaPlugin = {
name: 'json-schema-plugin',
setup(build) {
let schema: JSONSchemaType<MermaidConfig> | undefined = undefined;
let schema: JSONSchemaType<MermaidConfigWithDefaults> | 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> =
const resolvedSchema: JSONSchemaType<MermaidConfigWithDefaults> =
content === source && schema ? schema : loadSchema(source, args.path);
if (content !== source) {
content = source;

3
.gitignore vendored
View File

@@ -48,6 +48,7 @@ demos/dev/**
!/demos/dev/example.html
!/demos/dev/reload.js
tsx-0/**
vite.config.ts.timestamp-*
# autogenereated by langium-cli
generated/
generated/

View File

@@ -55,6 +55,7 @@ export const imgSnapshotTest = (
const options: CypressMermaidConfig = {
..._options,
fontFamily: _options.fontFamily || 'courier',
// @ts-ignore TODO: Fix type of fontSize
fontSize: _options.fontSize || '16px',
sequence: {
...(_options.sequence || {}),

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -84,3 +84,13 @@ Example with legacy mode enabled (the latest version of KaTeX's stylesheet can b
</body>
</html>
```
## Handling Rendering Differences
Due to differences between default fonts across operating systems and browser's MathML implementations, inconsistent results can be seen across platforms. If having consistent results are important, or the most optimal rendered results are desired, `forceLegacyMathML` can be enabled in the config.
This option will always use KaTeX's stylesheet instead of only when MathML is not supported (as with `legacyMathML`). Note that only `forceLegacyMathML` needs to be set.
If including KaTeX's stylesheet is not a concern, enabling this option is recommended to avoid scenarios where no MathML implementation within a browser provides the desired output (as seen below).
![Image showing differences between Browsers](img/mathMLDifferences.png)

View File

@@ -10,7 +10,7 @@
### defaultConfig
`Const` **defaultConfig**: `MermaidConfig`
`Const` **defaultConfig**: `MermaidConfigWithDefaults`
#### Defined in
@@ -26,9 +26,9 @@ Pushes in a directive to the configuration
#### Parameters
| Name | Type | Description |
| :---------- | :-------------- | :----------------------- |
| `directive` | `MermaidConfig` | The directive to push in |
| Name | Type | Description |
| :---------- | :--------------------------------------------------- | :----------------------- |
| `directive` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> | The directive to push in |
#### Returns
@@ -36,13 +36,13 @@ Pushes in a directive to the configuration
#### Defined in
[config.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L188)
[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191)
---
### getConfig
**getConfig**(): `MermaidConfig`
**getConfig**(): `MermaidConfigWithDefaults`
## getConfig
@@ -54,19 +54,19 @@ Pushes in a directive to the configuration
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The currentConfig
#### Defined in
[config.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L131)
[config.ts:134](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L134)
---
### getSiteConfig
**getSiteConfig**(): `MermaidConfig`
**getSiteConfig**(): `MermaidConfigWithDefaults`
## getSiteConfig
@@ -78,13 +78,13 @@ The currentConfig
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The siteConfig
#### Defined in
[config.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L96)
[config.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L99)
---
@@ -108,9 +108,9 @@ The siteConfig
#### Parameters
| Name | Type | Default value | Description |
| :------- | :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfig` | `siteConfig` | base set of values, which currentConfig could be **reset** to. Defaults to the current siteConfig (e.g returned by [getSiteConfig](config.md#getsiteconfig)). |
| Name | Type | Default value | Description |
| :------- | :-------------------------- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfigWithDefaults` | `siteConfig` | base set of values, which currentConfig could be **reset** to. Defaults to the current siteConfig (e.g returned by [getSiteConfig](config.md#getsiteconfig)). |
#### Returns
@@ -118,7 +118,7 @@ The siteConfig
#### Defined in
[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218)
[config.ts:221](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L221)
---
@@ -147,7 +147,7 @@ options in-place
#### Defined in
[config.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L146)
[config.ts:149](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L149)
---
@@ -157,9 +157,9 @@ options in-place
#### Parameters
| Name | Type |
| :----- | :-------------- |
| `conf` | `MermaidConfig` |
| Name | Type |
| :----- | :--------------------------------------------------- |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> |
#### Returns
@@ -167,13 +167,13 @@ options in-place
#### Defined in
[config.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L75)
[config.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L78)
---
### setConfig
**setConfig**(`conf`): `MermaidConfig`
**setConfig**(`conf`): `MermaidConfigWithDefaults`
## setConfig
@@ -187,25 +187,25 @@ corresponding siteConfig value.
#### Parameters
| Name | Type | Description |
| :----- | :-------------- | :-------------------------- |
| `conf` | `MermaidConfig` | The potential currentConfig |
| Name | Type | Description |
| :----- | :--------------------------------------------------- | :-------------------------- |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> | The potential currentConfig |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The currentConfig merged with the sanitized conf
#### Defined in
[config.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L113)
[config.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L116)
---
### setSiteConfig
**setSiteConfig**(`conf`): `MermaidConfig`
**setSiteConfig**(`conf`): `MermaidConfigWithDefaults`
## setSiteConfig
@@ -220,36 +220,36 @@ function _Default value: At default, will mirror Global Config_
#### Parameters
| Name | Type | Description |
| :----- | :-------------- | :------------------------------------------ |
| `conf` | `MermaidConfig` | The base currentConfig to use as siteConfig |
| Name | Type | Description |
| :----- | :--------------------------------------------------- | :------------------------------------------ |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> | The base currentConfig to use as siteConfig |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The new siteConfig
#### Defined in
[config.ts:61](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L61)
[config.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L64)
---
### updateCurrentConfig
**updateCurrentConfig**(`siteCfg`, `_directives`): `MermaidConfig`
**updateCurrentConfig**(`siteCfg`, `_directives`): `MermaidConfigWithDefaults`
#### Parameters
| Name | Type |
| :------------ | :----------------- |
| `siteCfg` | `MermaidConfig` |
| `_directives` | `MermaidConfig`\[] |
| Name | Type |
| :------------ | :------------------------------------------------------ |
| `siteCfg` | `MermaidConfigWithDefaults` |
| `_directives` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>\[] |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
#### Defined in
@@ -259,18 +259,18 @@ The new siteConfig
### updateSiteConfig
**updateSiteConfig**(`conf`): `MermaidConfig`
**updateSiteConfig**(`conf`): `MermaidConfigWithDefaults`
#### Parameters
| Name | Type |
| :----- | :-------------- |
| `conf` | `MermaidConfig` |
| Name | Type |
| :----- | :--------------------------------------------------- |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
#### Defined in
[config.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L79)
[config.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L82)

View File

@@ -20,7 +20,7 @@
### default
`Const` **default**: `RequiredDeep`<`MermaidConfig`>
`Const` **default**: `RequiredDeep`<`MermaidConfigWithDefaults`>
Default mermaid configuration options.

View File

@@ -32,7 +32,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
### mermaidAPI
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md) & { `suppressErrors`: `true` }) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md) | `false`>(`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md)> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }>
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfigWithDefaults` = configApi.defaultConfig; `getConfig`: () => `MermaidConfigWithDefaults` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfigWithDefaults` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>) => `void` ; `parse`: (`text`: `string`, `parseOptions`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md) & { `suppressErrors`: `true` }) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md) | `false`>(`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md)> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>) => `MermaidConfigWithDefaults` = configApi.setConfig; `updateSiteConfig`: (`conf`: `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>) => `MermaidConfigWithDefaults` = configApi.updateSiteConfig }>
## mermaidAPI configuration defaults
@@ -169,7 +169,7 @@ Create the user styles
| Name | Type | Description |
| :---------- | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfig` | configuration that has style and theme settings to use |
| `config` | `MermaidConfigWithDefaults` | configuration that has style and theme settings to use |
| `classDefs` | `undefined` \| `null` \| `Record`<`string`, `DiagramStyleClassDef`> | the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) |
#### Returns
@@ -192,7 +192,7 @@ the string with all the user styles
| Name | Type |
| :---------- | :-------------------------------------------------------- |
| `config` | `MermaidConfig` |
| `config` | `MermaidConfigWithDefaults` |
| `graphType` | `string` |
| `classDefs` | `undefined` \| `Record`<`string`, `DiagramStyleClassDef`> |
| `svgId` | `string` |

View File

@@ -240,6 +240,8 @@ Communication tools and platforms
### Other
- [Astro](https://astro.build/)
- [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/)
- [Bisheng](https://www.npmjs.com/package/bisheng)
- [bisheng-plugin-mermaid](https://github.com/yct21/bisheng-plugin-mermaid)
- [Blazorade Mermaid: Render Mermaid diagrams in Blazor applications](https://github.com/Blazorade/Blazorade-Mermaid/wiki)
@@ -249,6 +251,7 @@ Communication tools and platforms
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
- [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/)
- [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic)
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)
- [NiceGUI: Let any browser be the frontend of your Python code](https://nicegui.io) ✅

View File

@@ -4,7 +4,7 @@
"version": "10.2.4",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@8.15.7",
"packageManager": "pnpm@8.15.8",
"keywords": [
"diagram",
"markdown",
@@ -19,7 +19,7 @@
"build:esbuild": "pnpm run -r clean && tsx .esbuild/build.ts",
"build:mermaid": "pnpm build:esbuild --mermaid",
"build:viz": "pnpm build:esbuild --visualize",
"build:types": "tsx .build/types.ts",
"build:types": "pnpm --filter mermaid types:build-config && tsx .build/types.ts",
"build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch",
"dev": "tsx .esbuild/server.ts",
"dev:vite": "tsx .vite/server.ts",
@@ -107,7 +107,7 @@
"jison": "^0.4.18",
"js-yaml": "^4.1.0",
"jsdom": "^24.0.0",
"langium-cli": "3.0.1",
"langium-cli": "3.0.3",
"lint-staged": "^15.2.2",
"markdown-table": "^3.0.3",
"nyc": "^15.1.0",

View File

@@ -1,4 +1,4 @@
import type { MermaidConfig } from 'mermaid';
import type { MermaidConfigWithDefaults } from 'mermaid';
const warning = (s: string) => {
// Todo remove debug code
@@ -27,7 +27,7 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
};
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
export let getConfig: () => MermaidConfig;
export let getConfig: () => MermaidConfigWithDefaults;
export let sanitizeText: (str: string) => string;
// eslint-disable @typescript-eslint/no-explicit-any
export let setupGraphViewbox: (

View File

@@ -114,7 +114,7 @@
"jison": "^0.4.18",
"js-base64": "^3.7.7",
"jsdom": "^24.0.0",
"json-schema-to-typescript": "^13.1.2",
"json-schema-to-typescript": "^14.0.4",
"micromatch": "^4.0.5",
"path-browserify": "^1.0.1",
"prettier": "^3.2.5",

View File

@@ -10,24 +10,22 @@
/* eslint-disable no-console */
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import _Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
import { JSON_SCHEMA, load } from 'js-yaml';
import { compile, type JSONSchema } from 'json-schema-to-typescript';
import assert from 'node:assert';
import { execFile } from 'node:child_process';
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { promisify } from 'node:util';
import { load, JSON_SCHEMA } from 'js-yaml';
import { compile, type JSONSchema } from 'json-schema-to-typescript';
import prettier from 'prettier';
import _Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
// Workaround for wrong AJV types, see
// https://github.com/ajv-validator/ajv/issues/2132#issuecomment-1290409907
const Ajv2019 = _Ajv2019 as unknown as typeof _Ajv2019.default;
// !!! -- The config.type.js file is created by this script -- !!!
import type { MermaidConfig } from '../src/config.type.js';
import type { MermaidConfigWithDefaults } from '../src/config.type.js';
// options for running the main command
const verifyOnly = process.argv.includes('--verify');
@@ -35,29 +33,7 @@ const verifyOnly = process.argv.includes('--verify');
const git = process.argv.includes('--git');
/**
* 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',
];
/**
* Loads the MermaidConfig JSON schema YAML file.
* Loads the MermaidConfigWithDefaults JSON schema YAML file.
*
* @returns The loaded JSON Schema, use {@link validateSchema} to confirm it is a valid JSON Schema.
*/
@@ -79,7 +55,9 @@ async function loadJsonSchemaFromYaml() {
* @param jsonSchema - The value to validate as JSON Schema 2019-09
* @throws {Error} if the given object is invalid.
*/
function validateSchema(jsonSchema: unknown): asserts jsonSchema is JSONSchemaType<MermaidConfig> {
function validateSchema(
jsonSchema: unknown
): asserts jsonSchema is JSONSchemaType<MermaidConfigWithDefaults> {
if (typeof jsonSchema !== 'object') {
throw new Error(`jsonSchema param is not an object: actual type is ${typeof jsonSchema}`);
}
@@ -109,7 +87,7 @@ function validateSchema(jsonSchema: unknown): asserts jsonSchema is JSONSchemaTy
*
* @param mermaidConfigSchema - The input JSON Schema.
*/
async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidConfigWithDefaults>) {
/**
* Replace all usages of `allOf` with `extends`.
*
@@ -131,81 +109,19 @@ async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidCon
return schema;
}
/**
* For backwards compatibility with older Mermaid Typescript defs,
* we need to make all value optional instead of required.
*
* This is because the `MermaidConfig` type is used as an input, and everything is optional,
* since all the required values have default values.s
*
* In the future, we should make make the input to Mermaid `Partial<MermaidConfig>`.
*
* @todo TODO: Remove this function when Mermaid releases a new breaking change.
* @param schema - The input schema.
* @returns The schema with all required values removed.
*/
function removeRequired(schema: JSONSchemaType<Record<string, any>>) {
return { ...schema, required: [] };
}
/**
* This is a temporary hack to control the order the types are generated in.
*
* By default, json-schema-to-typescript outputs the $defs in the order they
* are used, then any unused schemas at the end.
*
* **The only purpose of this function is to make the `git diff` simpler**
* **We should remove this later to simplify the code**
*
* @todo TODO: Remove this function in a future PR.
* @param schema - The input schema.
* @returns The schema with all `$ref`s removed.
*/
function unrefSubschemas(schema: JSONSchemaType<Record<string, any>>) {
return {
...schema,
properties: Object.fromEntries(
Object.entries(schema.properties).map(([key, propertySchema]) => {
if (MERMAID_CONFIG_DIAGRAM_KEYS.includes(key)) {
const { $ref, ...propertySchemaWithoutRef } = propertySchema as JSONSchemaType<unknown>;
if ($ref === undefined) {
throw Error(
`subSchema ${key} is in MERMAID_CONFIG_DIAGRAM_KEYS but does not have a $ref field`
);
}
const [
_root, // eslint-disable-line @typescript-eslint/no-unused-vars
_defs, // eslint-disable-line @typescript-eslint/no-unused-vars
defName,
] = $ref.split('/');
return [
key,
{
...propertySchemaWithoutRef,
tsType: defName,
},
];
}
return [key, propertySchema];
})
),
};
}
assert.ok(mermaidConfigSchema.$defs);
const modifiedSchema = {
...unrefSubschemas(removeRequired(mermaidConfigSchema)),
...mermaidConfigSchema,
$defs: Object.fromEntries(
Object.entries(mermaidConfigSchema.$defs).map(([key, subSchema]) => {
return [key, removeRequired(replaceAllOfWithExtends(subSchema))];
return [key, replaceAllOfWithExtends(subSchema as JSONSchemaType<Record<string, any>>)];
})
),
};
const typescriptFile = await compile(
modifiedSchema as JSONSchema, // json-schema-to-typescript only allows JSON Schema 4 as input type
'MermaidConfig',
modifiedSchema as unknown as JSONSchema, // json-schema-to-typescript only allows JSON Schema 4 as input type
'MermaidConfigWithDefaults',
{
additionalProperties: false, // in JSON Schema 2019-09, these are called `unevaluatedProperties`
unreachableDefinitions: true, // definition for FontConfig is unreachable
@@ -259,6 +175,7 @@ async function main() {
}
const configJsonSchema = await loadJsonSchemaFromYaml();
// TODO: Add code to mark objects with default values as required
removeProp(configJsonSchema, 'default');

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { expect, it } from 'vitest';
import * as configApi from './config.js';
import type { MermaidConfig } from './config.type.js';
import type { MermaidConfig, MermaidConfigWithDefaults } from './config.type.js';
describe('when working with site config', () => {
beforeEach(() => {
@@ -17,12 +18,12 @@ describe('when working with site config', () => {
expect(config_1).toEqual(config_2);
});
it('should respect secure keys when applying directives', () => {
const config_0: MermaidConfig = {
const config_0 = {
fontFamily: 'foo-font',
securityLevel: 'strict', // can't be changed
fontSize: 12345, // can't be changed
secure: [...configApi.defaultConfig.secure!, 'fontSize'],
};
} as MermaidConfigWithDefaults;
configApi.setSiteConfig(config_0);
const directive: MermaidConfig = {
fontFamily: 'baf',
@@ -30,7 +31,7 @@ describe('when working with site config', () => {
fontSize: 54321,
securityLevel: 'loose',
};
const cfg: MermaidConfig = configApi.updateCurrentConfig(config_0, [directive]);
const cfg: MermaidConfigWithDefaults = configApi.updateCurrentConfig(config_0, [directive]);
expect(cfg.fontFamily).toEqual(directive.fontFamily);
expect(cfg.fontSize).toBe(config_0.fontSize);
expect(cfg.securityLevel).toBe(config_0.securityLevel);

View File

@@ -1,20 +1,23 @@
import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfigWithDefaults, MermaidConfig } from './config.type.js';
import config from './defaultConfig.js';
import { log } from './logger.js';
import theme from './themes/index.js';
import config from './defaultConfig.js';
import type { MermaidConfig } from './config.type.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const defaultConfig: MermaidConfig = Object.freeze(config);
export const defaultConfig: MermaidConfigWithDefaults = Object.freeze(config);
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let siteConfig: MermaidConfigWithDefaults = assignWithDepth({}, defaultConfig);
let configFromInitialize: MermaidConfig;
let directives: MermaidConfig[] = [];
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let currentConfig: MermaidConfigWithDefaults = assignWithDepth({}, defaultConfig);
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => {
export const updateCurrentConfig = (
siteCfg: MermaidConfigWithDefaults,
_directives: MermaidConfig[]
) => {
// start with config being the siteConfig
let cfg: MermaidConfig = assignWithDepth({}, siteCfg);
let cfg: MermaidConfigWithDefaults = assignWithDepth({}, siteCfg);
// let sCfg = assignWithDepth(defaultConfig, siteConfigDelta);
// Join directives
@@ -58,7 +61,7 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: Mermaid
* @param conf - The base currentConfig to use as siteConfig
* @returns The new siteConfig
*/
export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => {
export const setSiteConfig = (conf: MermaidConfig): MermaidConfigWithDefaults => {
siteConfig = assignWithDepth({}, defaultConfig);
siteConfig = assignWithDepth(siteConfig, conf);
@@ -76,7 +79,7 @@ export const saveConfigFromInitialize = (conf: MermaidConfig): void => {
configFromInitialize = assignWithDepth({}, conf);
};
export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => {
export const updateSiteConfig = (conf: MermaidConfig): MermaidConfigWithDefaults => {
siteConfig = assignWithDepth(siteConfig, conf);
updateCurrentConfig(siteConfig, directives);
@@ -93,7 +96,7 @@ export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => {
*
* @returns The siteConfig
*/
export const getSiteConfig = (): MermaidConfig => {
export const getSiteConfig = (): MermaidConfigWithDefaults => {
return assignWithDepth({}, siteConfig);
};
/**
@@ -110,7 +113,7 @@ export const getSiteConfig = (): MermaidConfig => {
* @param conf - The potential currentConfig
* @returns The currentConfig merged with the sanitized conf
*/
export const setConfig = (conf: MermaidConfig): MermaidConfig => {
export const setConfig = (conf: MermaidConfig): MermaidConfigWithDefaults => {
checkConfig(conf);
assignWithDepth(currentConfig, conf);
@@ -128,7 +131,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => {
*
* @returns The currentConfig
*/
export const getConfig = (): MermaidConfig => {
export const getConfig = (): MermaidConfigWithDefaults => {
return assignWithDepth({}, currentConfig);
};
/**

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,5 @@
import type { RequiredDeep } from 'type-fest';
import type { MermaidConfigWithDefaults } from './config.type.js';
import theme from './themes/index.js';
import type { MermaidConfig } from './config.type.js';
// Uses our custom Vite jsonSchemaPlugin to load only the default values from
// our JSON Schema
// @ts-expect-error This file is automatically generated via a custom Vite plugin
@@ -15,7 +12,7 @@ import defaultConfigJson from './schemas/config.schema.yaml?only-defaults=true';
* Non-JSON JS default values are listed in this file, e.g. functions, or
* `undefined` (explicitly set so that `configKeys` finds them).
*/
const config: RequiredDeep<MermaidConfig> = {
const config: MermaidConfigWithDefaults = {
...defaultConfigJson,
// Set, even though they're `undefined` so that `configKeys` finds these keys
// TODO: Should we replace these with `null` so that they can go in the JSON Schema?

View File

@@ -1,13 +1,13 @@
import type { MermaidConfig } from '../config.type.js';
import type { MermaidConfigWithDefaults } from '../config.type.js';
import { UnknownDiagramError } from '../errors.js';
import { log } from '../logger.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import type {
DetectorRecord,
DiagramDetector,
DiagramLoader,
ExternalDiagramDefinition,
} from './types.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import { UnknownDiagramError } from '../errors.js';
export const detectors: Record<string, DetectorRecord> = {};
@@ -33,7 +33,7 @@ export const detectors: Record<string, DetectorRecord> = {};
* @param config - The mermaid config.
* @returns A graph definition key
*/
export const detectType = function (text: string, config?: MermaidConfig): string {
export const detectType = function (text: string, config?: MermaidConfigWithDefaults): string {
text = text
.replace(frontMatterRegex, '')
.replace(directiveRegex, '')

View File

@@ -1,4 +1,5 @@
import { it, describe, expect } from 'vitest';
import { describe, expect, it, beforeAll } from 'vitest';
import type { MermaidConfigWithDefaults } from '../config.type.js';
import { detectType } from './detectType.js';
import { addDiagrams } from './diagram-orchestration.js';
@@ -46,30 +47,40 @@ describe('diagram-orchestration', () => {
// graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B')).toBe('flowchart');
// graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })).toBe(
'flowchart'
);
expect(
detectType('graph TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-d3' },
} as MermaidConfigWithDefaults)
).toBe('flowchart');
// flowchart & dagre-d3 => error
expect(() =>
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })
detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-d3' },
} as MermaidConfigWithDefaults)
).toThrowErrorMatchingInlineSnapshot(
`[UnknownDiagramError: No diagram type detected matching given configuration for text: flowchart TD; A-->B]`
);
// graph & dagre-wrapper => flowchart-v2
expect(
detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } })
detectType('graph TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-wrapper' },
} as MermaidConfigWithDefaults)
).toBe('flowchart-v2');
// flowchart ==> flowchart-v2
expect(detectType('flowchart TD; A-->B')).toBe('flowchart-v2');
// flowchart && dagre-wrapper ==> flowchart-v2
expect(
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } })
detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-wrapper' },
} as MermaidConfigWithDefaults)
).toBe('flowchart-v2');
// flowchart && elk ==> flowchart-elk
expect(detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'elk' } })).toBe(
'flowchart-elk'
);
expect(
detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'elk' },
} as MermaidConfigWithDefaults)
).toBe('flowchart-elk');
});
it('should not detect flowchart if pie contains flowchart', () => {

View File

@@ -5,7 +5,7 @@ import {
} from 'd3';
import type { Diagram } from '../../Diagram.js';
import * as configApi from '../../config.js';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
import insertMarkers from '../../dagre-wrapper/markers.js';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
@@ -75,7 +75,7 @@ export const draw = async function (
const magicFactor = Math.max(1, Math.round(0.125 * (bounds2.width / bounds2.height)));
const height = bounds2.height + magicFactor + 10;
const width = bounds2.width + 10;
const { useMaxWidth } = conf as Exclude<MermaidConfig['block'], undefined>;
const { useMaxWidth } = conf as Exclude<MermaidConfigWithDefaults['block'], undefined>;
configureSvgSize(svg, height, width, !!useMaxWidth);
log.debug('Here Bounds', bounds, bounds2);
svg.attr(

View File

@@ -1,17 +1,16 @@
// @ts-ignore: JISON doesn't support types
import parser from './parser/c4Diagram.jison';
import type { DiagramDefinition } from '../../diagram-api/types.js';
import db from './c4Db.js';
import renderer from './c4Renderer.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/c4Diagram.jison';
import styles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
parser,
db,
renderer,
styles,
init: ({ c4, wrap }: MermaidConfig) => {
init: ({ c4, wrap }) => {
renderer.setConf(c4);
db.setWrap(wrap);
},

View File

@@ -4,7 +4,7 @@ import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import { render } from '../../dagre-wrapper/index.js';
import utils from '../../utils.js';
import utils, { getEdgeId } from '../../utils.js';
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import common from '../common/common.js';
@@ -231,7 +231,10 @@ export const addRelations = function (relations: ClassRelation[], g: graphlib.Gr
//Set relationship style and line type
classes: 'relation',
pattern: edge.relation.lineType == 1 ? 'dashed' : 'solid',
id: `id_${edge.id1}_${edge.id2}_${cnt}`,
id: getEdgeId(edge.id1, edge.id2, {
prefix: 'id',
counter: cnt,
}),
// Set link type for rendering
arrowhead: edge.type === 'arrow_open' ? 'none' : 'normal',
//Set edge extra labels

View File

@@ -1,4 +1,6 @@
import { sanitizeText, removeScript, parseGenericTypes, countOccurrence } from './common.js';
import { describe, expect, it } from 'vitest';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
import { countOccurrence, parseGenericTypes, removeScript, sanitizeText } from './common.js';
describe('when securityLevel is antiscript, all script must be removed', () => {
/**
@@ -67,7 +69,7 @@ describe('Sanitize text', () => {
const result = sanitizeText(maliciousStr, {
securityLevel: 'strict',
flowchart: { htmlLabels: true },
});
} as MermaidConfigWithDefaults);
expect(result).not.toContain('javascript:alert(1)');
});
});

View File

@@ -1,5 +1,5 @@
import DOMPurify from 'dompurify';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
// Remove and ignore br:s
export const lineBreakRegex = /<br\s*\/?>/gi;
@@ -63,7 +63,7 @@ export const removeScript = (txt: string): string => {
return sanitizedText;
};
const sanitizeMore = (text: string, config: MermaidConfig) => {
const sanitizeMore = (text: string, config: MermaidConfigWithDefaults) => {
if (config.flowchart?.htmlLabels !== false) {
const level = config.securityLevel;
if (level === 'antiscript' || level === 'strict') {
@@ -78,7 +78,7 @@ const sanitizeMore = (text: string, config: MermaidConfig) => {
return text;
};
export const sanitizeText = (text: string, config: MermaidConfig): string => {
export const sanitizeText = (text: string, config: MermaidConfigWithDefaults): string => {
if (!text) {
return text;
}
@@ -94,7 +94,7 @@ export const sanitizeText = (text: string, config: MermaidConfig): string => {
export const sanitizeTextOrArray = (
a: string | string[] | string[][],
config: MermaidConfig
config: MermaidConfigWithDefaults
): string | string[] => {
if (typeof a === 'string') {
return sanitizeText(a, config);
@@ -310,7 +310,10 @@ export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.leng
* @param config - Configuration for Mermaid
* @returns Object containing \{width, height\}
*/
export const calculateMathMLDimensions = async (text: string, config: MermaidConfig) => {
export const calculateMathMLDimensions = async (
text: string,
config: MermaidConfigWithDefaults
) => {
text = await renderKatex(text, config);
const divElem = document.createElement('div');
divElem.innerHTML = text;
@@ -332,23 +335,28 @@ export const calculateMathMLDimensions = async (text: string, config: MermaidCon
* @param config - Configuration for Mermaid
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
*/
export const renderKatex = async (text: string, config: MermaidConfig): Promise<string> => {
export const renderKatex = async (
text: string,
config: MermaidConfigWithDefaults
): Promise<string> => {
if (!hasKatex(text)) {
return text;
}
if (!isMathMLSupported() && !config.legacyMathML) {
if (!(isMathMLSupported() || config.legacyMathML || config.forceLegacyMathML)) {
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
}
const { default: katex } = await import('katex');
const outputMode =
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)
? 'htmlAndMathml'
: 'mathml';
return text
.split(lineBreakRegex)
.map((line) =>
hasKatex(line)
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
${line}
</div>`
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">${line}</div>`
: `<div>${line}</div>`
)
.join('')
@@ -357,7 +365,7 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
.renderToString(c, {
throwOnError: true,
displayMode: true,
output: isMathMLSupported() ? 'mathml' : 'htmlAndMathml',
output: outputMode,
})
.replace(/\n/g, ' ')
.replace(/<annotation.*<\/annotation>/g, '')

View File

@@ -1,17 +1,17 @@
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import { setConfig } from '../../diagram-api/diagramAPI.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
export const diagram = {
export const diagram: DiagramDefinition = {
parser: flowParser,
db: flowDb,
renderer: flowRendererV2,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
init: (cnf) => {
if (!cnf.flowchart) {
cnf.flowchart = {};
}

View File

@@ -1,17 +1,17 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowRenderer from './flowRenderer.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import flowDb from './flowDb.js';
import flowRenderer from './flowRenderer.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
export const diagram = {
export const diagram: DiagramDefinition = {
parser: flowParser,
db: flowDb,
renderer: flowRendererV2,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
init: (cnf) => {
if (!cnf.flowchart) {
cnf.flowchart = {};
}

View File

@@ -1,7 +1,7 @@
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { select, curveLinear, selectAll } from 'd3';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import utils from '../../utils.js';
import utils, { getEdgeId } from '../../utils.js';
import { render } from '../../dagre-wrapper/index.js';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { log } from '../../logger.js';
@@ -210,7 +210,11 @@ export const addEdges = async function (edges, g, diagObj) {
cnt++;
// Identify Link
const linkIdBase = 'L-' + edge.start + '-' + edge.end;
const linkIdBase = getEdgeId(edge.start, edge.end, {
counter: cnt,
prefix: 'L',
});
// count the links from+to the same node to give unique id
if (linkIdCnt[linkIdBase] === undefined) {
linkIdCnt[linkIdBase] = 0;
@@ -219,7 +223,8 @@ export const addEdges = async function (edges, g, diagObj) {
linkIdCnt[linkIdBase]++;
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
}
let linkId = linkIdBase + '-' + linkIdCnt[linkIdBase];
let linkId = `${linkIdBase}_${linkIdCnt[linkIdBase]}`;
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
const linkNameStart = 'LS-' + edge.start;
const linkNameEnd = 'LE-' + edge.end;

View File

@@ -6,7 +6,7 @@ import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { log } from '../../logger.js';
import common, { evaluate, renderKatex } from '../common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
import { interpolateToCurve, getStylesFromArray, getEdgeId } from '../../utils.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import flowChartShapes from './flowChartShapes.js';
import { replaceIconSubstring } from '../../rendering-util/createText.js';
@@ -175,7 +175,10 @@ export const addEdges = async function (edges, g, diagObj) {
cnt++;
// Identify Link
const linkId = 'L-' + edge.start + '-' + edge.end;
const linkId = getEdgeId(edge.start, edge.end, {
counter: cnt,
prefix: 'L',
});
const linkNameStart = 'LS-' + edge.start;
const linkNameEnd = 'LE-' + edge.end;

View File

@@ -1,8 +1,8 @@
import cytoscape from 'cytoscape';
// @ts-expect-error No types available
// @ts-ignore Types for cose-bilkent are not present
import coseBilkent from 'cytoscape-cose-bilkent';
import { select } from 'd3';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { DrawDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
@@ -21,7 +21,7 @@ function drawNodes(
svg: D3Element,
mindmap: FilledMindMapNode,
section: number,
conf: MermaidConfig
conf: MermaidConfigWithDefaults
) {
drawNode(db, svg, mindmap, section, conf);
if (mindmap.children) {
@@ -64,7 +64,12 @@ function drawEdges(edgesEl: D3Element, cy: cytoscape.Core) {
});
}
function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) {
function addNodes(
mindmap: MindmapNode,
cy: cytoscape.Core,
conf: MermaidConfigWithDefaults,
level: number
) {
cy.add({
group: 'nodes',
data: {
@@ -99,7 +104,10 @@ function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig,
}
}
function layoutMindmap(node: MindmapNode, conf: MermaidConfig): Promise<cytoscape.Core> {
function layoutMindmap(
node: MindmapNode,
conf: MermaidConfigWithDefaults
): Promise<cytoscape.Core> {
return new Promise((resolve) => {
// Add temporary render element
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');

View File

@@ -3,7 +3,7 @@ import { createText } from '../../rendering-util/createText.js';
import type { FilledMindMapNode, MindmapDB } from './mindmapTypes.js';
import type { Point } from '../../types.js';
import { parseFontSize } from '../../utils.js';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
const MAX_SECTIONS = 12;
@@ -180,7 +180,7 @@ export const drawNode = function (
elem: D3Element,
node: FilledMindMapNode,
fullSection: number,
conf: MermaidConfig
conf: MermaidConfigWithDefaults
): number {
const htmlLabels = conf.htmlLabels;
const section = fullSection % (MAX_SECTIONS - 1);

View File

@@ -6,7 +6,7 @@ import { getConfig } from '../../diagram-api/diagramAPI.js';
import { cleanAndMerge, parseFontSize } from '../../utils.js';
import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js';
import type { D3Section, PieDB, Sections } from './pieTypes.js';
import type { MermaidConfig, PieDiagramConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults, PieDiagramConfig } from '../../config.type.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
const createPieArcs = (sections: Sections): d3.PieArcDatum<D3Section>[] => {
@@ -38,7 +38,7 @@ const createPieArcs = (sections: Sections): d3.PieArcDatum<D3Section>[] => {
export const draw: DrawDefinition = (text, id, _version, diagObj) => {
log.debug('rendering pie chart\n' + text);
const db = diagObj.db as PieDB;
const globalConfig: MermaidConfig = getConfig();
const globalConfig: MermaidConfigWithDefaults = getConfig();
const pieConfig: Required<PieDiagramConfig> = cleanAndMerge(db.getConfig(), globalConfig.pie);
const MARGIN = 40;
const LEGEND_RECT_SIZE = 18;

View File

@@ -5,7 +5,7 @@ import { render } from '../../dagre-wrapper/index.js';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import common from '../common/common.js';
import utils from '../../utils.js';
import utils, { getEdgeId } from '../../utils.js';
import {
DEFAULT_DIAGRAM_DIRECTION,
@@ -252,7 +252,6 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
type: 'group',
padding: 0, //getConfig().flowchart.padding
};
graphItemCount++;
const parentNodeId = itemId + PARENT_ID;
g.setNode(parentNodeId, groupData);
@@ -270,17 +269,23 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
from = noteData.id;
to = itemId;
}
g.setEdge(from, to, {
arrowhead: 'none',
arrowType: '',
style: G_EDGE_STYLE,
labelStyle: '',
id: getEdgeId(from, to, {
counter: graphItemCount,
}),
classes: CSS_EDGE_NOTE_EDGE,
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
labelpos: G_EDGE_LABELPOS,
labelType: G_EDGE_LABELTYPE,
thickness: G_EDGE_THICKNESS,
});
graphItemCount++;
} else {
g.setNode(itemId, nodeData);
}
@@ -324,7 +329,9 @@ const setupDoc = (g, parentParsedItem, doc, diagramStates, diagramDb, altFlag) =
setupNode(g, parentParsedItem, item.state1, diagramStates, diagramDb, altFlag);
setupNode(g, parentParsedItem, item.state2, diagramStates, diagramDb, altFlag);
const edgeData = {
id: 'edge' + graphItemCount,
id: getEdgeId(item.state1.id, item.state2.id, {
counter: graphItemCount,
}),
arrowhead: 'normal',
arrowTypeEnd: 'arrow_barb',
style: G_EDGE_STYLE,

View File

@@ -6,7 +6,7 @@ import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import type { Diagram } from '../../Diagram.js';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
interface Block<TDesc, TSection> {
number: number;
@@ -241,7 +241,7 @@ export const drawTasks = function (
masterX: number,
masterY: number,
maxTaskHeight: number,
conf: MermaidConfig,
conf: MermaidConfigWithDefaults,
maxEventCount: number,
maxEventLineLength: number,
maxSectionHeight: number,
@@ -318,7 +318,7 @@ export const drawEvents = function (
sectionColor: number,
masterX: number,
masterY: number,
conf: MermaidConfig
conf: MermaidConfigWithDefaults
) {
let maxEventHeight = 0;
const eventBeginY = masterY;

View File

@@ -36,6 +36,7 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram
.attr('height', chartConfig.height)
.attr('class', 'background');
// @ts-ignore: TODO Fix ts errors
configureSvgSize(svg, chartConfig.height, chartConfig.width, true);
svg.attr('viewBox', `0 0 ${chartConfig.width} ${chartConfig.height}`);

View File

@@ -1,9 +1,13 @@
import mermaid, { type MermaidConfig } from 'mermaid';
import mermaid, { type MermaidConfigWithDefaults } from 'mermaid';
import zenuml from '../../../../../mermaid-zenuml/dist/mermaid-zenuml.core.mjs';
const init = mermaid.registerExternalDiagrams([zenuml]);
export const render = async (id: string, code: string, config: MermaidConfig): Promise<string> => {
export const render = async (
id: string,
code: string,
config: MermaidConfigWithDefaults
): Promise<string> => {
await init;
mermaid.initialize(config);
const { svg } = await mermaid.render(id, code);

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -60,3 +60,13 @@ Example with legacy mode enabled (the latest version of KaTeX's stylesheet can b
</body>
</html>
```
## Handling Rendering Differences
Due to differences between default fonts across operating systems and browser's MathML implementations, inconsistent results can be seen across platforms. If having consistent results are important, or the most optimal rendered results are desired, `forceLegacyMathML` can be enabled in the config.
This option will always use KaTeX's stylesheet instead of only when MathML is not supported (as with `legacyMathML`). Note that only `forceLegacyMathML` needs to be set.
If including KaTeX's stylesheet is not a concern, enabling this option is recommended to avoid scenarios where no MathML implementation within a browser provides the desired output (as seen below).
![Image showing differences between Browsers](img/mathMLDifferences.png)

View File

@@ -235,6 +235,8 @@ Communication tools and platforms
### Other
- [Astro](https://astro.build/)
- [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/)
- [Bisheng](https://www.npmjs.com/package/bisheng)
- [bisheng-plugin-mermaid](https://github.com/yct21/bisheng-plugin-mermaid)
- [Blazorade Mermaid: Render Mermaid diagrams in Blazor applications](https://github.com/Blazorade/Blazorade-Mermaid/wiki)
@@ -244,6 +246,7 @@ Communication tools and platforms
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
- [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/)
- [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic)
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)
- [NiceGUI: Let any browser be the frontend of your Python code](https://nicegui.io) ✅

View File

@@ -34,7 +34,7 @@
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.0",
"vite-plugin-pwa": "^0.19.7",
"vitepress": "1.1.0",
"vitepress": "1.1.4",
"workbox-window": "^7.0.0"
}
}

View File

@@ -3,7 +3,7 @@
* functionality and to render the diagrams to svg code!
*/
import { dedent } from 'ts-dedent';
import type { MermaidConfig } from './config.type.js';
import type { MermaidConfigWithDefaults, MermaidConfig } from './config.type.js';
import { log } from './logger.js';
import utils from './utils.js';
import type { ParseOptions, ParseResult, RenderResult } from './mermaidAPI.js';
@@ -18,6 +18,7 @@ import type { UnknownDiagramError } from './errors.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
export type {
MermaidConfigWithDefaults,
MermaidConfig,
DetailedError,
ExternalDiagramDefinition,
@@ -330,8 +331,8 @@ const executeQueue = async () => {
* // throws Error
* ```
*/
const parse: typeof mermaidAPI.parse = async (text, parseOptions) => {
return new Promise((resolve, reject) => {
const parse = async (text: string, parseOptions?: ParseOptions & { suppressErrors: true }) => {
return new Promise<ParseResult | false>((resolve, reject) => {
// This promise will resolve when the render call is done.
// It will be queued first and will be executed when it is first in line
const performCall = () =>

View File

@@ -1,4 +1,4 @@
import { vi, it, expect, describe, beforeEach } from 'vitest';
import { beforeEach, describe, expect, it, vi } from 'vitest';
// -------------------------------------
// Mocks and mocking
@@ -35,7 +35,7 @@ vi.mock('./diagrams/state/stateRenderer-v2.js');
// -------------------------------------
import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfig } from './config.type.js';
import type { MermaidConfigWithDefaults } from './config.type.js';
import mermaid from './mermaid.js';
import mermaidAPI, {
appendDivSvgG,
@@ -67,8 +67,8 @@ vi.mock('stylis', () => {
});
import { compile, serialize } from 'stylis';
import { decodeEntities, encodeEntities } from './utils.js';
import { Diagram } from './Diagram.js';
import { decodeEntities, encodeEntities } from './utils.js';
/**
* @see https://vitest.dev/guide/mocking.html Mock part of a module
@@ -283,12 +283,12 @@ describe('mermaidAPI', () => {
describe('createCssStyles', () => {
const serif = 'serif';
const sansSerif = 'sans-serif';
const mocked_config_with_htmlLabels: MermaidConfig = {
const mocked_config_with_htmlLabels = {
themeCSS: 'default',
fontFamily: serif,
altFontFamily: sansSerif,
htmlLabels: true,
};
} as MermaidConfigWithDefaults;
it('gets the cssStyles from the theme', () => {
const styles = createCssStyles(mocked_config_with_htmlLabels, null);
@@ -367,7 +367,7 @@ describe('mermaidAPI', () => {
}
// common suite and tests to verify that the right styles are created with the right htmlElements
function expect_correct_styles_with_htmlElements(mocked_config: MermaidConfig) {
function expect_correct_styles_with_htmlElements(mocked_config: MermaidConfigWithDefaults) {
describe('creates styles for "> *" and "span" elements', () => {
const htmlElements = ['> *', 'span'];
@@ -389,14 +389,14 @@ describe('mermaidAPI', () => {
});
it('there are flowchart.htmlLabels in the configuration', () => {
const mocked_config_flowchart_htmlLabels: MermaidConfig = {
const mocked_config_flowchart_htmlLabels = {
themeCSS: 'default',
fontFamily: 'serif',
altFontFamily: 'sans-serif',
flowchart: {
htmlLabels: true,
},
};
} as MermaidConfigWithDefaults;
expect_correct_styles_with_htmlElements(mocked_config_flowchart_htmlLabels);
});
@@ -405,7 +405,7 @@ describe('mermaidAPI', () => {
themeCSS: 'default',
fontFamily: 'serif',
altFontFamily: 'sans-serif',
};
} as MermaidConfigWithDefaults;
describe('creates styles for shape elements "rect", "polygon", "ellipse", and "circle"', () => {
const htmlElements = ['rect', 'polygon', 'ellipse', 'circle'];
@@ -430,7 +430,7 @@ describe('mermaidAPI', () => {
themeCSS: 'default',
htmlLabels: true,
themeVariables: { fontFamily: 'serif' },
};
} as MermaidConfigWithDefaults;
const classDef1 = { id: 'classDef1', styles: ['style1-1'], textStyles: [] };

View File

@@ -10,24 +10,26 @@
*
* In addition to the render function, a number of behavioral configuration options are available.
*/
// @ts-ignore TODO: Investigate D3 issue
import { select } from 'd3';
import { compile, serialize, stringify } from 'stylis';
// @ts-ignore: TODO Fix ts errors
import DOMPurify from 'dompurify';
import isEmpty from 'lodash-es/isEmpty.js';
import { version } from '../package.json';
import * as configApi from './config.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import { Diagram } from './Diagram.js';
import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js';
import * as configApi from './config.js';
import type { MermaidConfigWithDefaults, MermaidConfig } from './config.type.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
import { evaluate } from './diagrams/common/common.js';
import errorRenderer from './diagrams/error/errorRenderer.js';
import { attachFunctions } from './interactionDb.js';
import { log, setLogLevel } from './logger.js';
import { preprocessDiagram } from './preprocess.js';
import getStyles from './styles.js';
import theme from './themes/index.js';
import DOMPurify from 'dompurify';
import type { MermaidConfig } from './config.type.js';
import { evaluate } from './diagrams/common/common.js';
import isEmpty from 'lodash-es/isEmpty.js';
import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.js';
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
import { preprocessDiagram } from './preprocess.js';
import { decodeEntities } from './utils.js';
const MAX_TEXTLENGTH = 50_000;
@@ -149,7 +151,7 @@ export const cssImportantStyles = (
* @returns the string with all the user styles
*/
export const createCssStyles = (
config: MermaidConfig,
config: MermaidConfigWithDefaults,
classDefs: Record<string, DiagramStyleClassDef> | null | undefined = {}
): string => {
let cssStyles = '';
@@ -196,7 +198,7 @@ export const createCssStyles = (
};
export const createUserStyles = (
config: MermaidConfig,
config: MermaidConfigWithDefaults,
graphType: string,
classDefs: Record<string, DiagramStyleClassDef> | undefined,
svgId: string

View File

@@ -1,3 +1,4 @@
import type { MermaidConfig } from './config.type.js';
import { cleanupComments } from './diagram-api/comments.js';
import { extractFrontMatter } from './diagram-api/frontmatter.js';
import type { DiagramMetadata } from './diagram-api/types.js';
@@ -30,7 +31,7 @@ const processFrontmatter = (code: string) => {
};
const processDirectives = (code: string) => {
const initDirective = utils.detectInit(code) ?? {};
const initDirective: MermaidConfig = utils.detectInit(code) ?? {};
const wrapDirectives = utils.detectDirective(code, 'wrap');
if (Array.isArray(wrapDirectives)) {
initDirective.wrap = wrapDirectives.some(({ type }) => {

View File

@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck TODO: Fix types
import type { MermaidConfig } from '../config.type.js';
import type { MermaidConfigWithDefaults } from '../config.type.js';
import type { Group } from '../diagram-api/types.js';
import type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js';
import { log } from '../logger.js';
@@ -195,7 +195,7 @@ export const createText = (
width = 200,
addSvgBackground = false,
} = {},
config: MermaidConfig
config: MermaidConfigWithDefaults
) => {
log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
if (useHtmlLabels) {

View File

@@ -1,14 +1,16 @@
import type { Content } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { dedent } from 'ts-dedent';
import type { MermaidConfigWithDefaults } from '../config.type.js';
import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';
import { RootContent } from 'mdast';
type MarkdownConfig = Partial<Pick<MermaidConfigWithDefaults, 'markdownAutoWrap'>>;
/**
* @param markdown - markdown to process
* @returns processed markdown
*/
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string {
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MarkdownConfig): string {
// Replace multiple newlines with a single newline
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line
@@ -22,13 +24,13 @@ function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfi
/**
* @param markdown - markdown to split into lines
*/
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] {
export function markdownToLines(markdown: string, config: MarkdownConfig = {}): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const { children } = fromMarkdown(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]];
let currentLine = 0;
function processNode(node: Content, parentType: MarkdownWordType = 'normal') {
function processNode(node: RootContent, parentType: MarkdownWordType = 'normal') {
if (node.type === 'text') {
const textLines = node.value.split('\n');
textLines.forEach((textLine, index) => {
@@ -60,10 +62,10 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
return lines;
}
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) {
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MarkdownConfig = {}) {
const { children } = fromMarkdown(markdown);
function output(node: Content): string {
function output(node: RootContent): string {
if (node.type === 'text') {
if (markdownAutoWrap === false) {
return node.value.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;');

View File

@@ -1,11 +1,11 @@
# Used for VS Code's YAML plugin to automatically error on invalid types
# yaml-language-server: $schema=https://json-schema.org/draft/2019-09/schema
# This file defines the MermaidConfig JSON Schema as a YAML file.
# This file defines the MermaidConfigWithDefaults JSON Schema as a YAML file.
#
# From this file, the following things can be generated:
# - `scripts/create-types-from-json-schema.mjs`
# Used to generate the `src/config.type.ts` TypeScript types for MermaidConfig
# Used to generate the `src/config.type.ts` TypeScript types for MermaidConfigWithDefaults
# with the `json-schema-to-typescript` NPM package.
# - `.build/jsonSchema.ts`
# Used to generate the default values from the `default` keys in this
@@ -20,13 +20,13 @@
# - Use the `|` character for multi-line strings
# - Use `meta:enum` to document enum values (from jsonschema2md)
# - Use `tsType` to override the TypeScript type (from json-schema-to-typescript)
# - If adding a new object to `MermaidConfig` (e.g. a new diagram type),
# - If adding a new object to `MermaidConfigWithDefaults` (e.g. a new diagram type),
# you may need to add it to `.build/jsonSchema.ts`, `src/docs.mts`
# and `scripts/create-types-from-json-schema.mjs`
# to get the default values/docs/types to generate properly.
$id: https://mermaid.js.org/schemas/config.schema.json
$schema: https://json-schema.org/draft/2019-09/schema
title: Mermaid Config
title: MermaidConfigWithDefaults
type: object
additionalProperties: false
required:
@@ -180,6 +180,13 @@ properties:
fall back to legacy rendering for KaTeX.
type: boolean
default: false
forceLegacyMathML:
description: |
This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
If set to true, ignores legacyMathML.
type: boolean
default: false
deterministicIds:
description: |
This option controls if the generated ids of nodes in the SVG are
@@ -254,6 +261,11 @@ properties:
This is useful when you want to control how to handle syntax errors in your application.
$defs: # JSON Schema definition (maybe we should move these to a separate file)
MermaidConfig:
title: MermaidConfig
description: MermaidConfigWithDefaults with all fields optional
type: object
tsType: "import('type-fest').PartialDeep<MermaidConfigWithDefaults>"
BaseDiagramConfig:
# TODO: More config needs to be moved here
title: Base Diagram Config

View File

@@ -1,5 +1,3 @@
import type { interpolateToCurve } from './utils.js';
export interface Point {
x: number;
y: number;
@@ -32,7 +30,7 @@ export interface EdgeData {
arrowTypeEnd: string;
style: string;
labelStyle: string;
curve: ReturnType<typeof interpolateToCurve>;
curve: any;
}
export type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;

View File

@@ -1,5 +1,5 @@
import { sanitizeUrl } from '@braintree/sanitize-url';
import type { CurveFactory, Selection } from 'd3';
import type { CurveFactory } from 'd3';
import {
curveBasis,
curveBasisClosed,
@@ -7,12 +7,12 @@ import {
curveBumpX,
curveBumpY,
curveBundle,
curveCardinal,
curveCardinalClosed,
curveCardinalOpen,
curveCardinal,
curveCatmullRom,
curveCatmullRomClosed,
curveCatmullRomOpen,
curveCatmullRom,
curveLinear,
curveLinearClosed,
curveMonotoneX,
@@ -23,17 +23,17 @@ import {
curveStepBefore,
select,
} from 'd3';
import common from './diagrams/common/common.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
import { log } from './logger.js';
import { detectType } from './diagram-api/detectType.js';
import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfig } from './config.type.js';
import memoize from 'lodash-es/memoize.js';
import merge from 'lodash-es/merge.js';
import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfigWithDefaults, MermaidConfig } from './config.type.js';
import { detectType } from './diagram-api/detectType.js';
import { directiveRegex } from './diagram-api/regexes.js';
import common from './diagrams/common/common.js';
import { log } from './logger.js';
import type { D3Element } from './mermaidAPI.js';
import type { Point, TextDimensionConfig, TextDimensions } from './types.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const ZERO_WIDTH_SPACE = '\u200b';
@@ -97,10 +97,7 @@ const directiveWithoutOpen =
* @param config - Optional mermaid configuration object.
* @returns The json object representing the init passed to mermaid.initialize()
*/
export const detectInit = function (
text: string,
config?: MermaidConfig
): MermaidConfig | undefined {
export const detectInit = function (text: string): MermaidConfig | undefined {
const inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/);
let results: MermaidConfig & { config?: unknown } = {};
@@ -116,7 +113,7 @@ export const detectInit = function (
return;
}
let type = detectType(text, config);
let type = detectType(text);
// Move the `config` value to appropriate diagram type value
const prop = 'config';
@@ -124,7 +121,7 @@ export const detectInit = function (
if (type === 'flowchart-v2') {
type = 'flowchart';
}
results[type as keyof MermaidConfig] = results[prop];
results[type as keyof MermaidConfigWithDefaults] = results[prop];
delete results[prop];
}
@@ -230,12 +227,16 @@ export const isSubstringInArray = function (str: string, arr: string[]): number
* @param defaultCurve - The default curve to return
* @returns The curve factory to use
*/
export function interpolateToCurve(interpolate: string | undefined, defaultCurve: CurveFactory) {
export function interpolateToCurve(
interpolate: string | undefined,
defaultCurve: CurveFactory
): CurveFactory {
if (!interpolate) {
return defaultCurve;
}
const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`;
// @ts-ignore TODO: Fix issue with curve type
return d3CurveTypes[curveName as keyof typeof d3CurveTypes] ?? defaultCurve;
}
@@ -246,7 +247,10 @@ export function interpolateToCurve(interpolate: string | undefined, defaultCurve
* @param config - Configuration passed to MermaidJS
* @returns The formatted URL or `undefined`.
*/
export function formatUrl(linkStr: string, config: MermaidConfig): string | undefined {
export function formatUrl(
linkStr: string,
config: Pick<MermaidConfigWithDefaults, 'securityLevel'>
): string | undefined {
const url = linkStr.trim();
if (!url) {
@@ -483,25 +487,6 @@ export const random = (options: { length: number }) => {
return makeRandomHex(options.length);
};
interface TextData {
text: string;
x: number;
y: number;
anchor: 'start' | 'middle' | 'end';
fontFamily?: string;
fontSize?: string | number;
fontWeight?: string | number;
fill?: string;
class?: string;
textMargin: number;
style?: string;
width?: number;
height?: number;
rx?: number;
ry?: number;
valign?: string;
}
export const getTextObj = function () {
return {
x: 0,
@@ -516,7 +501,7 @@ export const getTextObj = function () {
ry: 0,
valign: undefined,
text: '',
} satisfies TextData;
};
};
/**
@@ -527,9 +512,20 @@ export const getTextObj = function () {
* @returns Text element with given styling and content
*/
export const drawSimpleText = function (
elem: Selection<SVGSVGElement, any, HTMLElement, any>,
textData: TextData
) {
elem: SVGElement,
textData: {
text: string;
x: number;
y: number;
anchor: 'start' | 'middle' | 'end';
fontFamily: string;
fontSize: string | number;
fontWeight: string | number;
fill: string;
class: string | undefined;
textMargin: number;
}
): SVGTextElement {
// Remove and ignore br:s
const nText = textData.text.replace(common.lineBreakRegex, ' ');
@@ -693,7 +689,7 @@ export const calculateTextDimensions: (
text: string,
config: TextDimensionConfig
) => TextDimensions = memoize(
(text: string, config: TextDimensionConfig) => {
(text: string, config: TextDimensionConfig): TextDimensions => {
const { fontSize = 12, fontFamily = 'Arial', fontWeight = 400 } = config;
if (!text) {
return { width: 0, height: 0 };
@@ -723,7 +719,9 @@ export const calculateTextDimensions: (
for (const line of lines) {
const textObj = getTextObj();
textObj.text = line || ZERO_WIDTH_SPACE;
// @ts-ignore TODO: Fix D3 types
const textElem = drawSimpleText(g, textObj)
// @ts-ignore TODO: Fix D3 types
.style('font-size', _fontSizePx)
.style('font-weight', fontWeight)
.style('font-family', fontFamily);
@@ -931,3 +929,19 @@ export const decodeEntities = function (text: string): string {
export const isString = (value: unknown): value is string => {
return typeof value === 'string';
};
export const getEdgeId = (
from: string,
to: string,
{
counter = 0,
prefix,
suffix,
}: {
counter?: number;
prefix?: string;
suffix?: string;
}
) => {
return `${prefix ? `${prefix}_` : ''}${from}_${to}_${counter}${suffix ? `_${suffix}` : ''}`;
};

View File

@@ -3,7 +3,7 @@ import type { FlowchartDiagramConfig } from '../config.type.js';
export const getSubGraphTitleMargins = ({
flowchart,
}: {
flowchart: FlowchartDiagramConfig;
flowchart: Pick<FlowchartDiagramConfig, 'subGraphTitleMargin'>;
}): {
subGraphTitleTopMargin: number;
subGraphTitleBottomMargin: number;

1628
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff