mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-27 22:57:10 +01:00
Compare commits
50 Commits
sidv/fixTy
...
sidv/runCo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84a2bf1b58 | ||
|
|
ef28eb8be1 | ||
|
|
c78573d9aa | ||
|
|
1aeef62db1 | ||
|
|
d639d5bdc9 | ||
|
|
f946c3da06 | ||
|
|
adfed1e432 | ||
|
|
3944e9ac00 | ||
|
|
156fbd1958 | ||
|
|
7e251ee8bc | ||
|
|
7dd0d126e2 | ||
|
|
fd45dbfc14 | ||
|
|
5c9eafabae | ||
|
|
2b9dc0ea80 | ||
|
|
8ac7dc81e0 | ||
|
|
2967b3c1bb | ||
|
|
534fd85339 | ||
|
|
7298008374 | ||
|
|
844a175039 | ||
|
|
fd35a54735 | ||
|
|
a6e6c3fb18 | ||
|
|
767baa4ec6 | ||
|
|
f422a66dde | ||
|
|
fae976e994 | ||
|
|
8678ceeb3c | ||
|
|
9cb62f4d2e | ||
|
|
fd731c5ccd | ||
|
|
222d8eed4e | ||
|
|
718d52a72c | ||
|
|
fe1a06271a | ||
|
|
bd2370555b | ||
|
|
86c9ee4e90 | ||
|
|
b26bcf1343 | ||
|
|
5d5c6275f9 | ||
|
|
9c1a47d1fc | ||
|
|
13852b7f4e | ||
|
|
4fd7a88a15 | ||
|
|
5c2a6b5eb1 | ||
|
|
9cbebbb8a0 | ||
|
|
e26d987c4e | ||
|
|
53669efaf8 | ||
|
|
b68f45ef59 | ||
|
|
8f44de651b | ||
|
|
2ede244da0 | ||
|
|
77a181978e | ||
|
|
170bbce0d3 | ||
|
|
fc99d9be41 | ||
|
|
9fb9bed806 | ||
|
|
01b2f80a95 | ||
|
|
da7ff777d1 |
20
.build/common.ts
Normal file
20
.build/common.ts
Normal 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
122
.build/jsonSchema.ts
Normal 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)};`;
|
||||
};
|
||||
60
.esbuild/build.ts
Normal file
60
.esbuild/build.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { build } from 'esbuild';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
|
||||
const shouldVisualize = process.argv.includes('--visualize');
|
||||
|
||||
const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
||||
const commonOptions = { ...defaultOptions, entryName } as const;
|
||||
const buildConfigs = [
|
||||
// package.mjs
|
||||
{ ...commonOptions },
|
||||
// package.min.mjs
|
||||
{
|
||||
...commonOptions,
|
||||
minify: true,
|
||||
metafile: shouldVisualize,
|
||||
},
|
||||
// package.core.mjs
|
||||
{ ...commonOptions, core: true },
|
||||
];
|
||||
|
||||
if (entryName === 'mermaid') {
|
||||
const iifeOptions: MermaidBuildOptions = { ...commonOptions, format: 'iife' };
|
||||
buildConfigs.push(
|
||||
// mermaid.js
|
||||
{ ...iifeOptions },
|
||||
// mermaid.min.js
|
||||
{ ...iifeOptions, minify: true, metafile: shouldVisualize },
|
||||
);
|
||||
}
|
||||
|
||||
const results = await Promise.all(buildConfigs.map((option) => build(getBuildConfig(option))));
|
||||
|
||||
if (shouldVisualize) {
|
||||
for (const { metafile } of results) {
|
||||
if (!metafile) {
|
||||
continue;
|
||||
}
|
||||
const fileName = Object.keys(metafile.outputs)
|
||||
.filter((file) => !file.includes('chunks') && file.endsWith('js'))[0]
|
||||
.replace('dist/', '');
|
||||
// Upload metafile into https://esbuild.github.io/analyze/
|
||||
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handler = (e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
await mkdir('stats').catch(() => {});
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
await Promise.allSettled(packageNames.map((pkg) => buildPackage(pkg).catch(handler)));
|
||||
};
|
||||
|
||||
void main();
|
||||
15
.esbuild/jisonPlugin.ts
Normal file
15
.esbuild/jisonPlugin.ts
Normal 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: [] };
|
||||
});
|
||||
},
|
||||
};
|
||||
35
.esbuild/jsonSchemaPlugin.ts
Normal file
35
.esbuild/jsonSchemaPlugin.ts
Normal 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;
|
||||
107
.esbuild/server.ts
Normal file
107
.esbuild/server.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import express from 'express';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import cors from 'cors';
|
||||
import { getBuildConfig, defaultOptions } from './util.js';
|
||||
import { context } from 'esbuild';
|
||||
import chokidar from 'chokidar';
|
||||
|
||||
const mermaidCtx = await context(
|
||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' }),
|
||||
);
|
||||
const mermaidIIFECtx = await context(
|
||||
getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: false,
|
||||
core: false,
|
||||
entryName: 'mermaid',
|
||||
format: 'iife',
|
||||
}),
|
||||
);
|
||||
const externalCtx = await context(
|
||||
getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: false,
|
||||
core: false,
|
||||
entryName: 'mermaid-example-diagram',
|
||||
}),
|
||||
);
|
||||
const zenumlCtx = await context(
|
||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' }),
|
||||
);
|
||||
const contexts = [mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
|
||||
|
||||
const rebuildAll = async () => {
|
||||
console.time('Rebuild time');
|
||||
await Promise.all(contexts.map((ctx) => ctx.rebuild()));
|
||||
console.timeEnd('Rebuild time');
|
||||
};
|
||||
|
||||
let clients: { id: number; response: Response }[] = [];
|
||||
function eventsHandler(request: Request, response: Response, next: NextFunction) {
|
||||
const headers = {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Connection: 'keep-alive',
|
||||
'Cache-Control': 'no-cache',
|
||||
};
|
||||
response.writeHead(200, headers);
|
||||
const clientId = Date.now();
|
||||
clients.push({
|
||||
id: clientId,
|
||||
response,
|
||||
});
|
||||
request.on('close', () => {
|
||||
clients = clients.filter((client) => client.id !== clientId);
|
||||
});
|
||||
}
|
||||
|
||||
let timeoutId: NodeJS.Timeout | undefined = undefined;
|
||||
|
||||
/**
|
||||
* Debounce file change events to avoid rebuilding multiple times.
|
||||
*/
|
||||
function handleFileChange() {
|
||||
if (timeoutId !== undefined) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
timeoutId = setTimeout(async () => {
|
||||
await rebuildAll();
|
||||
sendEventsToAll();
|
||||
timeoutId = undefined;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function sendEventsToAll() {
|
||||
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
|
||||
}
|
||||
|
||||
async function createServer() {
|
||||
handleFileChange();
|
||||
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();
|
||||
98
.esbuild/util.ts
Normal file
98
.esbuild/util.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { BuildOptions } from 'esbuild';
|
||||
import { readFileSync } from 'fs';
|
||||
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
import { jisonPlugin } from './jisonPlugin.js';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
export interface MermaidBuildOptions {
|
||||
minify: boolean;
|
||||
core: boolean;
|
||||
metafile: boolean;
|
||||
format: 'esm' | 'iife';
|
||||
entryName: keyof typeof packageOptions;
|
||||
}
|
||||
|
||||
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName'> = {
|
||||
minify: false,
|
||||
metafile: false,
|
||||
core: false,
|
||||
format: 'esm',
|
||||
} as const;
|
||||
|
||||
const buildOptions = (override: BuildOptions): BuildOptions => {
|
||||
return {
|
||||
bundle: true,
|
||||
minify: true,
|
||||
keepNames: true,
|
||||
platform: 'browser',
|
||||
tsconfig: 'tsconfig.json',
|
||||
resolveExtensions: ['.ts', '.js', '.json', '.jison', '.yaml'],
|
||||
external: ['require', 'fs', 'path'],
|
||||
outdir: 'dist',
|
||||
plugins: [jisonPlugin, jsonSchemaPlugin],
|
||||
sourcemap: 'external',
|
||||
...override,
|
||||
};
|
||||
};
|
||||
|
||||
const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOptions) => {
|
||||
if (core) {
|
||||
fileName += '.core';
|
||||
} else if (format === 'esm') {
|
||||
fileName += '.esm';
|
||||
}
|
||||
if (minify) {
|
||||
fileName += '.min';
|
||||
}
|
||||
return fileName;
|
||||
};
|
||||
|
||||
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
|
||||
const { core, entryName, metafile, format, minify } = options;
|
||||
const external: string[] = ['require', 'fs', 'path'];
|
||||
const { name, file, packageName } = packageOptions[entryName];
|
||||
const outFileName = getFileName(name, options);
|
||||
let output: BuildOptions = buildOptions({
|
||||
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
|
||||
entryPoints: {
|
||||
[outFileName]: `src/${file}`,
|
||||
},
|
||||
metafile,
|
||||
minify,
|
||||
logLevel: 'info',
|
||||
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
|
||||
});
|
||||
|
||||
if (core) {
|
||||
const { dependencies } = JSON.parse(
|
||||
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8'),
|
||||
);
|
||||
// Core build is used to generate file without bundled dependencies.
|
||||
// This is used by downstream projects to bundle dependencies themselves.
|
||||
// Ignore dependencies and any dependencies of dependencies
|
||||
external.push(...Object.keys(dependencies));
|
||||
output.external = external;
|
||||
}
|
||||
|
||||
if (format === 'iife') {
|
||||
output.format = 'iife';
|
||||
output.splitting = false;
|
||||
output.globalName = '__esbuild_esm_mermaid';
|
||||
// Workaround for removing the .default access in esbuild IIFE.
|
||||
// https://github.com/mermaid-js/mermaid/pull/4109#discussion_r1292317396
|
||||
output.footer = {
|
||||
js: 'globalThis.mermaid = globalThis.__esbuild_esm_mermaid.default;',
|
||||
};
|
||||
output.outExtension = { '.js': '.js' };
|
||||
} else {
|
||||
output.format = 'esm';
|
||||
output.splitting = true;
|
||||
output.outExtension = { '.js': '.mjs' };
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
@@ -48,6 +48,8 @@ module.exports = {
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'cypress/no-async-tests': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': 'error',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
|
||||
15
.github/workflows/e2e.yml
vendored
15
.github/workflows/e2e.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
# Install NPM dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v4
|
||||
uses: cypress-io/github-action@v6
|
||||
id: cypress
|
||||
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
|
||||
# Otherwise (e.g. if running from fork), we run on a single container only
|
||||
@@ -44,14 +44,19 @@ jobs:
|
||||
parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
VITEST_COVERAGE: true
|
||||
VITE_COVERAGE: true
|
||||
CYPRESS_COVERAGE: true
|
||||
CYPRESS_COMMIT: ${{ github.sha }}
|
||||
|
||||
- name: Check coverage files
|
||||
run: |
|
||||
ls -lh ./coverage
|
||||
ls -lh ./coverage/cypress
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
# Run step only pushes to develop and pull_requests
|
||||
if: ${{ steps.cypress.conclusion == 'success' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop')}}
|
||||
with:
|
||||
files: coverage/cypress/lcov.info
|
||||
files: ./coverage/cypress/lcov.info
|
||||
flags: e2e
|
||||
name: mermaid-codecov
|
||||
fail_ci_if_error: false
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -43,8 +43,6 @@ jobs:
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
# Run step only pushes to develop and pull_requests
|
||||
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }}
|
||||
with:
|
||||
files: ./coverage/vitest/lcov.info
|
||||
flags: unit
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -46,3 +46,4 @@ stats/
|
||||
|
||||
demos/dev/**
|
||||
!/demos/dev/example.html
|
||||
!/demos/dev/reload.js
|
||||
|
||||
@@ -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');
|
||||
@@ -32,28 +32,10 @@ const visualizerOptions = (packageName: string, core = false): PluginOption[] =>
|
||||
template: chartType as TemplateType,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
}) as PluginOption
|
||||
}) as 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 () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"faber",
|
||||
"flatmap",
|
||||
"foswiki",
|
||||
"frontmatter",
|
||||
"ftplugin",
|
||||
"gantt",
|
||||
"gitea",
|
||||
@@ -56,6 +57,7 @@
|
||||
"gzipped",
|
||||
"huynh",
|
||||
"huynhicode",
|
||||
"iife",
|
||||
"inkdrop",
|
||||
"jaoude",
|
||||
"jgreywolf",
|
||||
@@ -80,6 +82,7 @@
|
||||
"mdbook",
|
||||
"mermaidjs",
|
||||
"mermerd",
|
||||
"metafile",
|
||||
"mindaugas",
|
||||
"mindmap",
|
||||
"mindmaps",
|
||||
@@ -92,6 +95,7 @@
|
||||
"nikolay",
|
||||
"nirname",
|
||||
"orlandoni",
|
||||
"outdir",
|
||||
"pathe",
|
||||
"pbrolin",
|
||||
"phpbb",
|
||||
|
||||
11
cypress/integration/other/iife.spec.js
Normal file
11
cypress/integration/other/iife.spec.js
Normal 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');
|
||||
});
|
||||
});
|
||||
@@ -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>');
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,6 @@ describe('Configuration and directives - nodes should be light blue', () => {
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from initialize - nodes should be green', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -28,7 +27,6 @@ graph TD
|
||||
end `,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from initialize overriding themeVariable - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -46,7 +44,6 @@ graph TD
|
||||
`,
|
||||
{ theme: 'base', themeVariables: { primaryColor: '#ff0000' }, logLevel: 0 }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from directive - nodes should be grey', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -62,7 +59,24 @@ graph TD
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from frontmatter - nodes should be grey', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: neutral
|
||||
---
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('Settings from directive overriding theme variable - nodes should be red', () => {
|
||||
@@ -79,7 +93,6 @@ graph TD
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Settings from initialize and directive - nodes should be grey', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -95,7 +108,6 @@ graph TD
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Theme from initialize, directive overriding theme variable - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
@@ -111,8 +123,71 @@ graph TD
|
||||
`,
|
||||
{ theme: 'base' }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Theme from initialize, frontmatter overriding theme variable - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: '#ff0000'
|
||||
---
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
});
|
||||
it('Theme from initialize, frontmatter overriding theme variable, directive overriding primaryColor - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: '#00ff00'
|
||||
---
|
||||
%%{init: {'theme': 'base', 'themeVariables':{ 'primaryColor': '#ff0000'}}}%%
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
});
|
||||
|
||||
it('should render if values are not quoted properly', () => {
|
||||
// #ff0000 is not quoted properly, and will evaluate to null.
|
||||
// This test ensures that the rendering still works.
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: #ff0000
|
||||
---
|
||||
graph TD
|
||||
A(Start) --> B[/Another/]
|
||||
A[/Another/] --> C[End]
|
||||
subgraph section
|
||||
B
|
||||
C
|
||||
end
|
||||
`,
|
||||
{ theme: 'forest' }
|
||||
);
|
||||
});
|
||||
|
||||
it('Theme variable from initialize, theme from directive - nodes should be red', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
@@ -127,13 +202,11 @@ graph TD
|
||||
`,
|
||||
{ themeVariables: { primaryColor: '#ff0000' } }
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
describe('when rendering several diagrams', () => {
|
||||
it('diagrams should not taint later diagrams', () => {
|
||||
const url = 'http://localhost:9000/theme-directives.html';
|
||||
cy.visit(url);
|
||||
cy.get('svg');
|
||||
cy.matchImageSnapshot('conf-and-directives.spec-when-rendering-several-diagrams-diagram-1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]);
|
||||
|
||||
29
cypress/platform/iife.html
Normal file
29
cypress/platform/iife.html
Normal 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>
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -140,6 +139,6 @@ if (typeof document !== 'undefined') {
|
||||
void contentLoaded().finally(markRendered);
|
||||
}
|
||||
},
|
||||
false
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<title>Mermaid development page</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre class="mermaid">info</pre>
|
||||
|
||||
<pre id="diagram" class="mermaid">
|
||||
graph TB
|
||||
a --> b
|
||||
@@ -30,5 +32,7 @@ graph TB
|
||||
console.log(svg);
|
||||
el.innerHTML = svg;
|
||||
</script>
|
||||
|
||||
<script src="/dev/reload.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
22
demos/dev/reload.js
Normal file
22
demos/dev/reload.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// Connect to the server and reload the page if the server sends a reload message
|
||||
const connectToEvents = () => {
|
||||
const events = new EventSource('/events');
|
||||
const loadTime = Date.now();
|
||||
events.onmessage = (event) => {
|
||||
const time = JSON.parse(event.data);
|
||||
if (time && time > loadTime) {
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
events.onerror = (error) => {
|
||||
console.error(error);
|
||||
events.close();
|
||||
// Try to reconnect after 1 second in case of errors
|
||||
setTimeout(connectToEvents, 1000);
|
||||
};
|
||||
events.onopen = () => {
|
||||
console.log('Connected to live reload server');
|
||||
};
|
||||
};
|
||||
|
||||
setTimeout(connectToEvents, 500);
|
||||
@@ -21,6 +21,8 @@
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: This is a title
|
||||
config:
|
||||
theme: forest
|
||||
---
|
||||
erDiagram
|
||||
%% title This is a title
|
||||
|
||||
@@ -123,6 +123,13 @@
|
||||
|
||||
<h3>flowchart</h3>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: This is another complicated flow
|
||||
config:
|
||||
theme: base
|
||||
flowchart:
|
||||
curve: cardinal
|
||||
---
|
||||
flowchart LR
|
||||
sid-B3655226-6C29-4D00-B685-3D5C734DC7E1["
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -37,7 +37,7 @@
|
||||
</pre>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
import mermaid from '/mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
theme: 'forest',
|
||||
logLevel: 3,
|
||||
|
||||
@@ -10,10 +10,41 @@ When mermaid starts, configuration is extracted to determine a configuration to
|
||||
|
||||
- The default configuration
|
||||
- Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**.
|
||||
- Directives - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
|
||||
- Frontmatter (v\<MERMAID_RELEASE_VERSION>+) - diagram authors can update select configuration parameters in the frontmatter of the diagram. These are applied to the render config.
|
||||
- Directives (Deprecated by Frontmatter) - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
|
||||
|
||||
**The render config** is configuration that is used when rendering by applying these configurations.
|
||||
|
||||
## Frontmatter config
|
||||
|
||||
The entire mermaid configuration (except the secure configs) can be overridden by the diagram author in the frontmatter of the diagram. The frontmatter is a YAML block at the top of the diagram.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Hello Title
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: "#00ff00"
|
||||
---
|
||||
flowchart
|
||||
Hello --> World
|
||||
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Hello Title
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: "#00ff00"
|
||||
---
|
||||
flowchart
|
||||
Hello --> World
|
||||
|
||||
```
|
||||
|
||||
## Theme configuration
|
||||
|
||||
## Starting mermaid
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
# Directives
|
||||
|
||||
> **Warning**
|
||||
> Directives are deprecated from v\<MERMAID_RELEASE_VERSION>. Please use the `config` key in frontmatter to pass configuration. See [Configuration](./configuration.md) for more details.
|
||||
|
||||
## Directives
|
||||
|
||||
Directives give a diagram author the capability to alter the appearance of a diagram before rendering by changing the applied configuration.
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L77)
|
||||
[mermaidAPI.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L78)
|
||||
|
||||
@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L97)
|
||||
[mermaidAPI.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98)
|
||||
|
||||
---
|
||||
|
||||
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:87](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L87)
|
||||
[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L7)
|
||||
[config.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L8)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -26,9 +26,9 @@ Pushes in a directive to the configuration
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :---------- | :---- | :----------------------- |
|
||||
| `directive` | `any` | The directive to push in |
|
||||
| Name | Type | Description |
|
||||
| :---------- | :-------------- | :----------------------- |
|
||||
| `directive` | `MermaidConfig` | The directive to push in |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -36,7 +36,7 @@ Pushes in a directive to the configuration
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191)
|
||||
[config.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L188)
|
||||
|
||||
---
|
||||
|
||||
@@ -60,7 +60,7 @@ The currentConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L137)
|
||||
[config.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L131)
|
||||
|
||||
---
|
||||
|
||||
@@ -118,7 +118,7 @@ The siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L223)
|
||||
[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218)
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +147,7 @@ options in-place
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L152)
|
||||
[config.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L146)
|
||||
|
||||
---
|
||||
|
||||
@@ -242,10 +242,10 @@ The new siteConfig
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------------ | :-------------- |
|
||||
| `siteCfg` | `MermaidConfig` |
|
||||
| `_directives` | `any`\[] |
|
||||
| Name | Type |
|
||||
| :------------ | :----------------- |
|
||||
| `siteCfg` | `MermaidConfig` |
|
||||
| `_directives` | `MermaidConfig`\[] |
|
||||
|
||||
#### Returns
|
||||
|
||||
@@ -253,7 +253,7 @@ The new siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:14](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L14)
|
||||
[config.ts:15](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L15)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
### configKeys
|
||||
|
||||
• `Const` **configKeys**: `string`\[]
|
||||
• `Const` **configKeys**: `Set`<`string`>
|
||||
|
||||
#### Defined in
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L81)
|
||||
[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82)
|
||||
|
||||
## Variables
|
||||
|
||||
@@ -96,7 +96,7 @@ mermaid.initialize(config);
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:668](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L668)
|
||||
[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -127,7 +127,7 @@ Return the last node appended
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:309](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L309)
|
||||
[mermaidAPI.ts:310](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L310)
|
||||
|
||||
---
|
||||
|
||||
@@ -153,7 +153,7 @@ the cleaned up svgCode
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:255](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L255)
|
||||
[mermaidAPI.ts:256](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L256)
|
||||
|
||||
---
|
||||
|
||||
@@ -179,7 +179,7 @@ the string with all the user styles
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L184)
|
||||
[mermaidAPI.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L185)
|
||||
|
||||
---
|
||||
|
||||
@@ -202,7 +202,7 @@ the string with all the user styles
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:232](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L232)
|
||||
[mermaidAPI.ts:233](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L233)
|
||||
|
||||
---
|
||||
|
||||
@@ -229,7 +229,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:168](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L168)
|
||||
[mermaidAPI.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L169)
|
||||
|
||||
---
|
||||
|
||||
@@ -249,7 +249,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:154](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L154)
|
||||
[mermaidAPI.ts:155](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L155)
|
||||
|
||||
---
|
||||
|
||||
@@ -269,7 +269,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L125)
|
||||
[mermaidAPI.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L126)
|
||||
|
||||
---
|
||||
|
||||
@@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:286](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L286)
|
||||
[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
|
||||
|
||||
---
|
||||
|
||||
@@ -320,4 +320,4 @@ Remove any existing elements from the given document
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:359](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L359)
|
||||
[mermaidAPI.ts:360](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L360)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -289,7 +289,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
|
||||
|
||||
@@ -307,7 +307,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>
|
||||
```
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
|
||||
|
||||
55
package.json
55
package.json
@@ -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",
|
||||
@@ -60,36 +60,37 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@applitools/eyes-cypress": "^3.33.1",
|
||||
"@applitools/eyes-cypress": "^3.37.0",
|
||||
"@commitlint/cli": "^17.6.1",
|
||||
"@commitlint/config-conventional": "^17.6.1",
|
||||
"@cspell/eslint-plugin": "^6.31.1",
|
||||
"@cypress/code-coverage": "^3.10.7",
|
||||
"@rollup/plugin-typescript": "^11.1.1",
|
||||
"@cspell/eslint-plugin": "^7.0.1",
|
||||
"@cypress/code-coverage": "^3.11.0",
|
||||
"@rollup/plugin-typescript": "^11.1.2",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/eslint": "^8.37.0",
|
||||
"@types/eslint": "^8.44.2",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsdom": "^21.1.1",
|
||||
"@types/lodash": "^4.14.194",
|
||||
"@types/mdast": "^3.0.11",
|
||||
"@types/node": "^18.16.0",
|
||||
"@types/prettier": "^2.7.2",
|
||||
"@types/prettier": "^3.0.0",
|
||||
"@types/rollup-plugin-visualizer": "^4.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.0",
|
||||
"@typescript-eslint/parser": "^5.59.0",
|
||||
"@vitest/coverage-v8": "^0.34.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
||||
"@typescript-eslint/parser": "^6.4.1",
|
||||
"@vitest/coverage-v8": "^0.34.2",
|
||||
"@vitest/spy": "^0.34.0",
|
||||
"@vitest/ui": "^0.34.0",
|
||||
"ajv": "^8.12.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"concurrently": "^8.0.1",
|
||||
"cors": "^2.8.5",
|
||||
"cypress": "^12.10.0",
|
||||
"cypress": "^12.17.4",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"esbuild": "^0.19.0",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-cypress": "^2.13.2",
|
||||
"esbuild": "^0.19.2",
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-cypress": "^2.14.0",
|
||||
"eslint-plugin-html": "^7.1.0",
|
||||
"eslint-plugin-jest": "^27.2.1",
|
||||
"eslint-plugin-jsdoc": "^46.0.0",
|
||||
@@ -110,16 +111,16 @@
|
||||
"nyc": "^15.1.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pnpm": "^8.6.8",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier": "^3.0.2",
|
||||
"prettier-plugin-jsdoc": "^0.4.2",
|
||||
"rimraf": "^5.0.0",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"start-server-and-test": "^2.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.3",
|
||||
"vite": "^4.3.9",
|
||||
"vite-plugin-istanbul": "^4.1.0",
|
||||
"vitest": "^0.34.0"
|
||||
"typescript": "^5.1.6",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-istanbul": "^5.0.0",
|
||||
"vitest": "^0.34.2"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.17.1"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"mermaid"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"prepublishOnly": "pnpm -w run build"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "10.3.1",
|
||||
"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",
|
||||
@@ -60,8 +60,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",
|
||||
@@ -74,11 +72,9 @@
|
||||
"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",
|
||||
@@ -86,6 +82,7 @@
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/d3-sankey": "^0.12.1",
|
||||
"@types/d3-scale": "^4.0.3",
|
||||
"@types/d3-scale-chromatic": "^3.0.0",
|
||||
"@types/d3-selection": "^3.0.5",
|
||||
"@types/d3-shape": "^3.1.1",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
|
||||
@@ -24,6 +24,7 @@ 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
|
||||
// @ts-ignore Incorrect types
|
||||
const Ajv2019 = _Ajv2019 as unknown as typeof _Ajv2019.default;
|
||||
|
||||
// !!! -- The config.type.js file is created by this script -- !!!
|
||||
|
||||
@@ -48,7 +48,7 @@ export class Diagram {
|
||||
// extractFrontMatter().
|
||||
|
||||
this.parser.parse = (text: string) =>
|
||||
originalParse(cleanupComments(extractFrontMatter(text, this.db)));
|
||||
originalParse(cleanupComments(extractFrontMatter(text, this.db, configApi.addDirective)));
|
||||
|
||||
this.parser.parser.yy = this.db;
|
||||
this.init = diagram.init;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
/**
|
||||
* assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the
|
||||
* assignWithDepth Extends the functionality of {@link Object.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
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import * as configApi from './config.js';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
|
||||
describe('when working with site config', function () {
|
||||
describe('when working with site config', () => {
|
||||
beforeEach(() => {
|
||||
// Resets the site config to default config
|
||||
configApi.setSiteConfig({});
|
||||
});
|
||||
it('should set site config and config properly', function () {
|
||||
it('should set site config and config properly', () => {
|
||||
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
|
||||
configApi.setSiteConfig(config_0);
|
||||
const config_1 = configApi.getSiteConfig();
|
||||
@@ -14,19 +16,26 @@ describe('when working with site config', function () {
|
||||
expect(config_1.fontSize).toEqual(config_0.fontSize);
|
||||
expect(config_1).toEqual(config_2);
|
||||
});
|
||||
it('should respect secure keys when applying directives', function () {
|
||||
const config_0 = {
|
||||
it('should respect secure keys when applying directives', () => {
|
||||
const config_0: MermaidConfig = {
|
||||
fontFamily: 'foo-font',
|
||||
securityLevel: 'strict', // can't be changed
|
||||
fontSize: 12345, // can't be changed
|
||||
secure: [...configApi.defaultConfig.secure!, 'fontSize'],
|
||||
};
|
||||
configApi.setSiteConfig(config_0);
|
||||
const directive = { fontFamily: 'baf', fontSize: 54321 /* fontSize shouldn't be changed */ };
|
||||
const cfg = configApi.updateCurrentConfig(config_0, [directive]);
|
||||
const directive: MermaidConfig = {
|
||||
fontFamily: 'baf',
|
||||
// fontSize and securityLevel shouldn't be changed
|
||||
fontSize: 54321,
|
||||
securityLevel: 'loose',
|
||||
};
|
||||
const cfg: MermaidConfig = 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);
|
||||
});
|
||||
it('should allow setting partial options', function () {
|
||||
it('should allow setting partial options', () => {
|
||||
const defaultConfig = configApi.getConfig();
|
||||
|
||||
configApi.setConfig({
|
||||
@@ -42,7 +51,7 @@ describe('when working with site config', function () {
|
||||
updatedConfig.quadrantChart!.chartWidth
|
||||
);
|
||||
});
|
||||
it('should set reset config properly', function () {
|
||||
it('should set reset config properly', () => {
|
||||
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
|
||||
configApi.setSiteConfig(config_0);
|
||||
const config_1 = { fontFamily: 'baf' };
|
||||
@@ -55,7 +64,7 @@ describe('when working with site config', function () {
|
||||
const config_4 = configApi.getSiteConfig();
|
||||
expect(config_4.fontFamily).toEqual(config_0.fontFamily);
|
||||
});
|
||||
it('should set global reset config properly', function () {
|
||||
it('should set global reset config properly', () => {
|
||||
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
|
||||
configApi.setSiteConfig(config_0);
|
||||
const config_1 = configApi.getSiteConfig();
|
||||
|
||||
@@ -3,15 +3,16 @@ 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.js';
|
||||
|
||||
export const defaultConfig: MermaidConfig = Object.freeze(config);
|
||||
|
||||
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
|
||||
let configFromInitialize: MermaidConfig;
|
||||
let directives: any[] = [];
|
||||
let directives: MermaidConfig[] = [];
|
||||
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
|
||||
|
||||
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[]) => {
|
||||
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => {
|
||||
// start with config being the siteConfig
|
||||
let cfg: MermaidConfig = assignWithDepth({}, siteCfg);
|
||||
// let sCfg = assignWithDepth(defaultConfig, siteConfigDelta);
|
||||
@@ -20,7 +21,6 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[])
|
||||
let sumOfDirectives: MermaidConfig = {};
|
||||
for (const d of _directives) {
|
||||
sanitize(d);
|
||||
|
||||
// Apply the data from the directive where the the overrides the themeVariables
|
||||
sumOfDirectives = assignWithDepth(sumOfDirectives, d);
|
||||
}
|
||||
@@ -111,12 +111,6 @@ export const getSiteConfig = (): MermaidConfig => {
|
||||
* @returns The currentConfig merged with the sanitized conf
|
||||
*/
|
||||
export const setConfig = (conf: MermaidConfig): MermaidConfig => {
|
||||
// sanitize(conf);
|
||||
// Object.keys(conf).forEach(key => {
|
||||
// const manipulator = manipulators[key];
|
||||
// conf[key] = manipulator ? manipulator(conf[key]) : conf[key];
|
||||
// });
|
||||
|
||||
checkConfig(conf);
|
||||
assignWithDepth(currentConfig, conf);
|
||||
|
||||
@@ -150,9 +144,12 @@ export const getConfig = (): MermaidConfig => {
|
||||
* @param options - The potential setConfig parameter
|
||||
*/
|
||||
export const sanitize = (options: any) => {
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
// Checking that options are not in the list of excluded options
|
||||
['secure', ...(siteConfig.secure ?? [])].forEach((key) => {
|
||||
if (options[key] !== undefined) {
|
||||
if (Object.hasOwn(options, key)) {
|
||||
// DO NOT attempt to print options[key] within `${}` as a malicious script
|
||||
// can exploit the logger's attempt to stringify the value and execute arbitrary code
|
||||
log.debug(`Denied attempt to modify a secure key ${key}`, options[key]);
|
||||
@@ -162,7 +159,7 @@ export const sanitize = (options: any) => {
|
||||
|
||||
// Check that there no attempts of prototype pollution
|
||||
Object.keys(options).forEach((key) => {
|
||||
if (key.indexOf('__') === 0) {
|
||||
if (key.startsWith('__')) {
|
||||
delete options[key];
|
||||
}
|
||||
});
|
||||
@@ -188,16 +185,14 @@ export const sanitize = (options: any) => {
|
||||
*
|
||||
* @param directive - The directive to push in
|
||||
*/
|
||||
export const addDirective = (directive: any) => {
|
||||
if (directive.fontFamily) {
|
||||
if (!directive.themeVariables) {
|
||||
directive.themeVariables = { fontFamily: directive.fontFamily };
|
||||
} else {
|
||||
if (!directive.themeVariables.fontFamily) {
|
||||
directive.themeVariables = { fontFamily: directive.fontFamily };
|
||||
}
|
||||
}
|
||||
export const addDirective = (directive: MermaidConfig) => {
|
||||
sanitizeDirective(directive);
|
||||
|
||||
// If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables
|
||||
if (directive.fontFamily && (!directive.themeVariables || !directive.themeVariables.fontFamily)) {
|
||||
directive.themeVariables = { fontFamily: directive.fontFamily };
|
||||
}
|
||||
|
||||
directives.push(directive);
|
||||
updateCurrentConfig(siteConfig, directives);
|
||||
};
|
||||
|
||||
@@ -265,5 +265,5 @@ const keyify = (obj: any, prefix = ''): string[] =>
|
||||
return [...res, prefix + el];
|
||||
}, []);
|
||||
|
||||
export const configKeys: string[] = keyify(config, '');
|
||||
export const configKeys: Set<string> = new Set(keyify(config, ''));
|
||||
export default config;
|
||||
|
||||
@@ -2,8 +2,13 @@ import { vi } from 'vitest';
|
||||
import { extractFrontMatter } from './frontmatter.js';
|
||||
|
||||
const dbMock = () => ({ setDiagramTitle: vi.fn() });
|
||||
const setConfigMock = vi.fn();
|
||||
|
||||
describe('extractFrontmatter', () => {
|
||||
beforeEach(() => {
|
||||
setConfigMock.mockClear();
|
||||
});
|
||||
|
||||
it('returns text unchanged if no frontmatter', () => {
|
||||
expect(extractFrontMatter('diagram', dbMock())).toEqual('diagram');
|
||||
});
|
||||
@@ -75,4 +80,21 @@ describe('extractFrontmatter', () => {
|
||||
'tag suffix cannot contain exclamation marks'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles frontmatter with config', () => {
|
||||
const text = `---
|
||||
title: hello
|
||||
config:
|
||||
graph:
|
||||
string: hello
|
||||
number: 14
|
||||
boolean: false
|
||||
array: [1, 2, 3]
|
||||
---
|
||||
diagram`;
|
||||
expect(extractFrontMatter(text, {}, setConfigMock)).toEqual('diagram');
|
||||
expect(setConfigMock).toHaveBeenCalledWith({
|
||||
graph: { string: 'hello', number: 14, boolean: false, array: [1, 2, 3] },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,40 +1,53 @@
|
||||
import type { DiagramDB } from './types.js';
|
||||
import type { MermaidConfig } from '../config.type.js';
|
||||
import { frontMatterRegex } from './regexes.js';
|
||||
import type { DiagramDB } from './types.js';
|
||||
// The "* as yaml" part is necessary for tree-shaking
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
type FrontMatterMetadata = {
|
||||
interface FrontMatterMetadata {
|
||||
title?: string;
|
||||
// Allows custom display modes. Currently used for compact mode in gantt charts.
|
||||
displayMode?: string;
|
||||
};
|
||||
config?: MermaidConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and parse frontmatter from text, if present, and sets appropriate
|
||||
* properties in the provided db.
|
||||
* @param text - The text that may have a YAML frontmatter.
|
||||
* @param db - Diagram database, could be of any diagram.
|
||||
* @param setDiagramConfig - Optional function to set diagram config.
|
||||
* @returns text with frontmatter stripped out
|
||||
*/
|
||||
export function extractFrontMatter(text: string, db: DiagramDB): string {
|
||||
export function extractFrontMatter(
|
||||
text: string,
|
||||
db: DiagramDB,
|
||||
setDiagramConfig?: (config: MermaidConfig) => void
|
||||
): string {
|
||||
const matches = text.match(frontMatterRegex);
|
||||
if (matches) {
|
||||
const parsed: FrontMatterMetadata = yaml.load(matches[1], {
|
||||
// To keep things simple, only allow strings, arrays, and plain objects.
|
||||
// https://www.yaml.org/spec/1.2/spec.html#id2802346
|
||||
schema: yaml.FAILSAFE_SCHEMA,
|
||||
}) as FrontMatterMetadata;
|
||||
|
||||
if (parsed?.title) {
|
||||
db.setDiagramTitle?.(parsed.title);
|
||||
}
|
||||
|
||||
if (parsed?.displayMode) {
|
||||
db.setDisplayMode?.(parsed.displayMode);
|
||||
}
|
||||
|
||||
return text.slice(matches[0].length);
|
||||
} else {
|
||||
if (!matches) {
|
||||
return text;
|
||||
}
|
||||
|
||||
const parsed: FrontMatterMetadata = yaml.load(matches[1], {
|
||||
// To support config, we need JSON schema.
|
||||
// https://www.yaml.org/spec/1.2/spec.html#id2803231
|
||||
schema: yaml.JSON_SCHEMA,
|
||||
}) as FrontMatterMetadata;
|
||||
|
||||
if (parsed?.title) {
|
||||
// toString() is necessary because YAML could parse the title as a number/boolean
|
||||
db.setDiagramTitle?.(parsed.title.toString());
|
||||
}
|
||||
|
||||
if (parsed?.displayMode) {
|
||||
// toString() is necessary because YAML could parse the title as a number/boolean
|
||||
db.setDisplayMode?.(parsed.displayMode.toString());
|
||||
}
|
||||
|
||||
if (parsed?.config) {
|
||||
setDiagramConfig?.(parsed.config);
|
||||
}
|
||||
|
||||
return text.slice(matches[0].length);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as configApi from './config.js';
|
||||
|
||||
import { log } from './logger.js';
|
||||
import { directiveSanitizer } from './utils.js';
|
||||
|
||||
let currentDirective: { type?: string; args?: any } | undefined = {};
|
||||
|
||||
@@ -60,9 +58,6 @@ const handleDirective = function (p: any, directive: any, type: string): void {
|
||||
delete directive.args[prop];
|
||||
}
|
||||
});
|
||||
log.info('sanitize in handleDirective', directive.args);
|
||||
directiveSanitizer(directive.args);
|
||||
log.info('sanitize in handleDirective (done)', directive.args);
|
||||
configApi.addDirective(directive.args);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,28 @@ When mermaid starts, configuration is extracted to determine a configuration to
|
||||
|
||||
- The default configuration
|
||||
- Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**.
|
||||
- Directives - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
|
||||
- Frontmatter (v<MERMAID_RELEASE_VERSION>+) - diagram authors can update select configuration parameters in the frontmatter of the diagram. These are applied to the render config.
|
||||
- Directives (Deprecated by Frontmatter) - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
|
||||
|
||||
**The render config** is configuration that is used when rendering by applying these configurations.
|
||||
|
||||
## Frontmatter config
|
||||
|
||||
The entire mermaid configuration (except the secure configs) can be overridden by the diagram author in the frontmatter of the diagram. The frontmatter is a YAML block at the top of the diagram.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Hello Title
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
primaryColor: "#00ff00"
|
||||
---
|
||||
flowchart
|
||||
Hello --> World
|
||||
|
||||
```
|
||||
|
||||
## Theme configuration
|
||||
|
||||
## Starting mermaid
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Directives
|
||||
|
||||
```warning
|
||||
Directives are deprecated from v<MERMAID_RELEASE_VERSION>. Please use the `config` key in frontmatter to pass configuration. See [Configuration](./configuration.md) for more details.
|
||||
```
|
||||
|
||||
## Directives
|
||||
|
||||
Directives give a diagram author the capability to alter the appearance of a diagram before rendering by changing the applied configuration.
|
||||
|
||||
@@ -23,13 +23,14 @@ import { attachFunctions } from './interactionDb.js';
|
||||
import { log, setLogLevel } from './logger.js';
|
||||
import getStyles from './styles.js';
|
||||
import theme from './themes/index.js';
|
||||
import utils, { directiveSanitizer } from './utils.js';
|
||||
import utils from './utils.js';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { 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 { parseDirective } from './directiveUtils.js';
|
||||
import { extractFrontMatter } from './diagram-api/frontmatter.js';
|
||||
|
||||
// diagram names that support classDef statements
|
||||
const CLASSDEF_DIAGRAMS = [
|
||||
@@ -385,10 +386,14 @@ const render = async function (
|
||||
|
||||
configApi.reset();
|
||||
|
||||
// Add Directives. Must do this before getting the config and before creating the diagram.
|
||||
// We need to add the directives before creating the diagram.
|
||||
// So extractFrontMatter is called twice. Once here and once in the diagram parser.
|
||||
// This can be fixed in a future refactor.
|
||||
extractFrontMatter(text, {}, configApi.addDirective);
|
||||
|
||||
// Add Directives.
|
||||
const graphInit = utils.detectInit(text);
|
||||
if (graphInit) {
|
||||
directiveSanitizer(graphInit);
|
||||
configApi.addDirective(graphInit);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
# - `scripts/create-types-from-json-schema.mjs`
|
||||
# Used to generate the `src/config.type.ts` TypeScript types for MermaidConfig
|
||||
# with the `json-schema-to-typescript` NPM package.
|
||||
# - `.vite/jsonSchemaPlugin.ts`
|
||||
# - `.build/jsonSchema.ts`
|
||||
# Used to generate the default values from the `default` keys in this
|
||||
# JSON Schema using the `ajv` NPM package.
|
||||
# Non-JSON values, like functions or `undefined`, still need to be manually
|
||||
# set in `src/defaultConfig.ts`)
|
||||
# - `src/docs.mts`
|
||||
# Used to genereate Markdown documentation for this JSON Schema by using
|
||||
# Used to generate Markdown documentation for this JSON Schema by using
|
||||
# the `@adobe/jsonschema2md` NPM package.
|
||||
|
||||
# Useful things to know when editting this file
|
||||
# Useful things to know when editing this file
|
||||
# - 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),
|
||||
# you may need to add it to `.vite/jsonSchemaPlugin.ts` and `src/docs.mts`
|
||||
# you may need to add it to `.build/jsonSchema.ts` and `src/docs.mts`
|
||||
# to get the docs/default values to generate properly.
|
||||
$id: https://mermaid-js.github.io/schemas/config.schema.json
|
||||
$schema: https://json-schema.org/draft/2019-09/schema
|
||||
@@ -1851,7 +1851,7 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
||||
The color of the links in the sankey diagram.
|
||||
anyOf:
|
||||
- $ref: '#/$defs/SankeyLinkColor'
|
||||
- description: An arbtirary [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value)
|
||||
- description: An arbitrary [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value)
|
||||
type: string
|
||||
default: gradient
|
||||
nodeAlignment:
|
||||
|
||||
@@ -97,32 +97,36 @@ 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 {
|
||||
export const detectInit = function (
|
||||
text: string,
|
||||
config?: MermaidConfig
|
||||
): MermaidConfig | undefined {
|
||||
const inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/);
|
||||
let results = {};
|
||||
|
||||
if (Array.isArray(inits)) {
|
||||
const args = inits.map((init) => init.args);
|
||||
directiveSanitizer(args);
|
||||
|
||||
sanitizeDirective(args);
|
||||
results = assignWithDepth(results, [...args]);
|
||||
} else {
|
||||
results = inits.args;
|
||||
}
|
||||
if (results) {
|
||||
let type = detectType(text, config);
|
||||
['config'].forEach((prop) => {
|
||||
if (results[prop] !== undefined) {
|
||||
if (type === 'flowchart-v2') {
|
||||
type = 'flowchart';
|
||||
}
|
||||
results[type] = results[prop];
|
||||
delete results[prop];
|
||||
}
|
||||
});
|
||||
|
||||
if (!results) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Todo: refactor this, these results are never used
|
||||
let type = detectType(text, config);
|
||||
['config'].forEach((prop) => {
|
||||
if (results[prop] !== undefined) {
|
||||
if (type === 'flowchart-v2') {
|
||||
type = 'flowchart';
|
||||
}
|
||||
results[type] = results[prop];
|
||||
delete results[prop];
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -843,67 +847,63 @@ export const entityDecode = function (html: string): string {
|
||||
*
|
||||
* @param args - Directive's JSON
|
||||
*/
|
||||
export const directiveSanitizer = (args: any) => {
|
||||
log.debug('directiveSanitizer called with', args);
|
||||
if (typeof args === 'object') {
|
||||
// check for array
|
||||
if (args.length) {
|
||||
args.forEach((arg) => directiveSanitizer(arg));
|
||||
} else {
|
||||
// This is an object
|
||||
Object.keys(args).forEach((key) => {
|
||||
log.debug('Checking key', key);
|
||||
if (key.startsWith('__')) {
|
||||
log.debug('sanitize deleting __ option', key);
|
||||
delete args[key];
|
||||
}
|
||||
export const sanitizeDirective = (args: unknown): void => {
|
||||
log.debug('sanitizeDirective called with', args);
|
||||
|
||||
if (key.includes('proto')) {
|
||||
log.debug('sanitize deleting proto option', key);
|
||||
delete args[key];
|
||||
}
|
||||
// Return if not an object
|
||||
if (typeof args !== 'object' || args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.includes('constr')) {
|
||||
log.debug('sanitize deleting constr option', key);
|
||||
delete args[key];
|
||||
}
|
||||
// Sanitize each element if an array
|
||||
if (Array.isArray(args)) {
|
||||
args.forEach((arg) => sanitizeDirective(arg));
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.includes('themeCSS')) {
|
||||
log.debug('sanitizing themeCss option');
|
||||
args[key] = sanitizeCss(args[key]);
|
||||
}
|
||||
if (key.includes('fontFamily')) {
|
||||
log.debug('sanitizing fontFamily option');
|
||||
args[key] = sanitizeCss(args[key]);
|
||||
}
|
||||
if (key.includes('altFontFamily')) {
|
||||
log.debug('sanitizing altFontFamily option');
|
||||
args[key] = sanitizeCss(args[key]);
|
||||
}
|
||||
if (!configKeys.includes(key)) {
|
||||
log.debug('sanitize deleting option', key);
|
||||
delete args[key];
|
||||
} else {
|
||||
if (typeof args[key] === 'object') {
|
||||
log.debug('sanitize deleting object', key);
|
||||
directiveSanitizer(args[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Sanitize each key if an object
|
||||
for (const key of Object.keys(args)) {
|
||||
log.debug('Checking key', key);
|
||||
if (
|
||||
key.startsWith('__') ||
|
||||
key.includes('proto') ||
|
||||
key.includes('constr') ||
|
||||
!configKeys.has(key) ||
|
||||
args[key] == null
|
||||
) {
|
||||
log.debug('sanitize deleting key: ', key);
|
||||
delete args[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recurse if an object
|
||||
if (typeof args[key] === 'object') {
|
||||
log.debug('sanitizing object', key);
|
||||
sanitizeDirective(args[key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
const cssMatchers = ['themeCSS', 'fontFamily', 'altFontFamily'];
|
||||
for (const cssKey of cssMatchers) {
|
||||
if (key.includes(cssKey)) {
|
||||
log.debug('sanitizing css option', key);
|
||||
args[key] = sanitizeCss(args[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.themeVariables) {
|
||||
const kArr = Object.keys(args.themeVariables);
|
||||
for (const k of kArr) {
|
||||
for (const k of Object.keys(args.themeVariables)) {
|
||||
const val = args.themeVariables[k];
|
||||
if (val && val.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) {
|
||||
if (val?.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) {
|
||||
args.themeVariables[k] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
log.debug('After sanitization', args);
|
||||
};
|
||||
export const sanitizeCss = (str) => {
|
||||
|
||||
export const sanitizeCss = (str: string): string => {
|
||||
let startCnt = 0;
|
||||
let endCnt = 0;
|
||||
|
||||
@@ -1020,8 +1020,8 @@ export default {
|
||||
random,
|
||||
runFunc,
|
||||
entityDecode,
|
||||
initIdGenerator: initIdGenerator,
|
||||
directiveSanitizer,
|
||||
initIdGenerator,
|
||||
sanitizeDirective,
|
||||
sanitizeCss,
|
||||
insertTitle,
|
||||
parseFontSize,
|
||||
|
||||
1590
pnpm-lock.yaml
generated
1590
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -5,20 +5,24 @@
|
||||
* (i.e. the root of the Mermaid project).
|
||||
*/
|
||||
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import prettier from 'prettier';
|
||||
|
||||
const filepath = './cSpell.json';
|
||||
const cSpell: { words: string[] } = JSON.parse(readFileSync(filepath, 'utf8'));
|
||||
const main = async () => {
|
||||
const filepath = './cSpell.json';
|
||||
const cSpell: { words: string[] } = JSON.parse(await readFile(filepath, 'utf8'));
|
||||
|
||||
cSpell.words = [...new Set(cSpell.words.map((word) => word.toLowerCase()))];
|
||||
cSpell.words.sort((a, b) => a.localeCompare(b));
|
||||
cSpell.words = [...new Set(cSpell.words.map((word) => word.toLowerCase()))];
|
||||
cSpell.words.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
const prettierConfig = prettier.resolveConfig.sync(filepath) ?? {};
|
||||
writeFileSync(
|
||||
filepath,
|
||||
prettier.format(JSON.stringify(cSpell), {
|
||||
...prettierConfig,
|
||||
const prettierConfig = (await prettier.resolveConfig(filepath)) ?? {};
|
||||
await writeFile(
|
||||
filepath,
|
||||
})
|
||||
);
|
||||
await prettier.format(JSON.stringify(cSpell), {
|
||||
...prettierConfig,
|
||||
filepath,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
void main();
|
||||
|
||||
@@ -5,5 +5,14 @@
|
||||
// ensure that nobody can accidentally use this config for a build
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["packages", "tests", "scripts", "cypress", "__mocks__", "./.eslintrc.cjs", "./*"]
|
||||
"include": [
|
||||
"packages",
|
||||
"tests",
|
||||
"scripts",
|
||||
"cypress",
|
||||
"__mocks__",
|
||||
"./.eslintrc.cjs",
|
||||
"./*",
|
||||
"demos/dev"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user