Compare commits

..

40 Commits

Author SHA1 Message Date
Sidharth Vinod
5e210753b7 feat: Add live-reload 2023-08-13 18:51:02 +05:30
Sidharth Vinod
350aaf9b51 chore: Remove @vitest/coverage-c8 2023-08-13 18:45:49 +05:30
Sidharth Vinod
5cd792f027 feat: Add esbuild 2023-08-13 18:33:41 +05:30
Sidharth Vinod
43217e1395 Merge branch 'master' into develop
* master:
  Version update and adjusted error diagram
  Fix for broker error diagram related #4178
  Adding new flowchart tests related to issue #2139
  Fix for interim issue with classes in state diagrams
  #2139 Applying user defined classes properly when calculating shape width
  chore: Ignore localhost
  Update packages/mermaid/src/docs/community/development.md
  Update docs/community/development.md
  docs: Add development example page.
2023-08-12 17:04:25 +05:30
Sidharth Vinod
aa34b99203 Merge remote-tracking branch 'origin/release/10.3.1'
* origin/release/10.3.1: (85 commits)
  Version update and adjusted error diagram
  Fix for broker error diagram related #4178
  Adding new flowchart tests related to issue #2139
  Fix for interim issue with classes in state diagrams
  use default export in `error` diagram
  create `ParserDefinition` type
  standardized `error` diagram'
  #2139 Applying user defined classes properly when calculating shape width
  style: format packages/mermaid/src/config.type.ts
  build(types): use prettier conf on config.types.ts
  Syntax Update CONTRIBUTING.md
  docs: Correct detectType filename
  chore: Minor cleanups
  chore: remove comment
  chore: Remove comments, cleanup
  fix!(deps): fix zenuml style leakage. update @zenuml/core to ^3.0.6 to fix the style leakage.
  Update selectSvgElement.ts
  create `Group` type
  Add specialChars in textNoTagsToken, alphaNumToken
  Return Unicode Text to idStringToken definition
  ...
2023-08-12 17:04:06 +05:30
Sidharth Vinod
591cb794eb Merge pull request #4717 from Yokozuna59/chore/convert-assignWithDepth-to-ts
convert `assignWithDepth` to TS
2023-08-11 17:32:40 +00:00
Reda Al Sulais
6170538c47 Merge branch 'develop' into chore/convert-assignWithDepth-to-ts 2023-08-11 19:54:57 +03:00
Reda Al Sulais
c37c494a1e Update assignWithDepth.ts 2023-08-11 19:54:37 +03:00
Knut Sveidqvist
a0b80f5490 Version update and adjusted error diagram 2023-08-11 14:03:56 +02:00
Knut Sveidqvist
493023118f Fix for broker error diagram related #4178 2023-08-11 13:59:16 +02:00
Knut Sveidqvist
c5e7e6040c Adding new flowchart tests related to issue #2139 2023-08-11 13:58:19 +02:00
Knut Sveidqvist
c938c7a438 Fix for interim issue with classes in state diagrams 2023-08-11 13:57:26 +02:00
Knut Sveidqvist
c7bcf72c69 Merge branch 'develop' into release/10.4.0 2023-08-11 11:46:30 +02:00
Sidharth Vinod
da602add5d Merge pull request #4697 from mermaid-js/sidv/fixTripleParsing
fix: Remove triple parsing of diagrams
2023-08-11 15:14:33 +05:30
Knut Sveidqvist
9049a99c8d Merge pull request #4722 from mermaid-js/2139-handling-styling-properly-when-calculating-width
#2139 Applying user defined classes properly when calculating shape width
2023-08-11 09:58:29 +02:00
Knut Sveidqvist
60957519c8 Merge branch 'develop' into release/10.4.0 2023-08-11 09:43:58 +02:00
Reda Al Sulais
d8dd68cad2 redeclare config parameter add default value for each variable 2023-08-11 01:42:26 +03:00
Reda Al Sulais
9b9ad1da7d Merge pull request #4718 from Yokozuna59/chore/standardized-error-definitions
standardized `error` diagram
2023-08-10 22:35:02 +00:00
Reda Al Sulais
0d380bb2c7 Merge branch 'develop' into chore/standardized-error-definitions 2023-08-11 01:24:47 +03:00
Reda Al Sulais
9ab048c7e9 use default export in error diagram 2023-08-11 00:55:31 +03:00
Sidharth Vinod
1078b1c8f6 Merge pull request #4719 from Yokozuna59/chore/create-ParserDefinition-type
create `ParserDefinition` type
2023-08-10 21:35:57 +00:00
Reda Al Sulais
980a5ac8b5 create ParserDefinition type 2023-08-10 22:26:04 +03:00
Reda Al Sulais
d5f11fc80a convert assignWithDepth to TS 2023-08-10 22:09:14 +03:00
Reda Al Sulais
46de343785 standardized error diagram'
* remove empty `styles.js`
* use named imports/exports instead of default
* remove unnecessary data in diagram definition
2023-08-10 22:07:39 +03:00
Sidharth Vinod
66b579cd89 Merge pull request #4715 from aloisklink/chore/use-our-prettier-config-for-types
Use our prettier config on the `packages/mermaid/src/config.type.ts` file
2023-08-10 14:36:03 +00:00
Alois Klink
0b9f6d1ff9 style: format packages/mermaid/src/config.type.ts
Run `pnpm run --filter mermaid types:build-config` to format
the `packages/mermaid/src/config.type.ts` file with the new prettier
config.
2023-08-09 19:22:30 +01:00
Alois Klink
7742a6c485 build(types): use prettier conf on config.types.ts
Currently, the `packages/mermaid/src/config.type.ts` types file
(auto-generated via `pnpm run --filter mermaid types:build-config`)
uses the default prettier config.

Instead, we should use the prettier config in the Mermaid repo, as it's
slightly different from the default prettier config.
2023-08-09 19:15:09 +01:00
Nikolay Rozhkov
3e598f4e8e Merge pull request #4714 from mermaid-js/sidv/exampleDev
docs: Add development example page.
2023-08-09 17:08:29 +03:00
Sidharth Vinod
eb397fdb04 Merge branch 'sidv/exampleDev' of https://github.com/mermaid-js/mermaid into sidv/exampleDev
* 'sidv/exampleDev' of https://github.com/mermaid-js/mermaid:
  Update packages/mermaid/src/docs/community/development.md
  Update docs/community/development.md
2023-08-09 19:00:01 +05:30
Sidharth Vinod
6c1afb7a8d chore: Ignore localhost 2023-08-09 18:59:55 +05:30
Sidharth Vinod
fee6f459a8 Update packages/mermaid/src/docs/community/development.md 2023-08-09 17:44:49 +05:30
Sidharth Vinod
e128a11f3b Update docs/community/development.md 2023-08-09 17:44:24 +05:30
Sidharth Vinod
06e4a6398c docs: Add development example page. 2023-08-09 14:59:50 +05:30
Sidharth Vinod
da8112afa5 Merge branch 'develop' into sidv/fixTripleParsing
* develop: (59 commits)
  fix!(deps): fix zenuml style leakage. update @zenuml/core to ^3.0.6 to fix the style leakage.
  Update selectSvgElement.ts
  create `Group` type
  Add specialChars in textNoTagsToken, alphaNumToken
  Return Unicode Text to idStringToken definition
  Add underscore to unit test on special Chars
  Revert to old docs concerning quotations marks in string
  Refactor unit tests and remove repetition
  Correct idStringToken definition to include all individual special tokens
  Add unit tests for node ids with special Chars
  Create lychee.toml
  create `selectSvgElement`
  change `svgElem` to `SVG` in `configureSvgSize`
  add `configureSvgSize` in infoRenderer
  run docs:build
  remove info sandbox test case
  Remove replaceAll method in addLink
  Modify HREF token regex to contain space
  Add unit tests for stange node names
  Remove escaped quotes with backslash feature
  ...
2023-08-08 13:37:02 +05:30
Sidharth Vinod
bae9a7c1b8 docs: Correct detectType filename 2023-08-08 11:13:45 +05:30
Sidharth Vinod
776b219c03 chore: Minor cleanups 2023-08-08 00:31:17 +05:30
Sidharth Vinod
4471001ef2 chore: remove comment
Co-authored-by: Alois Klink <alois@aloisklink.com>
2023-08-08 00:24:38 +05:30
Sidharth Vinod
646f7662d0 chore: Remove comments, cleanup
Co-authored-by: Alois Klink <alois@aloisklink.com>
Co-authored-by: Nikolay Rozhkov <nironame@gmail.com>
Co-authored-by: Yokozuna59 <u.yokozuna@gmail.com>
2023-08-07 15:12:37 +05:30
Sidharth Vinod
95efc1786f fix: unitTests after tripleParsing removal 2023-08-03 23:21:49 +05:30
Sidharth Vinod
10d5b5d1d6 fix: Remove triple parsing of diagrams 2023-08-03 22:54:37 +05:30
60 changed files with 1279 additions and 727 deletions

20
.build/common.ts Normal file
View File

@@ -0,0 +1,20 @@
/**
* Shared common options for both ESBuild and Vite
*/
export 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',
},
} as const;

122
.build/jsonSchema.ts Normal file
View File

@@ -0,0 +1,122 @@
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',
'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.
*/
export 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)};`;
};

34
.esbuild/build.ts Normal file
View File

@@ -0,0 +1,34 @@
import { build } from 'esbuild';
import { mkdir, writeFile } from 'node:fs/promises';
import { getBuildConfig } from './util.js';
import { packageOptions } from '../.build/common.js';
const shouldVisualize = process.argv.includes('--visualize');
const buildPackage = async (entryName: keyof typeof packageOptions) => {
await build(getBuildConfig({ entryName, minify: false }));
const { metafile } = await build(
getBuildConfig({ entryName, minify: true, metafile: shouldVisualize })
);
if (metafile) {
// Upload metafile into https://esbuild.github.io/analyze/
await writeFile(`stats/meta-${entryName}.json`, JSON.stringify(metafile));
}
await build(getBuildConfig({ entryName, minify: false, core: true }));
await build(getBuildConfig({ entryName, minify: true, format: 'iife' }));
};
const handler = (e) => {
console.error(e);
process.exit(1);
};
const main = async () => {
await mkdir('stats').catch(() => {});
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
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;

93
.esbuild/server.ts Normal file
View File

@@ -0,0 +1,93 @@
import express from 'express';
import type { NextFunction, Request, Response } from 'express';
import cors from 'cors';
import { getBuildConfig } from './util.js';
import { context } from 'esbuild';
import chokidar from 'chokidar';
const mermaidCtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid' })
);
const mermaidIIFECtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid', format: 'iife' })
);
const externalCtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid-example-diagram' })
);
const zenumlCtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid-zenuml' })
);
const contexts = [mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
const rebuildAll = async () => {
await Promise.all(contexts.map((ctx) => ctx.rebuild()));
};
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() {
const app = express();
chokidar
.watch('**/src/**/*.{js,ts,yaml,json}', {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
.on('all', async (event, path) => {
// Ignore other events.
if (!['add', 'change'].includes(event)) {
return;
}
console.log(`${path} changed. Rebuilding...`);
handleFileChange();
});
app.use(cors());
app.get('/events', eventsHandler);
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();

81
.esbuild/util.ts Normal file
View File

@@ -0,0 +1,81 @@
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));
interface MermaidBuildOptions {
minify: boolean;
core?: boolean;
metafile?: boolean;
format?: 'esm' | 'iife';
entryName: keyof typeof packageOptions;
}
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,
};
};
export const getBuildConfig = ({
minify,
core,
entryName,
metafile,
format,
}: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
let output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[`${name}${core ? '.core' : format === 'iife' ? '' : '.esm'}${
minify ? '.min' : ''
}`]: `src/${file}`,
},
metafile,
logLevel: 'info',
});
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';
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;
};

3
.gitignore vendored
View File

@@ -43,3 +43,6 @@ stats/
**/contributor-names.json
.pnpm-store
.nyc_output
demos/dev/**
!/demos/dev/example.html

View File

@@ -3,11 +3,11 @@ import { resolve } from 'path';
import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js';
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
import { readFileSync } from 'fs';
import typescript from '@rollup/plugin-typescript';
import { visualizer } from 'rollup-plugin-visualizer';
import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js';
import istanbul from 'vite-plugin-istanbul';
import { packageOptions } from '../.build/common.js';
const visualize = process.argv.includes('--visualize');
const watch = process.argv.includes('--watch');
@@ -36,24 +36,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 {
minify: boolean | 'esbuild';
core?: boolean;
@@ -72,34 +54,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
sourcemap,
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 = {
configFile: false,
build: {
@@ -146,8 +102,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
const buildPackage = async (entryName: keyof typeof packageOptions) => {
await build(getBuildConfig({ minify: false, entryName }));
await build(getBuildConfig({ minify: 'esbuild', entryName }));
await build(getBuildConfig({ minify: false, core: true, entryName }));
};
const main = async () => {

View File

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

View File

@@ -1,108 +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 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',
'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;
}
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
/**
* Vite plugin that handles JSON Schemas saved as a `.schema.yaml` file.
@@ -119,32 +16,13 @@ export default function jsonSchemaPlugin(): PluginOption {
return;
}
if (idAsUrl.searchParams.get('only-defaults')) {
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 {
code: `export default ${JSON.stringify(generateDefaults(jsonSchema), undefined, 2)};`,
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
};
}
const jsonSchema = loadSchema(src, idAsUrl.pathname);
return {
code: idAsUrl.searchParams.get('only-defaults')
? getDefaults(jsonSchema)
: getSchema(jsonSchema),
map: null, // no source map
};
},
};
}

View File

@@ -26,9 +26,14 @@ Install required packages:
```bash
# npx is required for first install as volta support for pnpm is not added yet.
npx pnpm install
pnpm test
pnpm test # run unit tests
pnpm dev # starts a dev server
```
Open <http://localhost:9000> in your browser after starting the dev server.
You can also duplicate the `example.html` file in `demos/dev`, rename it and add your own mermaid code to it.
That will be served at <http://localhost:9000/dev/your-file-name.html>.
### Docker
If you are using docker and docker-compose, you have self-documented `run` bash script, which is a convenient alias for docker-compose commands:

View File

@@ -80,6 +80,7 @@
"mdbook",
"mermaidjs",
"mermerd",
"metafile",
"mindaugas",
"mindmap",
"mindmaps",

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

@@ -449,7 +449,7 @@ flowchart TD
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('65: text-color from classes', () => {
it('65-1: text-color from classes', () => {
imgSnapshotTest(
`
flowchart LR
@@ -460,6 +460,31 @@ flowchart TD
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('65-2: bold text from classes', () => {
imgSnapshotTest(
`
flowchart
classDef cat fill:#f9d5e5, stroke:#233d4d,stroke-width:2px, font-weight:bold;
CS(A long bold text to be viewed):::cat
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('65-3: bigger font from classes', () => {
imgSnapshotTest(
`
flowchart
Node1:::class1 --> Node2:::class2
Node1:::class1 --> Node3:::class2
Node3 --> Node4((I am a circle)):::larger
classDef class1 fill:lightblue
classDef class2 fill:pink
classDef larger font-size:30px,fill:yellow
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('66: More nested subgraph cases (TB)', () => {
imgSnapshotTest(
`

View File

@@ -1,7 +1,7 @@
<html>
<head>
<meta charset="utf-8" />
<script src="./viewer.js" type="module"></script>
<script type="module" src="./viewer.js"></script>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
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 type="module" src="./external-diagrams-mindmap.mjs" /> -->
<script type="module">
import exampleDiagram from '../../packages/mermaid-example-diagram/dist/mermaid-example-diagram.core.mjs';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import exampleDiagram from './mermaid-example-diagram.esm.mjs';
import mermaid from './mermaid.esm.mjs';
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
Function-->URL
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 id="FirstLine" class="mermaid2">
graph TB
1Function-->2URL
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 id="FirstLine" class="mermaid2">
classDiagram
class Test
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
callback ShapeCallback "clickByClass" "This is a tooltip for a callback"
</pre>
@@ -42,7 +42,7 @@
<pre id="FirstLine" class="mermaid">
classDiagram-v2
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>
</div>
@@ -77,7 +77,7 @@
Calling a Callback (look at the console log) :cl2, 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 cl3 call clickByGantt("test1", test2, test3)
@@ -102,9 +102,15 @@
div.className = 'created-by-gant-click';
div.style = 'padding: 20px; background: green; color: white;';
div.innerText = 'Clicked By Gant';
if (arg1) div.innerText += ' ' + arg1;
if (arg2) div.innerText += ' ' + arg2;
if (arg3) div.innerText += ' ' + arg3;
if (arg1) {
div.innerText += ' ' + arg1;
}
if (arg2) {
div.innerText += ' ' + arg2;
}
if (arg3) {
div.innerText += ' ' + arg3;
}
document.getElementsByTagName('body')[0].appendChild(div);
}

View File

@@ -1,6 +1,6 @@
import mermaid2 from './mermaid.esm.mjs';
import externalExample from '../../packages/mermaid-example-diagram/dist/mermaid-example-diagram.core.mjs';
import zenUml from '../../packages/mermaid-zenuml/dist/mermaid-zenuml.core.mjs';
import mermaid from './mermaid.esm.mjs';
import externalExample from './mermaid-example-diagram.esm.mjs';
import zenUml from './mermaid-zenuml.esm.mjs';
function b64ToUtf8(str) {
return decodeURIComponent(escape(window.atob(str)));
@@ -45,9 +45,9 @@ const contentLoaded = async function () {
document.getElementsByTagName('body')[0].appendChild(div);
}
await mermaid2.registerExternalDiagrams([externalExample, zenUml]);
mermaid2.initialize(graphObj.mermaid);
await mermaid2.run();
await mermaid.registerExternalDiagrams([externalExample, zenUml]);
mermaid.initialize(graphObj.mermaid);
await mermaid.run();
}
};
@@ -95,18 +95,14 @@ const contentLoadedApi = async function () {
divs[i] = div;
}
const defaultE2eCnf = { theme: 'forest' };
const defaultE2eCnf = { theme: 'forest', startOnLoad: false };
const cnf = merge(defaultE2eCnf, graphObj.mermaid);
mermaid2.initialize(cnf);
mermaid.initialize(cnf);
for (let i = 0; i < numCodes; i++) {
const { svg, bindFunctions } = await mermaid2.render(
'newid' + i,
graphObj.code[i],
divs[i]
);
const { svg, bindFunctions } = await mermaid.render('newid' + i, graphObj.code[i], divs[i]);
div.innerHTML = svg;
bindFunctions(div);
}
@@ -114,18 +110,21 @@ const contentLoadedApi = async function () {
const div = document.createElement('div');
div.id = 'block';
div.className = 'mermaid';
console.warn('graphObj.mermaid', graphObj.mermaid);
console.warn('graphObj', graphObj);
document.getElementsByTagName('body')[0].appendChild(div);
mermaid2.initialize(graphObj.mermaid);
const { svg, bindFunctions } = await mermaid2.render('newid', graphObj.code, div);
mermaid.initialize(graphObj.mermaid);
const { svg, bindFunctions } = await mermaid.render('newid', graphObj.code, div);
div.innerHTML = svg;
console.log(div.innerHTML);
bindFunctions(div);
}
}
};
if (typeof document !== 'undefined') {
mermaid.initialize({
startOnLoad: false,
});
/*!
* 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>
<head>
<script src="./viewer.js" type="module"></script>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<style>
.malware {
@@ -33,12 +32,6 @@
</script>
</head>
<body>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
startOnLoad: false,
useMaxWidth: true,
});
</script>
<script type="module" src="./viewer.js"></script>
</body>
</html>

62
demos/dev/example.html Normal file
View File

@@ -0,0 +1,62 @@
<!--Do not edit this file-->
<!--Duplicate this file to any name you like, run `pnpm dev`, open http://localhost:9000/dev/name.html to view-->
<html>
<head>
<title>Mermaid development page</title>
</head>
<body>
<pre id="diagram" class="mermaid">
graph TB
a --> b
a --> c
b --> d
c --> d
</pre>
<div id="dynamicDiagram"></div>
<script type="module">
import mermaid from '/mermaid.esm.mjs';
mermaid.parseError = function (err, hash) {
console.error('Mermaid error: ', err);
};
mermaid.initialize({
startOnLoad: true,
logLevel: 0,
});
const value = `graph TD\nHello --> World`;
const el = document.getElementById('dynamicDiagram');
const { svg } = await mermaid.render('dd', value);
console.log(svg);
el.innerHTML = svg;
</script>
<script>
// Set to false to disable live reload
const liveReload = true;
// 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');
};
};
if (liveReload) {
connectToEvents();
}
</script>
</body>
</html>

View File

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

View File

@@ -70,7 +70,21 @@ pnpm test
The `test` script and others are in the top-level `package.json` file.
All tests should run successfully without any errors or failures. (You might see _lint_ or _formatting_ warnings; those are ok during this step.)
All tests should run successfully without any errors or failures. (You might see _lint_ or _formatting_ "warnings"; those are ok during this step.)
#### 4. Make your changes
Now you are ready to make your changes!
Edit whichever files in `src` as required.
#### 5. See your changes
Open <http://localhost:9000> in your browser, after starting the dev server.
There is a list of demos that can be used to see and test your changes.
If you need a specific diagram, you can duplicate the `example.html` file in `/demos/dev` and add your own mermaid code to your copy.
That will be served at <http://localhost:9000/dev/your-file-name.html>.
After making code changes, the dev server will rebuild the mermaid library. You will need to reload the browser page yourself to see the changes. (PRs for auto reload are welcome!)
### Docker

View File

@@ -10,7 +10,7 @@
#### Grammar
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
This would be to define a JISON grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
For instance:
@@ -60,7 +60,7 @@ Place the renderer in the diagram folder.
### Step 3: Detection of the new diagram type
The second thing to do is to add the capability to detect the new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
The second thing to do is to add the capability to detect the new diagram to type to the detectType in `diagram-api/detectType.ts`. The detection should return a key for the new diagram type.
[This key will be used to as the aria roledescription](#aria-roledescription), so it should be a word that clearly describes the diagram type.
For example, if your new diagram use a UML deployment diagram, a good key would be "UMLDeploymentDiagram" because assistive technologies such as a screen reader
would voice that as "U-M-L Deployment diagram." Another good key would be "deploymentDiagram" because that would be voiced as "Deployment Diagram." A bad key would be "deployment" because that would not sufficiently describe the diagram.

View File

@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in
[mermaidAPI.ts:669](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L669)
[mermaidAPI.ts:668](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L668)
## Functions
@@ -127,7 +127,7 @@ Return the last node appended
#### Defined in
[mermaidAPI.ts:310](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L310)
[mermaidAPI.ts:309](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L309)
---
@@ -153,7 +153,7 @@ the cleaned up svgCode
#### Defined in
[mermaidAPI.ts:256](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L256)
[mermaidAPI.ts:255](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L255)
---
@@ -179,7 +179,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L185)
[mermaidAPI.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L184)
---
@@ -202,7 +202,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:233](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L233)
[mermaidAPI.ts:232](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L232)
---
@@ -229,7 +229,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L169)
[mermaidAPI.ts:168](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L168)
---
@@ -249,7 +249,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:155](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L155)
[mermaidAPI.ts:154](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L154)
---
@@ -269,7 +269,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L126)
[mermaidAPI.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L125)
---
@@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in
[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
[mermaidAPI.ts:286](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L286)
---
@@ -320,4 +320,4 @@ Remove any existing elements from the given document
#### Defined in
[mermaidAPI.ts:360](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L360)
[mermaidAPI.ts:359](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L359)

View File

@@ -64,7 +64,7 @@ Example:
```html
<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>
```
@@ -83,7 +83,7 @@ Example:
B-->D(fa:fa-spinner);
</pre>
<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>
</body>
</html>

View File

@@ -285,7 +285,7 @@ To select a version:
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
@@ -303,7 +303,7 @@ To Deploy Mermaid:
```html
<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 });
</script>
```

View File

@@ -128,7 +128,7 @@ b. The importing of mermaid library through the `mermaid.esm.mjs` or `mermaid.es
```html
<body>
<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 });
</script>
</body>
@@ -168,7 +168,7 @@ Rendering in Mermaid is initialized by `mermaid.initialize()` call. However, doi
</pre>
<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 });
</script>
</body>

View File

@@ -300,7 +300,7 @@ From version 9.4.0 you can simplify this code to:
```html
<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>
```

View File

@@ -469,7 +469,7 @@ You can use this method to add mermaid including the timeline diagram to a web p
```html
<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>
```

View File

@@ -15,14 +15,14 @@
"git graph"
],
"scripts": {
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
"build:mermaid": "pnpm build:vite --mermaid",
"build:viz": "pnpm build:mermaid --visualize",
"build": "pnpm run -r clean && pnpm build:esbuild && pnpm build:types",
"build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts",
"build:mermaid": "pnpm build:esbuild --mermaid",
"build:viz": "pnpm build:esbuild --visualize",
"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:watch": "pnpm build:vite --watch",
"build": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev",
"dev": "ts-node-esm --transpileOnly .esbuild/server.ts",
"dev:vite": "ts-node-esm --transpileOnly .vite/server.ts",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
"release": "pnpm build",
"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 . && ts-node-esm scripts/fixCSpell.ts",
@@ -31,8 +31,8 @@
"cypress": "cypress run",
"cypress:open": "cypress open",
"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",
"e2e:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm e2e",
"coverage:merge": "ts-node-esm scripts/coverage.ts",
"coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge",
"ci": "vitest run",
@@ -82,11 +82,12 @@
"@vitest/spy": "^0.33.0",
"@vitest/ui": "^0.33.0",
"ajv": "^8.12.0",
"chokidar": "^3.5.3",
"concurrently": "^8.0.1",
"cors": "^2.8.5",
"cypress": "^12.10.0",
"cypress-image-snapshot": "^4.0.1",
"esbuild": "^0.18.0",
"esbuild": "^0.19.0",
"eslint": "^8.39.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-cypress": "^2.13.2",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "10.3.0",
"version": "11.0.0-alpha.2",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -24,6 +24,7 @@
],
"scripts": {
"clean": "rimraf dist",
"dev": "pnpm -w dev",
"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 && ts-node-esm scripts/docs.cli.mts",
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm scripts/docs.cli.mts --verify",
@@ -58,8 +59,6 @@
},
"dependencies": {
"@braintree/sanitize-url": "^6.0.1",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
@@ -72,14 +71,14 @@
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"mdast-util-from-markdown": "^1.3.0",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.3",
"ts-dedent": "^2.2.0",
"uuid": "^9.0.0",
"web-worker": "^1.2.0"
"uuid": "^9.0.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^7.1.4",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"@types/cytoscape": "^3.19.9",
"@types/d3": "^7.4.0",
"@types/d3-sankey": "^0.12.1",

View File

@@ -18,6 +18,7 @@ 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';
@@ -207,6 +208,7 @@ async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidCon
{
additionalProperties: false, // in JSON Schema 2019-09, these are called `unevaluatedProperties`
unreachableDefinitions: true, // definition for FontConfig is unreachable
style: (await prettier.resolveConfig('.')) ?? {},
}
);

View File

@@ -4,8 +4,9 @@ import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
import { extractFrontMatter } from './diagram-api/frontmatter.js';
import { UnknownDiagramError } from './errors.js';
import { DetailedError } from './utils.js';
import { cleanupComments } from './diagram-api/comments.js';
import type { DetailedError } from './utils.js';
import type { DiagramDefinition } from './diagram-api/types.js';
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
@@ -15,9 +16,11 @@ export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?:
*/
export class Diagram {
type = 'graph';
parser;
renderer;
db;
parser: DiagramDefinition['parser'];
renderer: DiagramDefinition['renderer'];
db: DiagramDefinition['db'];
private init?: DiagramDefinition['init'];
private detectError?: UnknownDiagramError;
constructor(public text: string) {
this.text += '\n';
@@ -32,7 +35,6 @@ export class Diagram {
log.debug('Type ' + this.type);
// Setup diagram
this.db = diagram.db;
this.db.clear?.();
this.renderer = diagram.renderer;
this.parser = diagram.parser;
const originalParse = this.parser.parse.bind(this.parser);
@@ -49,10 +51,7 @@ export class Diagram {
originalParse(cleanupComments(extractFrontMatter(text, this.db)));
this.parser.parser.yy = this.db;
if (diagram.init) {
diagram.init(cnf);
log.info('Initialized diagram ' + this.type, cnf);
}
this.init = diagram.init;
this.parse();
}
@@ -61,6 +60,7 @@ export class Diagram {
throw this.detectError;
}
this.db.clear?.();
this.init?.(configApi.getConfig());
this.parser.parse(this.text);
}

View File

@@ -1,32 +1,36 @@
'use strict';
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* @function assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the
* assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the
* ability to merge arbitrary-depth objects For each key in src with path `k` (recursively)
* performs an Object.assign(dst[`k`], src[`k`]) with a slight change from the typical handling of
* undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to {} and
* undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to `{}` and
* effectively merged with src[`k`]<p> Additionally, dissimilar types will not clobber unless the
* config.clobber parameter === true. Example:
*
* ```js
* let config_0 = { foo: { bar: 'bar' }, bar: 'foo' };
* let config_1 = { foo: 'foo', bar: 'bar' };
* let result = assignWithDepth(config_0, config_1);
* console.log(result);
* //-> result: { foo: { bar: 'bar' }, bar: 'bar' }
* ```
* ```
* const config_0 = { foo: { bar: 'bar' }, bar: 'foo' };
* const config_1 = { foo: 'foo', bar: 'bar' };
* const result = assignWithDepth(config_0, config_1);
* console.log(result);
* //-> result: { foo: { bar: 'bar' }, bar: 'bar' }
* ```
*
* Traditional Object.assign would have clobbered foo in config_0 with foo in config_1. If src is a
* destructured array of objects and dst is not an array, assignWithDepth will apply each element
* of src to dst in order.
* @param {any} dst - The destination of the merge
* @param {any} src - The source object(s) to merge into destination
* @param {{ depth: number; clobber: boolean }} [config] - Depth: depth
* to traverse within src and dst for merging - clobber: should dissimilar types clobber (default:
* { depth: 2, clobber: false }). Default is `{ depth: 2, clobber: false }`
* @returns {any}
* @param dst - The destination of the merge
* @param src - The source object(s) to merge into destination
* @param config -
* * depth: depth to traverse within src and dst for merging
* * clobber: should dissimilar types clobber
*/
const assignWithDepth = function (dst, src, config) {
const { depth, clobber } = Object.assign({ depth: 2, clobber: false }, config);
const assignWithDepth = (
dst: any,
src: any,
{ depth = 2, clobber = false }: { depth?: number; clobber?: boolean } = {}
): any => {
const config: { depth: number; clobber: boolean } = { depth, clobber };
if (Array.isArray(src) && !Array.isArray(dst)) {
src.forEach((s) => assignWithDepth(dst, s, config));
return dst;

View File

@@ -8,7 +8,7 @@
/**
* Configuration options to pass to the `dompurify` library.
*/
export type DOMPurifyConfiguration = import("dompurify").Config;
export type DOMPurifyConfiguration = import('dompurify').Config;
/**
* JavaScript function that returns a `FontConfig`.
*
@@ -39,7 +39,7 @@ export type FontCalculator = () => Partial<FontConfig>;
* This interface was referenced by `MermaidConfig`'s JSON-Schema
* via the `definition` "SankeyLinkColor".
*/
export type SankeyLinkColor = "source" | "target" | "gradient";
export type SankeyLinkColor = 'source' | 'target' | 'gradient';
/**
* Controls the alignment of the Sankey diagrams.
*
@@ -49,7 +49,7 @@ export type SankeyLinkColor = "source" | "target" | "gradient";
* This interface was referenced by `MermaidConfig`'s JSON-Schema
* via the `definition` "SankeyNodeAlignment".
*/
export type SankeyNodeAlignment = "left" | "right" | "center" | "justify";
export type SankeyNodeAlignment = 'left' | 'right' | 'center' | 'justify';
/**
* The font size to use
*/
@@ -61,7 +61,7 @@ export interface MermaidConfig {
* You may also use `themeCSS` to override this value.
*
*/
theme?: string | "default" | "forest" | "dark" | "neutral" | "null";
theme?: string | 'default' | 'forest' | 'dark' | 'neutral' | 'null';
themeVariables?: any;
themeCSS?: string;
/**
@@ -88,12 +88,12 @@ export interface MermaidConfig {
| 0
| 2
| 1
| "trace"
| "debug"
| "info"
| "warn"
| "error"
| "fatal"
| 'trace'
| 'debug'
| 'info'
| 'warn'
| 'error'
| 'fatal'
| 3
| 4
| 5
@@ -101,7 +101,7 @@ export interface MermaidConfig {
/**
* Level of trust for parsed diagram
*/
securityLevel?: string | "strict" | "loose" | "antiscript" | "sandbox" | undefined;
securityLevel?: string | 'strict' | 'loose' | 'antiscript' | 'sandbox' | undefined;
/**
* Dictates whether mermaid starts on Page load
*/
@@ -689,11 +689,11 @@ export interface QuadrantChartConfig extends BaseDiagramConfig {
/**
* position of x-axis labels
*/
xAxisPosition?: "top" | "bottom";
xAxisPosition?: 'top' | 'bottom';
/**
* position of y-axis labels
*/
yAxisPosition?: "left" | "right";
yAxisPosition?: 'left' | 'right';
/**
* stroke width of edges of the box that are inside the quadrant
*/
@@ -723,7 +723,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
/**
* Directional bias for layout of entities
*/
layoutDirection?: string | "TB" | "BT" | "LR" | "RL";
layoutDirection?: string | 'TB' | 'BT' | 'LR' | 'RL';
/**
* The minimum width of an entity box. Expressed in pixels.
*/
@@ -788,7 +788,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig {
* Decides which rendering engine that is to be used for the rendering.
*
*/
defaultRenderer?: string | "dagre-d3" | "dagre-wrapper" | "elk";
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
}
/**
* This interface was referenced by `MermaidConfig`'s JSON-Schema
@@ -812,7 +812,7 @@ export interface ClassDiagramConfig extends BaseDiagramConfig {
* Decides which rendering engine that is to be used for the rendering.
*
*/
defaultRenderer?: string | "dagre-d3" | "dagre-wrapper" | "elk";
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
nodeSpacing?: number;
rankSpacing?: number;
/**
@@ -872,7 +872,7 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
/**
* Multiline message alignment
*/
messageAlign?: string | "left" | "center" | "right";
messageAlign?: string | 'left' | 'center' | 'right';
/**
* Prolongs the edge of the diagram downwards.
*
@@ -951,7 +951,7 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
/**
* Multiline message alignment
*/
messageAlign?: string | "left" | "center" | "right";
messageAlign?: string | 'left' | 'center' | 'right';
/**
* Prolongs the edge of the diagram downwards.
*
@@ -1062,12 +1062,12 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
* Controls the display mode.
*
*/
displayMode?: string | "compact";
displayMode?: string | 'compact';
/**
* On which day a week-based interval should start
*
*/
weekday?: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday";
weekday?: 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday';
}
/**
* The object containing configurations specific for sequence diagrams
@@ -1121,7 +1121,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
/**
* Multiline message alignment
*/
messageAlign?: string | "left" | "center" | "right";
messageAlign?: string | 'left' | 'center' | 'right';
/**
* Mirror actors under diagram
*
@@ -1178,7 +1178,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
/**
* This sets the text alignment of actor-attached notes
*/
noteAlign?: string | "left" | "center" | "right";
noteAlign?: string | 'left' | 'center' | 'right';
/**
* This sets the font size of actor messages
*/
@@ -1254,7 +1254,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
* Defines how mermaid renders curves for flowcharts.
*
*/
curve?: string | "basis" | "linear" | "cardinal";
curve?: string | 'basis' | 'linear' | 'cardinal';
/**
* Represents the padding between the labels and the shape
*
@@ -1266,7 +1266,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
* Decides which rendering engine that is to be used for the rendering.
*
*/
defaultRenderer?: string | "dagre-d3" | "dagre-wrapper" | "elk";
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
/**
* Width of nodes where text is wrapped.
*
@@ -1296,7 +1296,7 @@ export interface SankeyDiagramConfig extends BaseDiagramConfig {
* See <https://github.com/d3/d3-sankey#alignments>.
*
*/
nodeAlignment?: "left" | "right" | "center" | "justify";
nodeAlignment?: 'left' | 'right' | 'center' | 'justify';
useMaxWidth?: boolean;
}
/**

View File

@@ -35,7 +35,12 @@ describe('DiagramAPI', () => {
'loki',
{
db: {},
parser: {},
parser: {
parse: (_text) => {
return;
},
parser: { yy: {} },
},
renderer: {},
styles: {},
},

View File

@@ -69,11 +69,11 @@ export const getDiagram = (name: string): DiagramDefinition => {
if (name in diagrams) {
return diagrams[name];
}
throw new Error(`Diagram ${name} not found.`);
throw new DiagramNotFoundError(name);
};
export class DiagramNotFoundError extends Error {
constructor(message: string) {
super(`Diagram ${message} not found.`);
constructor(name: string) {
super(`Diagram ${name} not found.`);
}
}

View File

@@ -1,5 +1,5 @@
import { Diagram } from '../Diagram.js';
import { MermaidConfig } from '../config.type.js';
import type { MermaidConfig } from '../config.type.js';
import type * as d3 from 'd3';
export interface InjectUtils {
@@ -27,7 +27,7 @@ export interface DiagramDB {
export interface DiagramDefinition {
db: DiagramDB;
renderer: any;
parser: any;
parser: ParserDefinition;
styles?: any;
init?: (config: MermaidConfig) => void;
injectUtils?: (
@@ -70,6 +70,11 @@ export type DrawDefinition = (
diagramObject: Diagram
) => void;
export interface ParserDefinition {
parse: (text: string) => void;
parser: { yy: DiagramDB };
}
/**
* Type for function parse directive from diagram code.
*

View File

@@ -555,7 +555,6 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
export const draw = function (text, id, _version, diagObj) {
conf = getConfig().er;
log.info('Drawing ER diagram');
// diag.db.clear();
const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sandbox mode
let sandboxElement;

View File

@@ -1,23 +1,15 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import styles from './styles.js';
import renderer from './errorRenderer.js';
export const diagram: DiagramDefinition = {
db: {
clear: () => {
// Quite ok, clear needs to be there for error to work as a regular diagram
},
},
styles,
import type { DiagramDefinition } from '../../diagram-api/types.js';
import { renderer } from './errorRenderer.js';
const diagram: DiagramDefinition = {
db: {},
renderer,
parser: {
parser: { yy: {} },
parse: () => {
// no op
parse: (): void => {
return;
},
},
init: () => {
// no op
},
};
export default diagram;

View File

@@ -1,100 +1,81 @@
/** Created by knut on 14-12-11. */
// @ts-ignore TODO: Investigate D3 issue
import { select } from 'd3';
import { log } from '../../logger.js';
import { getErrorMessage } from '../../utils.js';
/**
* Merges the value of `conf` with the passed `cnf`
*
* @param cnf - Config to merge
*/
export const setConf = function () {
// no-op
};
import type { Group, SVG } from '../../diagram-api/types.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
/**
* Draws a an info picture in the tag with id: id based on the graph definition in text.
*
* @param _text - Mermaid graph definition.
* @param id - The text for the error
* @param mermaidVersion - The version
* @param version - The version
*/
export const draw = (_text: string, id: string, mermaidVersion: string) => {
try {
log.debug('Renering svg for syntax error\n');
export const draw = (_text: string, id: string, version: string) => {
log.debug('renering svg for syntax error\n');
const svg = select('#' + id);
const svg: SVG = selectSvgElement(id);
svg.attr('viewBox', '0 0 2412 512');
configureSvgSize(svg, 100, 512, true);
const g = svg.append('g');
const g: Group = svg.append('g');
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z'
);
g.append('path')
.attr('class', 'error-icon')
.attr(
'd',
'm436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z'
);
g.append('text') // text label for the x axis
.attr('class', 'error-text')
.attr('x', 1440)
.attr('y', 250)
.attr('font-size', '150px')
.style('text-anchor', 'middle')
.text('Syntax error in text');
g.append('text') // text label for the x axis
.attr('class', 'error-text')
.attr('x', 1250)
.attr('y', 400)
.attr('font-size', '100px')
.style('text-anchor', 'middle')
.text('mermaid version ' + mermaidVersion);
svg.attr('height', 100);
svg.attr('width', 500);
svg.attr('viewBox', '768 0 912 512');
} catch (e) {
log.error('Error while rendering info diagram');
log.error(getErrorMessage(e));
}
g.append('text') // text label for the x axis
.attr('class', 'error-text')
.attr('x', 1440)
.attr('y', 250)
.attr('font-size', '150px')
.style('text-anchor', 'middle')
.text('Syntax error in text');
g.append('text') // text label for the x axis
.attr('class', 'error-text')
.attr('x', 1250)
.attr('y', 400)
.attr('font-size', '100px')
.style('text-anchor', 'middle')
.text(`mermaid version ${version}`);
};
export default {
setConf,
draw,
};
export const renderer = { draw };
export default renderer;

View File

@@ -1,3 +0,0 @@
const getStyles = () => ``;
export default getStyles;

View File

@@ -655,14 +655,7 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
diagObj.db.clear('ver-2');
try {
// Parse the graph definition
diagObj.parse(text);
return diagObj.db.getClasses();
} catch (e) {
return {};
}
return diagObj.db.getClasses();
};
const addSubGraphs = function (db) {
@@ -766,14 +759,8 @@ const insertChildren = (nodeArray, parentLookupDb) => {
*/
export const draw = async function (text, id, _version, diagObj) {
// Add temporary render element
diagObj.db.clear();
nodeDb = {};
portPos = {};
diagObj.db.setGen('gen-2');
// Parse the graph definition
diagObj.parser.parse(text);
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
let graph = {
id: 'root',

View File

@@ -1,10 +1,7 @@
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { select, curveLinear, selectAll } from 'd3';
import flowDb from './flowDb.js';
import { getConfig } from '../../config.js';
import utils 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';
@@ -344,15 +341,7 @@ export const addEdges = function (edges, g, diagObj) {
* @returns {object} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
diagObj.db.clear();
try {
// Parse the graph definition
diagObj.parse(text);
return diagObj.db.getClasses();
} catch (e) {
return;
}
return diagObj.db.getClasses();
};
/**
@@ -364,10 +353,6 @@ export const getClasses = function (text, diagObj) {
export const draw = async function (text, id, _version, diagObj) {
log.info('Drawing flowchart');
diagObj.db.clear();
flowDb.setGen('gen-2');
// Parse the graph definition
diagObj.parser.parse(text);
// Fetch the default direction, use TD if none was found
let dir = diagObj.db.getDirection();

View File

@@ -273,15 +273,7 @@ export const addEdges = function (edges, g, diagObj) {
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
diagObj.db.clear();
try {
// Parse the graph definition
diagObj.parse(text);
return diagObj.db.getClasses();
} catch (e) {
log.error(e);
return {};
}
return diagObj.db.getClasses();
};
/**
@@ -294,7 +286,6 @@ export const getClasses = function (text, diagObj) {
*/
export const draw = function (text, id, _version, diagObj) {
log.info('Drawing flowchart');
diagObj.db.clear();
const { securityLevel, flowchart: conf } = getConfig();
let sandboxElement;
if (securityLevel === 'sandbox') {

View File

@@ -172,14 +172,11 @@ describe('more than one sequence diagram', () => {
describe('when parsing a sequenceDiagram', function () {
beforeEach(function () {
// diagram.db = sequenceDb;
// diagram.db.clear();
diagram = new Diagram(`
sequenceDiagram
Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`);
diagram.db.clear();
});
it('should handle a sequenceDiagram definition', async function () {
const str = `
@@ -1482,8 +1479,6 @@ describe('when checking the bounds in a sequenceDiagram', function () {
let conf;
beforeEach(function () {
mermaidAPI.reset();
// diagram.db = sequenceDb;
// diagram.db.clear();
diagram.renderer.bounds.init();
conf = diagram.db.getConfig();
});
@@ -1635,7 +1630,6 @@ sequenceDiagram
Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`);
diagram.db.clear();
});
['tspan', 'fo', 'old', undefined].forEach(function (textPlacement) {
it(`
@@ -2009,8 +2003,6 @@ describe('when rendering a sequenceDiagram with actor mirror activated', () => {
let conf;
beforeEach(function () {
mermaidAPI.reset();
// diagram.db = sequenceDb;
diagram.db.clear();
conf = diagram.db.getConfig();
diagram.renderer.bounds.init();
});
@@ -2052,12 +2044,8 @@ describe('when rendering a sequenceDiagram with directives', () => {
mermaidAPI.initialize({ sequence: conf });
});
let conf;
beforeEach(function () {
mermaidAPI.reset();
// diagram.db = sequenceDb;
diagram.db.clear();
conf = diagram.db.getConfig();
diagram.renderer.bounds.init();
});
@@ -2069,10 +2057,7 @@ sequenceDiagram
participant Alice
`;
diagram = new Diagram(str);
diagram.renderer.bounds.init();
await mermaidAPI.parse(str);
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
const { bounds, models } = diagram.renderer.bounds.getBounds();
@@ -2093,7 +2078,7 @@ sequenceDiagram
participant Alice
`;
diagram.parse(str);
diagram = new Diagram(str);
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
const { bounds, models } = diagram.renderer.bounds.getBounds();
@@ -2114,7 +2099,7 @@ Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
await mermaidAPI.parse(str1);
diagram = new Diagram(str1);
diagram.renderer.draw(str1, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
expect(diagram.db.showSequenceNumbers()).toBe(true);
@@ -2124,7 +2109,7 @@ Alice->Bob:Hello Bob, how are you?
Note right of Bob: Bob thinks
Bob-->Alice: I am good thanks!`;
await mermaidAPI.parse(str2);
diagram = new Diagram(str2);
diagram.renderer.draw(str2, 'tst', '1.2.3', diagram);
expect(diagram.db.showSequenceNumbers()).toBe(false);
});

View File

@@ -749,9 +749,6 @@ function adjustCreatedDestroyedData(
export const draw = function (_text: string, id: string, _version: string, diagObj: Diagram) {
const { securityLevel, sequence } = configApi.getConfig();
conf = sequence;
diagObj.db.clear();
// Parse the graph definition
diagObj.parser.parse(_text);
// Handle root and Document for when rendering in sandbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {

View File

@@ -84,17 +84,8 @@ export const setConf = function (cnf) {
* @returns {object} ClassDef styles (a Map with keys = strings, values = )
*/
export const getClasses = function (text, diagramObj) {
log.trace('Extracting classes');
diagramObj.db.clear();
try {
// Parse the graph definition
diagramObj.parser.parse(text);
// must run extract() to turn the parsed statements into states, relationships, classes, etc.
diagramObj.db.extract(diagramObj.db.getRootDocV2());
return diagramObj.db.getClasses();
} catch (e) {
return e;
}
diagramObj.db.extract(diagramObj.db.getRootDocV2());
return diagramObj.db.getClasses();
};
/**
@@ -384,7 +375,6 @@ const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
*/
export const draw = async function (text, id, _version, diag) {
log.info('Drawing state diagram (v2)', id);
// diag.sb.clear();
nodeDb = {};
// Fetch the default direction, use TD if none was found
let dir = diag.db.getDirection();

View File

@@ -64,7 +64,21 @@ pnpm test
The `test` script and others are in the top-level `package.json` file.
All tests should run successfully without any errors or failures. (You might see _lint_ or _formatting_ warnings; those are ok during this step.)
All tests should run successfully without any errors or failures. (You might see _lint_ or _formatting_ "warnings"; those are ok during this step.)
#### 4. Make your changes
Now you are ready to make your changes!
Edit whichever files in `src` as required.
#### 5. See your changes
Open <http://localhost:9000> in your browser, after starting the dev server.
There is a list of demos that can be used to see and test your changes.
If you need a specific diagram, you can duplicate the `example.html` file in `/demos/dev` and add your own mermaid code to your copy.
That will be served at <http://localhost:9000/dev/your-file-name.html>.
After making code changes, the dev server will rebuild the mermaid library. You will need to reload the browser page yourself to see the changes. (PRs for auto reload are welcome!)
### Docker

View File

@@ -4,7 +4,7 @@
#### Grammar
This would be to define a jison grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
This would be to define a JISON grammar for the new diagram type. That should start with a way to identify that the text in the mermaid tag is a diagram of that type. Create a new folder under diagrams for your new diagram type and a parser folder in it. This leads us to step 2.
For instance:
@@ -55,7 +55,7 @@ Place the renderer in the diagram folder.
### Step 3: Detection of the new diagram type
The second thing to do is to add the capability to detect the new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
The second thing to do is to add the capability to detect the new diagram to type to the detectType in `diagram-api/detectType.ts`. The detection should return a key for the new diagram type.
[This key will be used to as the aria roledescription](#aria-roledescription), so it should be a word that clearly describes the diagram type.
For example, if your new diagram use a UML deployment diagram, a good key would be "UMLDeploymentDiagram" because assistive technologies such as a screen reader
would voice that as "U-M-L Deployment diagram." Another good key would be "deploymentDiagram" because that would be voiced as "Deployment Diagram." A bad key would be "deployment" because that would not sufficiently describe the diagram.

View File

@@ -3,6 +3,7 @@ import { mermaidAPI } from './mermaidAPI.js';
import './diagram-api/diagram-orchestration.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import { beforeAll, describe, it, expect, vi } from 'vitest';
import type { DiagramDefinition } from './diagram-api/types.js';
beforeAll(async () => {
addDiagrams();
@@ -92,13 +93,16 @@ describe('when using mermaid and ', () => {
it('should defer diagram load based on parameter', async () => {
let loaded = false;
const dummyDiagram = {
const dummyDiagram: DiagramDefinition = {
db: {},
renderer: () => {
// do nothing
},
parser: () => {
// do nothing
parser: {
parse: (_text) => {
return;
},
parser: { yy: {} },
},
styles: () => {
// do nothing

View File

@@ -108,8 +108,7 @@ export interface RenderResult {
async function parse(text: string, parseOptions?: ParseOptions): Promise<boolean> {
addDiagrams();
try {
const diagram = await getDiagramFromText(text);
diagram.parse();
await getDiagramFromText(text);
} catch (error) {
if (parseOptions?.suppressErrors) {
return false;

View File

@@ -19,7 +19,6 @@ import classDiagram from './diagrams/class/styles.js';
import flowchart from './diagrams/flowchart/styles.js';
import flowchartElk from './diagrams/flowchart/elk/styles.js';
import er from './diagrams/er/styles.js';
import error from './diagrams/error/styles.js';
import git from './diagrams/git/styles.js';
import gantt from './diagrams/gantt/styles.js';
import pie from './diagrams/pie/styles.js';
@@ -86,7 +85,6 @@ describe('styles', () => {
c4,
classDiagram,
er,
error,
flowchart,
flowchartElk,
gantt,

662
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff