mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-17 23:39:26 +02:00
Compare commits
65 Commits
bug/2234_c
...
sidv/class
Author | SHA1 | Date | |
---|---|---|---|
![]() |
345b75cc0e | ||
![]() |
0ebea7744b | ||
![]() |
44b93c039a | ||
![]() |
4d5313699e | ||
![]() |
cd198290d7 | ||
![]() |
60ed7d3273 | ||
![]() |
9bcfba6620 | ||
![]() |
7ea3c64268 | ||
![]() |
2b6a34e9e0 | ||
![]() |
458b90c78d | ||
![]() |
dd284c0986 | ||
![]() |
21539dfb6a | ||
![]() |
91785b8284 | ||
![]() |
f202770b70 | ||
![]() |
8186a54962 | ||
![]() |
866909b803 | ||
![]() |
408910e6e8 | ||
![]() |
24c8e575f4 | ||
![]() |
8d0ca2c876 | ||
![]() |
fc96ebefd4 | ||
![]() |
394330175f | ||
![]() |
f946c3da06 | ||
![]() |
156fbd1958 | ||
![]() |
7dd0d126e2 | ||
![]() |
205360c109 | ||
![]() |
984a0e6d06 | ||
![]() |
eb63568ceb | ||
![]() |
cc6f896b69 | ||
![]() |
83e47a7216 | ||
![]() |
1d64549cce | ||
![]() |
4ae361bd1f | ||
![]() |
6502496a2c | ||
![]() |
8678ceeb3c | ||
![]() |
9cb62f4d2e | ||
![]() |
6c0ef54e18 | ||
![]() |
fd731c5ccd | ||
![]() |
cbe9490dc0 | ||
![]() |
82054bfabc | ||
![]() |
963dd75c39 | ||
![]() |
1c24617f98 | ||
![]() |
1559c2ca21 | ||
![]() |
6141722b1f | ||
![]() |
222d8eed4e | ||
![]() |
718d52a72c | ||
![]() |
fe1a06271a | ||
![]() |
bd2370555b | ||
![]() |
86c9ee4e90 | ||
![]() |
b26bcf1343 | ||
![]() |
5d5c6275f9 | ||
![]() |
9c1a47d1fc | ||
![]() |
13852b7f4e | ||
![]() |
4fd7a88a15 | ||
![]() |
5c2a6b5eb1 | ||
![]() |
9cbebbb8a0 | ||
![]() |
e26d987c4e | ||
![]() |
53669efaf8 | ||
![]() |
b68f45ef59 | ||
![]() |
8f44de651b | ||
![]() |
2ede244da0 | ||
![]() |
77a181978e | ||
![]() |
170bbce0d3 | ||
![]() |
fc99d9be41 | ||
![]() |
9fb9bed806 | ||
![]() |
01b2f80a95 | ||
![]() |
da7ff777d1 |
25
.build/common.ts
Normal file
25
.build/common.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Shared common options for both ESBuild and Vite
|
||||
*/
|
||||
export const packageOptions = {
|
||||
parser: {
|
||||
name: 'mermaid-parser',
|
||||
packageName: 'parser',
|
||||
file: 'index.ts',
|
||||
},
|
||||
mermaid: {
|
||||
name: 'mermaid',
|
||||
packageName: 'mermaid',
|
||||
file: 'mermaid.ts',
|
||||
},
|
||||
'mermaid-example-diagram': {
|
||||
name: 'mermaid-example-diagram',
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-zenuml': {
|
||||
name: 'mermaid-zenuml',
|
||||
packageName: 'mermaid-zenuml',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
} as const;
|
5
.build/generateLangium.ts
Normal file
5
.build/generateLangium.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { generate } from 'langium-cli';
|
||||
|
||||
export async function generateLangium() {
|
||||
await generate({ file: `./packages/parser/langium-config.json` });
|
||||
}
|
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)};`;
|
||||
};
|
9
.build/langium-cli.d.ts
vendored
Normal file
9
.build/langium-cli.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
declare module 'langium-cli' {
|
||||
export interface GenerateOptions {
|
||||
file?: string;
|
||||
mode?: 'development' | 'production';
|
||||
watch?: boolean;
|
||||
}
|
||||
|
||||
export function generate(options: GenerateOptions): Promise<boolean>;
|
||||
}
|
65
.esbuild/build.ts
Normal file
65
.esbuild/build.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { build } from 'esbuild';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
|
||||
const shouldVisualize = process.argv.includes('--visualize');
|
||||
|
||||
const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
||||
const commonOptions = { ...defaultOptions, entryName } as const;
|
||||
const buildConfigs = [
|
||||
// package.mjs
|
||||
{ ...commonOptions },
|
||||
// package.min.mjs
|
||||
{
|
||||
...commonOptions,
|
||||
minify: true,
|
||||
metafile: shouldVisualize,
|
||||
},
|
||||
// package.core.mjs
|
||||
{ ...commonOptions, core: true },
|
||||
];
|
||||
|
||||
if (entryName === 'mermaid') {
|
||||
const iifeOptions: MermaidBuildOptions = { ...commonOptions, format: 'iife' };
|
||||
buildConfigs.push(
|
||||
// mermaid.js
|
||||
{ ...iifeOptions },
|
||||
// mermaid.min.js
|
||||
{ ...iifeOptions, minify: true, metafile: shouldVisualize }
|
||||
);
|
||||
}
|
||||
|
||||
const results = await Promise.all(buildConfigs.map((option) => build(getBuildConfig(option))));
|
||||
|
||||
if (shouldVisualize) {
|
||||
for (const { metafile } of results) {
|
||||
if (!metafile) {
|
||||
continue;
|
||||
}
|
||||
const fileName = Object.keys(metafile.outputs)
|
||||
.filter((file) => !file.includes('chunks') && file.endsWith('js'))[0]
|
||||
.replace('dist/', '');
|
||||
// Upload metafile into https://esbuild.github.io/analyze/
|
||||
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handler = (e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
await generateLangium();
|
||||
await mkdir('stats').catch(() => {});
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
// it should build `parser` before `mermaid` because it's a dependecy
|
||||
for (const pkg of packageNames) {
|
||||
await buildPackage(pkg).catch(handler);
|
||||
}
|
||||
};
|
||||
|
||||
void main();
|
15
.esbuild/jisonPlugin.ts
Normal file
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;
|
116
.esbuild/server.ts
Normal file
116
.esbuild/server.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import express from 'express';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import cors from 'cors';
|
||||
import { getBuildConfig, defaultOptions } from './util.js';
|
||||
import { context } from 'esbuild';
|
||||
import chokidar from 'chokidar';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
|
||||
const parserCtx = await context(
|
||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'parser' })
|
||||
);
|
||||
const mermaidCtx = await context(
|
||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' })
|
||||
);
|
||||
const mermaidIIFECtx = await context(
|
||||
getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: false,
|
||||
core: false,
|
||||
entryName: 'mermaid',
|
||||
format: 'iife',
|
||||
})
|
||||
);
|
||||
const externalCtx = await context(
|
||||
getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: false,
|
||||
core: false,
|
||||
entryName: 'mermaid-example-diagram',
|
||||
})
|
||||
);
|
||||
const zenumlCtx = await context(
|
||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' })
|
||||
);
|
||||
const contexts = [parserCtx, mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
|
||||
|
||||
const rebuildAll = async () => {
|
||||
console.time('Rebuild time');
|
||||
await Promise.all(contexts.map((ctx) => ctx.rebuild()));
|
||||
console.timeEnd('Rebuild time');
|
||||
};
|
||||
|
||||
let clients: { id: number; response: Response }[] = [];
|
||||
function eventsHandler(request: Request, response: Response, next: NextFunction) {
|
||||
const headers = {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Connection: 'keep-alive',
|
||||
'Cache-Control': 'no-cache',
|
||||
};
|
||||
response.writeHead(200, headers);
|
||||
const clientId = Date.now();
|
||||
clients.push({
|
||||
id: clientId,
|
||||
response,
|
||||
});
|
||||
request.on('close', () => {
|
||||
clients = clients.filter((client) => client.id !== clientId);
|
||||
});
|
||||
}
|
||||
|
||||
let timeoutId: NodeJS.Timeout | undefined = undefined;
|
||||
|
||||
/**
|
||||
* Debounce file change events to avoid rebuilding multiple times.
|
||||
*/
|
||||
function handleFileChange() {
|
||||
if (timeoutId !== undefined) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
timeoutId = setTimeout(async () => {
|
||||
await rebuildAll();
|
||||
sendEventsToAll();
|
||||
timeoutId = undefined;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function sendEventsToAll() {
|
||||
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
|
||||
}
|
||||
|
||||
async function createServer() {
|
||||
await generateLangium();
|
||||
handleFileChange();
|
||||
const app = express();
|
||||
chokidar
|
||||
.watch('**/src/**/*.{js,ts,langium,yaml,json}', {
|
||||
ignoreInitial: true,
|
||||
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
|
||||
})
|
||||
.on('all', async (event, path) => {
|
||||
// Ignore other events.
|
||||
if (!['add', 'change'].includes(event)) {
|
||||
return;
|
||||
}
|
||||
if (/\.langium$/.test(path)) {
|
||||
await generateLangium();
|
||||
}
|
||||
console.log(`${path} changed. Rebuilding...`);
|
||||
handleFileChange();
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
app.get('/events', eventsHandler);
|
||||
app.use(express.static('./packages/parser/dist'));
|
||||
app.use(express.static('./packages/mermaid/dist'));
|
||||
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||
app.use(express.static('demos'));
|
||||
app.use(express.static('cypress/platform'));
|
||||
|
||||
app.listen(9000, () => {
|
||||
console.log(`Listening on http://localhost:9000`);
|
||||
});
|
||||
}
|
||||
|
||||
createServer();
|
98
.esbuild/util.ts
Normal file
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;
|
||||
};
|
@@ -6,3 +6,6 @@ cypress/plugins/index.js
|
||||
coverage
|
||||
*.json
|
||||
node_modules
|
||||
|
||||
# autogenereated by langium-cli
|
||||
generated/
|
||||
|
@@ -49,10 +49,8 @@ module.exports = {
|
||||
'no-unused-vars': 'off',
|
||||
'cypress/no-async-tests': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': 'error',
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
|
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -17,9 +17,6 @@ body:
|
||||
- Use a clear and concise title
|
||||
- Fill out the text fields with as much detail as possible.
|
||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||
|
||||
There is a chance that the bug is already fixed in the git `develop` branch, but is not released yet.
|
||||
So please check in [Live Editor - Develop](https://develop.git.mermaid.live) before raising an issue.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
@@ -46,7 +43,7 @@ body:
|
||||
attributes:
|
||||
label: Code Sample
|
||||
description: |-
|
||||
If applicable, add the code sample or a link to the [Live Editor - Develop](https://develop.git.mermaid.live).
|
||||
If applicable, add the code sample or a link to the [Live Editor](https://mermaid.live).
|
||||
Any text pasted here will be rendered as a Code block.
|
||||
render: text
|
||||
- type: textarea
|
||||
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -7,14 +7,8 @@ contact_links:
|
||||
url: https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE
|
||||
about: Join our Community on Slack for Help and a casual chat.
|
||||
- name: Documentation
|
||||
url: https://mermaid.js.org
|
||||
url: https://mermaid-js.github.io
|
||||
about: Read our documentation for all that Mermaid.js can offer.
|
||||
- name: Live Editor
|
||||
url: https://mermaid.live
|
||||
about: Try the live editor to preview graphs in no time.
|
||||
- name: Live Editor - Develop
|
||||
url: https://develop.git.mermaid.live
|
||||
about: Try unreleased changes in the develop branch.
|
||||
- name: Live Editor - Next
|
||||
url: https://next.git.mermaid.live
|
||||
about: Try unreleased changes in the next branch.
|
||||
|
26
.github/pr-labeler.yml
vendored
26
.github/pr-labeler.yml
vendored
@@ -1,22 +1,4 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/release-drafter/release-drafter/master/schema.json
|
||||
autolabeler:
|
||||
- label: 'Type: Bug / Error'
|
||||
branch:
|
||||
- '/bug\/.+/'
|
||||
- '/fix\/.+/'
|
||||
- label: 'Type: Enhancement'
|
||||
branch:
|
||||
- '/feature\/.+/'
|
||||
- '/feat\/.+/'
|
||||
- label: 'Type: Other'
|
||||
branch:
|
||||
- '/other\/.+/'
|
||||
- '/chore\/.+/'
|
||||
- '/test\/.+/'
|
||||
- '/refactor\/.+/'
|
||||
- label: 'Area: Documentation'
|
||||
branch:
|
||||
- '/docs\/.+/'
|
||||
|
||||
template: |
|
||||
This field is unused, as we only use this config file for labeling PRs.
|
||||
'Type: Bug / Error': ['bug/*', fix/*]
|
||||
'Type: Enhancement': ['feature/*', 'feat/*']
|
||||
'Type: Other': ['other/*', 'chore/*', 'test/*', 'refactor/*']
|
||||
'Area: Documentation': ['docs/*']
|
||||
|
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -14,5 +14,5 @@ Make sure you
|
||||
|
||||
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
|
||||
- [ ] :computer: have added necessary unit/e2e tests.
|
||||
- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/docs/community/contributing.md#update-documentation) is used for all new features.
|
||||
- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/docs/community/development.md#3-update-documentation) is used for all new features.
|
||||
- [ ] :bookmark: targeted `develop` branch
|
||||
|
2
.github/release-drafter.yml
vendored
2
.github/release-drafter.yml
vendored
@@ -25,6 +25,8 @@ categories:
|
||||
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
|
||||
sort-by: title
|
||||
sort-direction: ascending
|
||||
branches:
|
||||
- develop
|
||||
exclude-labels:
|
||||
- 'Skip changelog'
|
||||
no-changes-template: 'This release contains minor changes and bugfixes.'
|
||||
|
6
.github/workflows/build-docs.yml
vendored
6
.github/workflows/build-docs.yml
vendored
@@ -16,12 +16,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: 18
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
- name: Install Packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify release version
|
||||
- name: Verify release verion
|
||||
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release')) }}
|
||||
run: pnpm --filter mermaid run docs:verify-version
|
||||
|
||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -19,13 +19,13 @@ jobs:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
2
.github/workflows/check-readme-in-sync.yml
vendored
2
.github/workflows/check-readme-in-sync.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check for difference in README.md and docs/README.md
|
||||
run: |
|
||||
|
2
.github/workflows/checks.yml
vendored
2
.github/workflows/checks.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
name: check tests
|
||||
if: github.repository_owner == 'mermaid-js'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: testomatio/check-tests@stable
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
4
.github/workflows/dependency-review.yml
vendored
4
.github/workflows/dependency-review.yml
vendored
@@ -1,6 +1,6 @@
|
||||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
|
||||
@@ -15,6 +15,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
|
6
.github/workflows/e2e-applitools.yml
vendored
6
.github/workflows/e2e-applitools.yml
vendored
@@ -28,15 +28,15 @@ jobs:
|
||||
- if: ${{ ! env.USE_APPLI }}
|
||||
name: Warn if not using Applitools
|
||||
run: |
|
||||
echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
|
||||
echo "::error,title=Not using Applitols::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
|
113
.github/workflows/e2e.yml
vendored
113
.github/workflows/e2e.yml
vendored
@@ -1,87 +1,32 @@
|
||||
# We use github cache to save snapshots between runs.
|
||||
# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
|
||||
# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
|
||||
# These are then downloaded before running the E2E, providing the reference snapshots.
|
||||
# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
|
||||
|
||||
name: E2E
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-readonly-queue/**'
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
|
||||
targetHash: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }}
|
||||
|
||||
jobs:
|
||||
cache:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18.x
|
||||
|
||||
- name: Cache snapshots
|
||||
id: cache-snapshot
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
save-always: true
|
||||
path: ./cypress/snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||
|
||||
# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
|
||||
- name: Switch to base branch
|
||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ env.targetHash }}
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v4
|
||||
id: cypress-snapshot-gen
|
||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
|
||||
with:
|
||||
start: pnpm run dev
|
||||
wait-on: 'http://localhost:9000'
|
||||
browser: chrome
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
needs: cache
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
containers: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
# These cached snapshots are downloaded, providing the reference snapshots.
|
||||
- name: Cache snapshots
|
||||
id: cache-snapshot
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: ./cypress/snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||
|
||||
# Install NPM dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
@@ -93,7 +38,6 @@ jobs:
|
||||
with:
|
||||
start: pnpm run dev:coverage
|
||||
wait-on: 'http://localhost:9000'
|
||||
browser: chrome
|
||||
# Disable recording if we don't have an API key
|
||||
# e.g. if this action was run from a fork
|
||||
record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
|
||||
@@ -102,7 +46,6 @@ jobs:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
VITEST_COVERAGE: true
|
||||
CYPRESS_COMMIT: ${{ github.sha }}
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
# Run step only pushes to develop and pull_requests
|
||||
@@ -114,55 +57,9 @@ jobs:
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
|
||||
|
||||
# We upload the artifacts into numbered archives to prevent overwriting
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: snapshots-${{ matrix.containers }}
|
||||
retention-days: 1
|
||||
path: ./cypress/snapshots
|
||||
|
||||
combineArtifacts:
|
||||
needs: e2e
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() }}
|
||||
steps:
|
||||
# Download all snapshot artifacts and merge them into a single folder
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: snapshots
|
||||
pattern: snapshots-*
|
||||
merge-multiple: true
|
||||
|
||||
# For successful push events, we save the snapshots cache
|
||||
- name: Save snapshots cache
|
||||
id: cache-upload
|
||||
if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: ./snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ github.event.after }}
|
||||
|
||||
- name: Flatten images to a folder
|
||||
if: ${{ needs.e2e.result == 'failure' }}
|
||||
run: |
|
||||
mkdir errors
|
||||
cd snapshots
|
||||
find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
|
||||
|
||||
- name: Upload Error snapshots
|
||||
if: ${{ needs.e2e.result == 'failure' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload-artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() && steps.cypress.conclusion == 'failure' }}
|
||||
with:
|
||||
name: error-snapshots
|
||||
retention-days: 10
|
||||
path: errors/
|
||||
|
||||
- name: Notify Users
|
||||
if: ${{ needs.e2e.result == 'failure' }}
|
||||
run: |
|
||||
echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"
|
||||
path: cypress/snapshots/**/__diff_output__/*
|
||||
|
4
.github/workflows/link-checker.yml
vendored
4
.github/workflows/link-checker.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
# lychee only uses the GITHUB_TOKEN to avoid rate-limiting
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Restore lychee cache
|
||||
uses: actions/cache@v3
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
- name: Link Checker
|
||||
uses: lycheeverse/lychee-action@v1.9.1
|
||||
uses: lycheeverse/lychee-action@v1.8.0
|
||||
with:
|
||||
args: >-
|
||||
--config .github/lychee.toml
|
||||
|
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@@ -20,13 +20,13 @@ jobs:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
23
.github/workflows/pr-labeler-config-validator.yml
vendored
Normal file
23
.github/workflows/pr-labeler-config-validator.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Validate PR Labeler Configuration
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/pr-labeler-config-validator.yml
|
||||
- .github/workflows/pr-labeler.yml
|
||||
- .github/pr-labeler.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/pr-labeler-config-validator.yml
|
||||
- .github/workflows/pr-labeler.yml
|
||||
- .github/pr-labeler.yml
|
||||
|
||||
jobs:
|
||||
pr-labeler:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Validate Configuration
|
||||
uses: Yash-Singh1/pr-labeler-config-validator@releases/v0.0.3
|
||||
with:
|
||||
configuration-path: .github/pr-labeler.yml
|
22
.github/workflows/pr-labeler.yml
vendored
22
.github/workflows/pr-labeler.yml
vendored
@@ -1,31 +1,13 @@
|
||||
name: Apply labels to PR
|
||||
on:
|
||||
pull_request_target:
|
||||
# required for pr-labeler to support PRs from forks
|
||||
# ===================== ⛔ ☢️ 🚫 ⚠️ Warning ⚠️ 🚫 ☢️ ⛔ =======================
|
||||
# Be very careful what you put in this GitHub Action workflow file to avoid
|
||||
# malicious PRs from getting access to the Mermaid-js repo.
|
||||
#
|
||||
# Please read the following first before reviewing/merging:
|
||||
# - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
# - https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
pr-labeler:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read # read permission is required to read config file
|
||||
pull-requests: write # write permission is required to label PRs
|
||||
steps:
|
||||
- name: Label PR
|
||||
uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
config-name: pr-labeler.yml
|
||||
disable-autolabeler: false
|
||||
disable-releaser: true
|
||||
uses: TimonVS/pr-labeler-action@v4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
4
.github/workflows/publish-docs.yml
vendored
4
.github/workflows/publish-docs.yml
vendored
@@ -23,12 +23,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: 18
|
||||
|
12
.github/workflows/release-draft.yml
vendored
12
.github/workflows/release-draft.yml
vendored
@@ -3,21 +3,13 @@ name: Draft Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
draft-release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # write permission is required to create a github release
|
||||
pull-requests: read # required to read PR titles/labels
|
||||
steps:
|
||||
- name: Draft Release
|
||||
uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
disable-autolabeler: true
|
||||
uses: toolmantim/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
@@ -9,14 +9,14 @@ jobs:
|
||||
publish-preview:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: 18.x
|
||||
|
4
.github/workflows/release-publish.yml
vendored
4
.github/workflows/release-publish.yml
vendored
@@ -8,14 +8,14 @@ jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: fregante/setup-git-user@v2
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js v18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: 18.x
|
||||
|
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -12,13 +12,13 @@ jobs:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
11
.github/workflows/update-browserlist.yml
vendored
11
.github/workflows/update-browserlist.yml
vendored
@@ -8,18 +8,11 @@ jobs:
|
||||
update-browser-list:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v2
|
||||
- run: npx update-browserslist-db@latest
|
||||
- uses: actions/checkout@v3
|
||||
- run: npx browserslist@latest --update-db
|
||||
- name: Commit changes
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
author_name: ${{ github.actor }}
|
||||
author_email: ${{ github.actor }}@users.noreply.github.com
|
||||
message: 'chore: update browsers list'
|
||||
push: false
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
branch: update-browserslist
|
||||
title: Update Browserslist
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -46,4 +46,7 @@ stats/
|
||||
|
||||
demos/dev/**
|
||||
!/demos/dev/example.html
|
||||
tsx-0/**
|
||||
!/demos/dev/reload.js
|
||||
|
||||
# autogenereated by langium-cli
|
||||
generated/
|
||||
|
@@ -6,6 +6,6 @@ export default {
|
||||
// https://prettier.io/docs/en/cli.html#--cache
|
||||
'prettier --write',
|
||||
],
|
||||
'cSpell.json': ['tsx scripts/fixCSpell.ts'],
|
||||
'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
|
||||
'**/*.jison': ['pnpm -w run lint:jison'],
|
||||
};
|
||||
|
1
.npmrc
1
.npmrc
@@ -1,3 +1,2 @@
|
||||
registry=https://registry.npmjs.org
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
|
@@ -10,6 +10,6 @@ stats
|
||||
.nyc_output
|
||||
# Autogenerated by `pnpm run --filter mermaid types:build-config`
|
||||
packages/mermaid/src/config.type.ts
|
||||
# Ignore the files creates in /demos/dev except for example.html
|
||||
demos/dev/**
|
||||
!/demos/dev/example.html
|
||||
|
||||
# autogenereated by langium-cli
|
||||
generated/
|
||||
|
@@ -3,11 +3,12 @@ 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';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
|
||||
const visualize = process.argv.includes('--visualize');
|
||||
const watch = process.argv.includes('--watch');
|
||||
@@ -36,24 +37,6 @@ const visualizerOptions = (packageName: string, core = false): PluginOption[] =>
|
||||
);
|
||||
};
|
||||
|
||||
const packageOptions = {
|
||||
mermaid: {
|
||||
name: 'mermaid',
|
||||
packageName: 'mermaid',
|
||||
file: 'mermaid.ts',
|
||||
},
|
||||
'mermaid-example-diagram': {
|
||||
name: 'mermaid-example-diagram',
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-zenuml': {
|
||||
name: 'mermaid-zenuml',
|
||||
packageName: 'mermaid-zenuml',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
};
|
||||
|
||||
interface BuildOptions {
|
||||
minify: boolean | 'esbuild';
|
||||
core?: boolean;
|
||||
@@ -72,34 +55,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: {
|
||||
@@ -117,9 +74,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
output,
|
||||
},
|
||||
},
|
||||
define: {
|
||||
'import.meta.vitest': 'undefined',
|
||||
},
|
||||
resolve: {
|
||||
extensions: [],
|
||||
},
|
||||
@@ -129,7 +83,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
// @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
|
||||
typescript({ compilerOptions: { declaration: false } }),
|
||||
istanbul({
|
||||
exclude: ['node_modules', 'test/', '__mocks__'],
|
||||
exclude: ['node_modules', 'test/', '__mocks__', 'generated'],
|
||||
extension: ['.js', '.ts'],
|
||||
requireEnv: true,
|
||||
forceBuildInstrument: coverage,
|
||||
@@ -149,24 +103,28 @@ 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 () => {
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
for (const pkg of packageNames.filter((pkg) => !mermaidOnly || pkg === 'mermaid')) {
|
||||
for (const pkg of packageNames.filter(
|
||||
(pkg) => !mermaidOnly || pkg === 'mermaid' || pkg === 'parser'
|
||||
)) {
|
||||
await buildPackage(pkg);
|
||||
}
|
||||
};
|
||||
|
||||
await generateLangium();
|
||||
|
||||
if (watch) {
|
||||
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
||||
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
||||
if (!mermaidOnly) {
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
||||
}
|
||||
} else if (visualize) {
|
||||
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
||||
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
|
||||
await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' }));
|
||||
} else {
|
||||
|
@@ -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,109 +1,5 @@
|
||||
import { load, JSON_SCHEMA } from 'js-yaml';
|
||||
import assert from 'node:assert';
|
||||
import Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
|
||||
import { PluginOption } from 'vite';
|
||||
|
||||
import type { MermaidConfig, BaseDiagramConfig } from '../packages/mermaid/src/config.type.js';
|
||||
|
||||
/**
|
||||
* All of the keys in the mermaid config that have a mermaid diagram config.
|
||||
*/
|
||||
const MERMAID_CONFIG_DIAGRAM_KEYS = [
|
||||
'flowchart',
|
||||
'sequence',
|
||||
'gantt',
|
||||
'journey',
|
||||
'class',
|
||||
'state',
|
||||
'er',
|
||||
'pie',
|
||||
'quadrantChart',
|
||||
'xyChart',
|
||||
'requirement',
|
||||
'mindmap',
|
||||
'timeline',
|
||||
'gitGraph',
|
||||
'c4',
|
||||
'sankey',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Generate default values from the JSON Schema.
|
||||
*
|
||||
* AJV does not support nested default values yet (or default values with $ref),
|
||||
* so we need to manually find them (this may be fixed in ajv v9).
|
||||
*
|
||||
* @param mermaidConfigSchema - The Mermaid JSON Schema to use.
|
||||
* @returns The default mermaid config object.
|
||||
*/
|
||||
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
|
||||
const ajv = new Ajv2019({
|
||||
useDefaults: true,
|
||||
allowUnionTypes: true,
|
||||
strict: true,
|
||||
});
|
||||
|
||||
ajv.addKeyword({
|
||||
keyword: 'meta:enum', // used by jsonschema2md
|
||||
errors: false,
|
||||
});
|
||||
ajv.addKeyword({
|
||||
keyword: 'tsType', // used by json-schema-to-typescript
|
||||
errors: false,
|
||||
});
|
||||
|
||||
// ajv currently doesn't support nested default values, see https://github.com/ajv-validator/ajv/issues/1718
|
||||
// (may be fixed in v9) so we need to manually use sub-schemas
|
||||
const mermaidDefaultConfig = {};
|
||||
|
||||
assert.ok(mermaidConfigSchema.$defs);
|
||||
const baseDiagramConfig = mermaidConfigSchema.$defs.BaseDiagramConfig;
|
||||
|
||||
for (const key of MERMAID_CONFIG_DIAGRAM_KEYS) {
|
||||
const subSchemaRef = mermaidConfigSchema.properties[key].$ref;
|
||||
const [root, defs, defName] = subSchemaRef.split('/');
|
||||
assert.strictEqual(root, '#');
|
||||
assert.strictEqual(defs, '$defs');
|
||||
const subSchema = {
|
||||
$schema: mermaidConfigSchema.$schema,
|
||||
$defs: mermaidConfigSchema.$defs,
|
||||
...mermaidConfigSchema.$defs[defName],
|
||||
} as JSONSchemaType<BaseDiagramConfig>;
|
||||
|
||||
const validate = ajv.compile(subSchema);
|
||||
|
||||
mermaidDefaultConfig[key] = {};
|
||||
|
||||
for (const required of subSchema.required ?? []) {
|
||||
if (subSchema.properties[required] === undefined && baseDiagramConfig.properties[required]) {
|
||||
mermaidDefaultConfig[key][required] = baseDiagramConfig.properties[required].default;
|
||||
}
|
||||
}
|
||||
if (!validate(mermaidDefaultConfig[key])) {
|
||||
throw new Error(
|
||||
`schema for subconfig ${key} does not have valid defaults! Errors were ${JSON.stringify(
|
||||
validate.errors,
|
||||
undefined,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const validate = ajv.compile(mermaidConfigSchema);
|
||||
|
||||
if (!validate(mermaidDefaultConfig)) {
|
||||
throw new Error(
|
||||
`Mermaid config JSON Schema does not have valid defaults! Errors were ${JSON.stringify(
|
||||
validate.errors,
|
||||
undefined,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return mermaidDefaultConfig;
|
||||
}
|
||||
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
|
||||
|
||||
/**
|
||||
* Vite plugin that handles JSON Schemas saved as a `.schema.yaml` file.
|
||||
@@ -120,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
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ async function createServer() {
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.static('./packages/parser/dist'));
|
||||
app.use(express.static('./packages/mermaid/dist'));
|
||||
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||
|
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@@ -18,8 +18,7 @@
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": ["scripts/docs.cli.mts"],
|
||||
// we'll need to change this to --import in Node.JS v20.6.0 and up
|
||||
"runtimeArgs": ["--loader", "tsx/esm"],
|
||||
"runtimeArgs": ["--loader", "ts-node/esm"],
|
||||
"cwd": "${workspaceRoot}/packages/mermaid",
|
||||
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
|
||||
"smartStep": true,
|
||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@@ -68,7 +68,7 @@ try {
|
||||
|
||||
### Init deprecated and InitThrowsErrors removed
|
||||
|
||||
The config passed to `init` was not being used earlier.
|
||||
The config passed to `init` was not being used eariler.
|
||||
It will now be used.
|
||||
The `init` function is deprecated and will be removed in the next major release.
|
||||
init currently works as a wrapper to `initialize` and `run`.
|
||||
@@ -195,7 +195,7 @@ mermaid.run({
|
||||
- "Cannot activate" in sequenceDiagram [\#647](https://github.com/knsv/mermaid/issues/647)
|
||||
- Link \("click" statement\) in flowchart does not work in exported SVG [\#646](https://github.com/knsv/mermaid/issues/646)
|
||||
- How to pass styling [\#639](https://github.com/knsv/mermaid/issues/639)
|
||||
- The live editor can't show seq diagram with notes for 8.0.0-alpha.3 [\#638](https://github.com/knsv/mermaid/issues/638)
|
||||
- The live editor cant show seq diagram with notes for 8.0.0-alpha.3 [\#638](https://github.com/knsv/mermaid/issues/638)
|
||||
- import mermaid.css with ES6 + NPM [\#634](https://github.com/knsv/mermaid/issues/634)
|
||||
- Actor line cuts through other elements [\#633](https://github.com/knsv/mermaid/issues/633)
|
||||
- Graph TD line out of the picture \(left side\) [\#630](https://github.com/knsv/mermaid/issues/630)
|
||||
@@ -504,7 +504,7 @@ mermaid.run({
|
||||
|
||||
- Docs css: code hard to read [\#324](https://github.com/knsv/mermaid/issues/324)
|
||||
- About Markpad integration [\#323](https://github.com/knsv/mermaid/issues/323)
|
||||
- How to link backwards in flowchat? [\#321](https://github.com/knsv/mermaid/issues/321)
|
||||
- How to link backwords in flowchat? [\#321](https://github.com/knsv/mermaid/issues/321)
|
||||
- Help with editor [\#310](https://github.com/knsv/mermaid/issues/310)
|
||||
- +1 [\#293](https://github.com/knsv/mermaid/issues/293)
|
||||
- Basic chart does not render on Chome, but does in Firefox [\#290](https://github.com/knsv/mermaid/issues/290)
|
||||
@@ -619,7 +619,7 @@ mermaid.run({
|
||||
- render to png from the cli does not display the marker-end arrow heads [\#181](https://github.com/knsv/mermaid/issues/181)
|
||||
- Links in sequence diagrams [\#159](https://github.com/knsv/mermaid/issues/159)
|
||||
- comment characters `%%` cause parse error [\#141](https://github.com/knsv/mermaid/issues/141)
|
||||
- Add a reversed asymmetric shape [\#124](https://github.com/knsv/mermaid/issues/124)
|
||||
- Add a reversed assymetric shape [\#124](https://github.com/knsv/mermaid/issues/124)
|
||||
- Add syntax for double headed arrows [\#123](https://github.com/knsv/mermaid/issues/123)
|
||||
- Support for font-awesome [\#49](https://github.com/knsv/mermaid/issues/49)
|
||||
|
||||
@@ -659,7 +659,7 @@ mermaid.run({
|
||||
- Auto linewrap for notes in sequence diagrams [\#178](https://github.com/knsv/mermaid/issues/178)
|
||||
- Execute code after initialize [\#176](https://github.com/knsv/mermaid/issues/176)
|
||||
- Autoscaling for all diagram types [\#175](https://github.com/knsv/mermaid/issues/175)
|
||||
- Problem with click event callback [\#174](https://github.com/knsv/mermaid/issues/174)
|
||||
- Problem wit click event callback [\#174](https://github.com/knsv/mermaid/issues/174)
|
||||
- How to escape characters? [\#170](https://github.com/knsv/mermaid/issues/170)
|
||||
- it can not work [\#167](https://github.com/knsv/mermaid/issues/167)
|
||||
- UML Class diagram [\#154](https://github.com/knsv/mermaid/issues/154)
|
||||
@@ -762,7 +762,7 @@ mermaid.run({
|
||||
- subgraph background is black in rendered flowchart PNG via CLI [\#121](https://github.com/knsv/mermaid/issues/121)
|
||||
- Integrate editor at https://github.com/naseer/mermaid-webapp [\#110](https://github.com/knsv/mermaid/issues/110)
|
||||
- Internet Explorer Support [\#99](https://github.com/knsv/mermaid/issues/99)
|
||||
- Asymmetric shapes not documented [\#82](https://github.com/knsv/mermaid/issues/82)
|
||||
- Assymetric shapes not documented [\#82](https://github.com/knsv/mermaid/issues/82)
|
||||
- NoModificationAllowedError [\#23](https://github.com/knsv/mermaid/issues/23)
|
||||
- Improve arrows [\#3](https://github.com/knsv/mermaid/issues/3)
|
||||
|
||||
@@ -908,7 +908,7 @@ mermaid.run({
|
||||
|
||||
- Question marks don't render properly with /dist/mermaid.full.min.js [\#30](https://github.com/knsv/mermaid/issues/30)
|
||||
- Error with some characters [\#25](https://github.com/knsv/mermaid/issues/25)
|
||||
- Provide parse function in browser without `require`? [\#21](https://github.com/knsv/mermaid/issues/21)
|
||||
- Provide parse function in browser widthout `require`? [\#21](https://github.com/knsv/mermaid/issues/21)
|
||||
- Better label text support [\#18](https://github.com/knsv/mermaid/issues/18)
|
||||
- Cap-cased words break parser [\#8](https://github.com/knsv/mermaid/issues/8)
|
||||
|
||||
|
@@ -59,8 +59,8 @@ representative at an online or offline event.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at <security@mermaid.live>.
|
||||
|
||||
reported to the community leaders responsible for enforcement at security@mermaid.live
|
||||
.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
@@ -1 +0,0 @@
|
||||
./packages/mermaid/src/docs/community/contributing.md
|
76
CONTRIBUTING.md
Normal file
76
CONTRIBUTING.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributing
|
||||
|
||||
Please read in detail about how to contribute documentation and code on the [Mermaid documentation site.](https://mermaid-js.github.io/mermaid/#/development)
|
||||
|
||||
---
|
||||
|
||||
# Mermaid contribution cheat-sheet
|
||||
|
||||
## Requirements
|
||||
|
||||
- [volta](https://volta.sh/) to manage node versions.
|
||||
- [Node.js](https://nodejs.org/en/). `volta install node`
|
||||
- [pnpm](https://pnpm.io/) package manager. `volta install pnpm`
|
||||
|
||||
## Development Installation
|
||||
|
||||
If you don't have direct access to push to mermaid repositories, make a fork first. Then clone. Or clone directly from mermaid-js:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:mermaid-js/mermaid.git
|
||||
cd mermaid
|
||||
```
|
||||
|
||||
Install required packages:
|
||||
|
||||
```bash
|
||||
# npx is required for first install as volta support for pnpm is not added yet.
|
||||
npx pnpm install
|
||||
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:
|
||||
|
||||
```bash
|
||||
./run install # npx pnpm install
|
||||
./run test # pnpm test
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run unit test
|
||||
pnpm test
|
||||
# Run unit test in watch mode
|
||||
pnpm test:watch
|
||||
# Run E2E test
|
||||
pnpm e2e
|
||||
# Debug E2E tests
|
||||
pnpm dev
|
||||
pnpm cypress:open # in another terminal
|
||||
```
|
||||
|
||||
## Branch name format:
|
||||
|
||||
```text
|
||||
[feature | bug | chore | docs]/[issue number]_[short description using dashes ('-') or underscores ('_') instead of spaces]
|
||||
```
|
||||
|
||||
eg: `feature/2945_state-diagram-new-arrow-florbs`, `bug/1123_fix_random_ugly_red_text`
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is necessary for all non bugfix/refactoring changes.
|
||||
|
||||
Only make changes to files that are in [`/packages/mermaid/src/docs`](packages/mermaid/src/docs)
|
||||
|
||||
**_DO NOT CHANGE FILES IN `/docs`_**
|
||||
|
||||
[Join our slack community if you want closer contact!](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
@@ -1,2 +0,0 @@
|
||||
FROM node:18.19.0-alpine3.18 AS base
|
||||
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh -
|
45
README.md
45
README.md
@@ -20,9 +20,6 @@ Generate diagrams from markdown-like text.
|
||||
<p align="center">
|
||||
<a href="./README.zh-CN.md">简体中文</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
Try Live Editor previews of future releases: <a href="https://develop.git.mermaid.live/" title="Try the mermaid version from the develop branch.">Develop</a> | <a href="https://next.git.mermaid.live/" title="Try the mermaid version from the next branch.">Next</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -34,7 +31,7 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
|
||||
[](https://www.jsdelivr.com/package/npm/mermaid)
|
||||
[](https://www.npmjs.com/package/mermaid)
|
||||
[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
|
||||
<img src="./img/header.png" alt="" />
|
||||
|
||||
@@ -44,22 +41,6 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
|
||||
|
||||
<a href="https://mermaid-js.github.io/mermaid/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a>
|
||||
|
||||
## Table of content
|
||||
|
||||
<details>
|
||||
<summary>Expand contents</summary>
|
||||
|
||||
- [About](#about)
|
||||
- [Examples](#examples)
|
||||
- [Release](#release)
|
||||
- [Related projects](#related-projects)
|
||||
- [Contributors](#contributors)
|
||||
- [Security and safe diagrams](#security-and-safe-diagrams)
|
||||
- [Reporting vulnerabilities](#reporting-vulnerabilities)
|
||||
- [Appreciation](#appreciation)
|
||||
|
||||
</details>
|
||||
|
||||
## About
|
||||
|
||||
<!-- <Main description> -->
|
||||
@@ -74,12 +55,12 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
|
||||
<br/>
|
||||
|
||||
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).<br/>
|
||||
For video tutorials, visit our [Tutorials](./docs/ecosystem/tutorials.md) page.
|
||||
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
|
||||
[Tutorials](./docs/config/Tutorials.md) has video tutorials.
|
||||
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
|
||||
|
||||
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations-community.md).
|
||||
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
|
||||
|
||||
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/ecosystem/tutorials.md).
|
||||
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/intro/getting-started.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/config/Tutorials.md).
|
||||
|
||||
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
|
||||
|
||||
@@ -184,7 +165,13 @@ class Class10 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
|
||||
namespace Namespace01 {
|
||||
class Class11
|
||||
class Class12 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```mermaid
|
||||
@@ -204,7 +191,13 @@ class Class10 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
|
||||
namespace Namespace01 {
|
||||
class Class11
|
||||
class Class12 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### State diagram [<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkEFvgzAMhf8K8nEqpYSNthx22Xbcqcexg0sCiZQQlDhIFeK_L8A6TfXp6fOz9ewJGssFVOAJSbwr7ByadGR1n8T6evpO0vQ1uZDSekOrXGFsPqJPO6q-2-imH8f_0TeHXm50lfelsAMjnEHFY6xpMdRAUhhRQxUlFy0GTTXU_RytYeAx-AdXZB1ULWovdoCB7OXWN1CRC-Ju-r3uz6UtchGHJqDbsPygU57iysb2reoWHpyOWBINvsqypb3vFMlw3TfWZF5xiY7keC6zkpUnZIUojwW-FAVvrvn51LLnvOXHQ84Q5nn-AVtLcwk">live editor</a>]
|
||||
|
@@ -12,7 +12,7 @@ Mermaid
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://mermaid.live/"><b>实时编辑器!</b></a>
|
||||
<a href="https://mermaid.live/"><b>Live Editor!</b></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://mermaid.js.org">📖 文档</a> | <a href="https://mermaid.js.org/intro/">🚀 入门</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE" title="Slack invite">🙌 加入我们</a>
|
||||
@@ -21,10 +21,6 @@ Mermaid
|
||||
<a href="./README.md">English</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
尝试未来版本的实时编辑器预览: <a href="https://develop.git.mermaid.live/" title="尝试来自develop分支的mermaid版本。">Develop</a> | <a href="https://next.git.mermaid.live/" title="尝试来自next分支的mermaid版本。">Next</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@@ -35,7 +31,7 @@ Mermaid
|
||||
[](https://www.jsdelivr.com/package/npm/mermaid)
|
||||
[](https://www.npmjs.com/package/mermaid)
|
||||
[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
|
||||
<img src="./img/header.png" alt="" />
|
||||
|
||||
@@ -57,9 +53,9 @@ Mermaid 是一个基于 Javascript 的图表绘制工具,通过解析类 Markd
|
||||
Mermaid 通过允许用户创建便于修改的图表来解决这一难题,它也可以作为生产脚本(或其他代码)的一部分。<br/>
|
||||
<br/>
|
||||
Mermaid 甚至能让非程序员也能通过 [Mermaid Live Editor](https://mermaid.live/) 轻松创建详细的图表。<br/>
|
||||
你可以访问 [教程](./docs/ecosystem/tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations-community.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
|
||||
你可以访问 [教程](./docs/config/Tutorials.md) 来查看 Live Editor 的视频教程,也可以查看 [Mermaid 的集成和使用](./docs/ecosystem/integrations.md) 这个清单来检查你的文档工具是否已经集成了 Mermaid 支持。
|
||||
|
||||
如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/ecosystem/tutorials.md).
|
||||
如果想要查看关于 Mermaid 更详细的介绍及基础使用方式,可以查看 [入门指引](./docs/intro/getting-started.md), [用法](./docs/config/usage.md) 和 [教程](./docs/config/Tutorials.md).
|
||||
|
||||
<!-- </Main description> -->
|
||||
|
||||
|
12
cSpell.json
12
cSpell.json
@@ -22,11 +22,9 @@
|
||||
"brkt",
|
||||
"brolin",
|
||||
"brotli",
|
||||
"catmull",
|
||||
"città",
|
||||
"classdef",
|
||||
"codedoc",
|
||||
"codemia",
|
||||
"colour",
|
||||
"commitlint",
|
||||
"cpettitt",
|
||||
@@ -40,10 +38,7 @@
|
||||
"docsy",
|
||||
"doku",
|
||||
"dompurify",
|
||||
"dont",
|
||||
"doublecircle",
|
||||
"edgechromium",
|
||||
"elems",
|
||||
"elkjs",
|
||||
"elle",
|
||||
"faber",
|
||||
@@ -62,6 +57,7 @@
|
||||
"gzipped",
|
||||
"huynh",
|
||||
"huynhicode",
|
||||
"iife",
|
||||
"inkdrop",
|
||||
"jaoude",
|
||||
"jgreywolf",
|
||||
@@ -75,6 +71,7 @@
|
||||
"knut",
|
||||
"knutsveidqvist",
|
||||
"laganeckas",
|
||||
"langium",
|
||||
"linetype",
|
||||
"lintstagedrc",
|
||||
"logmsg",
|
||||
@@ -86,6 +83,7 @@
|
||||
"mdbook",
|
||||
"mermaidjs",
|
||||
"mermerd",
|
||||
"metafile",
|
||||
"mindaugas",
|
||||
"mindmap",
|
||||
"mindmaps",
|
||||
@@ -99,10 +97,10 @@
|
||||
"nirname",
|
||||
"npmjs",
|
||||
"orlandoni",
|
||||
"outdir",
|
||||
"pathe",
|
||||
"pbrolin",
|
||||
"phpbb",
|
||||
"pixelmatch",
|
||||
"plantuml",
|
||||
"playfair",
|
||||
"pnpm",
|
||||
@@ -125,7 +123,6 @@
|
||||
"sidharth",
|
||||
"sidharthv",
|
||||
"sphinxcontrib",
|
||||
"ssim",
|
||||
"startx",
|
||||
"starty",
|
||||
"statediagram",
|
||||
@@ -159,7 +156,6 @@
|
||||
"vitepress",
|
||||
"vueuse",
|
||||
"xlink",
|
||||
"xychart",
|
||||
"yash",
|
||||
"yokozuna",
|
||||
"zenuml",
|
||||
|
@@ -6,18 +6,10 @@ const coverage = require('@cypress/code-coverage/task');
|
||||
|
||||
module.exports = defineConfig({
|
||||
projectId: 'n2sma2',
|
||||
viewportWidth: 1440,
|
||||
viewportHeight: 1024,
|
||||
e2e: {
|
||||
specPattern: 'cypress/integration/**/*.{js,ts}',
|
||||
specPattern: 'cypress/integration/**/*.{js,jsx,ts,tsx}',
|
||||
setupNodeEvents(on, config) {
|
||||
coverage(on, config);
|
||||
on('before:browser:launch', (browser = {}, launchOptions) => {
|
||||
if (browser.name === 'chrome' && browser.isHeadless) {
|
||||
launchOptions.args.push('--window-size=1440,1024', '--force-device-scale-factor=1');
|
||||
}
|
||||
return launchOptions;
|
||||
});
|
||||
addMatchImageSnapshotPlugin(on, config);
|
||||
// copy any needed variables from process.env to config.env
|
||||
config.env.useAppli = process.env.USE_APPLI ? true : false;
|
||||
|
@@ -10,7 +10,7 @@ interface CypressConfig {
|
||||
type CypressMermaidConfig = MermaidConfig & CypressConfig;
|
||||
|
||||
interface CodeObject {
|
||||
code: string | string[];
|
||||
code: string;
|
||||
mermaid: CypressMermaidConfig;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ const batchId: string =
|
||||
: Cypress.env('CYPRESS_COMMIT') || Date.now().toString());
|
||||
|
||||
export const mermaidUrl = (
|
||||
graphStr: string | string[],
|
||||
graphStr: string,
|
||||
options: CypressMermaidConfig,
|
||||
api: boolean
|
||||
): string => {
|
||||
@@ -52,21 +52,29 @@ export const imgSnapshotTest = (
|
||||
api = false,
|
||||
validation?: any
|
||||
): void => {
|
||||
const options: CypressMermaidConfig = {
|
||||
..._options,
|
||||
fontFamily: _options.fontFamily || 'courier',
|
||||
// @ts-ignore TODO: Fix type of fontSize
|
||||
fontSize: _options.fontSize || '16px',
|
||||
sequence: {
|
||||
...(_options.sequence || {}),
|
||||
actorFontFamily: 'courier',
|
||||
noteFontFamily:
|
||||
_options.sequence && _options.sequence.noteFontFamily
|
||||
? _options.sequence.noteFontFamily
|
||||
: 'courier',
|
||||
messageFontFamily: 'courier',
|
||||
},
|
||||
};
|
||||
cy.log(JSON.stringify(_options));
|
||||
const options: CypressMermaidConfig = Object.assign(_options);
|
||||
if (!options.fontFamily) {
|
||||
options.fontFamily = 'courier';
|
||||
}
|
||||
if (!options.sequence) {
|
||||
options.sequence = {};
|
||||
}
|
||||
if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) {
|
||||
options.sequence.actorFontFamily = 'courier';
|
||||
}
|
||||
if (options.sequence && !options.sequence.noteFontFamily) {
|
||||
options.sequence.noteFontFamily = 'courier';
|
||||
}
|
||||
options.sequence.actorFontFamily = 'courier';
|
||||
options.sequence.noteFontFamily = 'courier';
|
||||
options.sequence.messageFontFamily = 'courier';
|
||||
if (options.sequence && !options.sequence.actorFontFamily) {
|
||||
options.sequence.actorFontFamily = 'courier';
|
||||
}
|
||||
if (!options.fontSize) {
|
||||
options.fontSize = 16;
|
||||
}
|
||||
|
||||
const url: string = mermaidUrl(graphStr, options, api);
|
||||
openURLAndVerifyRendering(url, options, validation);
|
||||
@@ -74,15 +82,16 @@ export const imgSnapshotTest = (
|
||||
|
||||
export const urlSnapshotTest = (
|
||||
url: string,
|
||||
options: CypressMermaidConfig,
|
||||
_options: CypressMermaidConfig,
|
||||
_api = false,
|
||||
validation?: any
|
||||
): void => {
|
||||
const options: CypressMermaidConfig = Object.assign(_options);
|
||||
openURLAndVerifyRendering(url, options, validation);
|
||||
};
|
||||
|
||||
export const renderGraph = (
|
||||
graphStr: string | string[],
|
||||
graphStr: string,
|
||||
options: CypressMermaidConfig = {},
|
||||
api = false
|
||||
): void => {
|
||||
|
@@ -117,6 +117,7 @@ describe('Configuration', () => {
|
||||
});
|
||||
it('should not taint the initial configuration when using multiple directives', () => {
|
||||
const url = 'http://localhost:9000/regression/issue-1874.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
|
||||
cy.get('svg');
|
||||
|
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,12 +1,14 @@
|
||||
describe('Rerendering', () => {
|
||||
it('should be able to render after an error has occurred', () => {
|
||||
const url = 'http://localhost:9000/render-after-error.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('#graphDiv').should('exist');
|
||||
});
|
||||
|
||||
it('should be able to render and rerender a graph via API', () => {
|
||||
const url = 'http://localhost:9000/rerender.html';
|
||||
cy.viewport(1440, 1024);
|
||||
cy.visit(url);
|
||||
cy.get('#graph [id^=flowchart-A]').should('have.text', 'XMas');
|
||||
|
||||
|
@@ -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>');
|
||||
});
|
||||
});
|
@@ -132,9 +132,4 @@ describe('XSS', () => {
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
it('should sanitize backticks in class names properly', () => {
|
||||
cy.visit('http://localhost:9000/xss24.html');
|
||||
cy.wait(1000);
|
||||
cy.get('#the-malware').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
@@ -386,6 +386,30 @@ describe('Class diagram V2', () => {
|
||||
{ logLevel: 1, flowchart: { htmlLabels: false } }
|
||||
);
|
||||
});
|
||||
|
||||
it('18: should handle the direction statement with LR', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
|
||||
`,
|
||||
{ logLevel: 1, flowchart: { htmlLabels: false } }
|
||||
);
|
||||
});
|
||||
it('17a: should handle the direction statement with BT', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
@@ -433,31 +457,7 @@ describe('Class diagram V2', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('18a: should handle the direction statement with LR', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram
|
||||
direction LR
|
||||
class Student {
|
||||
-idCard : IdCard
|
||||
}
|
||||
class IdCard{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
class Bike{
|
||||
-id : int
|
||||
-name : string
|
||||
}
|
||||
Student "1" --o "1" IdCard : carries
|
||||
Student "1" --o "1" Bike : rides
|
||||
|
||||
`,
|
||||
{ logLevel: 1, flowchart: { htmlLabels: false } }
|
||||
);
|
||||
});
|
||||
|
||||
it('18b: should render a simple class diagram with notes', () => {
|
||||
it('18: should render a simple class diagram with notes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
@@ -562,23 +562,4 @@ class C13["With Città foreign language"]
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should render a simple class diagram with no members', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10
|
||||
`,
|
||||
{ logLevel: 1, flowchart: { htmlLabels: false } }
|
||||
);
|
||||
});
|
||||
it('should render a simple class diagram with style definition', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
classDiagram-v2
|
||||
class Class10
|
||||
style Class10 fill:#f9f,stroke:#333,stroke-width:4px
|
||||
`,
|
||||
{ logLevel: 1, flowchart: { htmlLabels: false } }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -501,16 +501,4 @@ describe('Class diagram', () => {
|
||||
B : -methods()
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle notes with anchor tag having target attribute', () => {
|
||||
renderGraph(
|
||||
`classDiagram
|
||||
class test { }
|
||||
note for test "<a href='https://mermaid.js.org/' target="_blank"><code>note about mermaid</code></a>"`
|
||||
);
|
||||
|
||||
cy.get('svg').then((svg) => {
|
||||
cy.get('a').should('have.attr', 'target', '_blank').should('have.attr', 'rel', 'noopener');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { imgSnapshotTest, urlSnapshotTest } from '../../helpers/util.ts';
|
||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Configuration and directives - nodes should be light blue', () => {
|
||||
it('No config - use default', () => {
|
||||
@@ -206,7 +206,8 @@ graph TD
|
||||
describe('when rendering several diagrams', () => {
|
||||
it('diagrams should not taint later diagrams', () => {
|
||||
const url = 'http://localhost:9000/theme-directives.html';
|
||||
urlSnapshotTest(url, {});
|
||||
cy.visit(url);
|
||||
cy.matchImageSnapshot('conf-and-directives.spec-when-rendering-several-diagrams-diagram-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
12
cypress/integration/rendering/debug.spec.js
Normal file
12
cypress/integration/rendering/debug.spec.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Flowchart', () => {
|
||||
it('34: testing the label width in percy', () => {
|
||||
imgSnapshotTest(
|
||||
`graph TD
|
||||
A[Christmas]
|
||||
`,
|
||||
{ theme: 'forest', fontFamily: '"Noto Sans SC", sans-serif' }
|
||||
);
|
||||
});
|
||||
});
|
@@ -729,37 +729,6 @@ A ~~~ B
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('5064: Should render when subgraph child has links to outside node and subgraph', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TB
|
||||
Out --> In
|
||||
subgraph Sub
|
||||
In
|
||||
end
|
||||
Sub --> In`
|
||||
);
|
||||
});
|
||||
|
||||
it('5059: Should render when subgraph contains only subgraphs, has link to outside and itself is part of a link', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart
|
||||
|
||||
subgraph Main
|
||||
subgraph Child1
|
||||
Node1
|
||||
Node2
|
||||
end
|
||||
subgraph Child2
|
||||
Node3
|
||||
Node4
|
||||
end
|
||||
end
|
||||
Main --> Out1
|
||||
Child2 --> Out2`
|
||||
);
|
||||
});
|
||||
|
||||
describe('Markdown strings flowchart (#4220)', () => {
|
||||
describe('html labels', () => {
|
||||
it('With styling and classes', () => {
|
||||
@@ -905,93 +874,4 @@ end
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Subgraph title margins', () => {
|
||||
it('Should render subgraphs with title margins set (LR)', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart LR
|
||||
|
||||
subgraph TOP
|
||||
direction TB
|
||||
subgraph B1
|
||||
direction RL
|
||||
i1 -->f1
|
||||
end
|
||||
subgraph B2
|
||||
direction BT
|
||||
i2 -->f2
|
||||
end
|
||||
end
|
||||
A --> TOP --> B
|
||||
B1 --> B2
|
||||
`,
|
||||
{ flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
|
||||
);
|
||||
});
|
||||
it('Should render subgraphs with title margins set (TD)', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart TD
|
||||
|
||||
subgraph TOP
|
||||
direction LR
|
||||
subgraph B1
|
||||
direction RL
|
||||
i1 -->f1
|
||||
end
|
||||
subgraph B2
|
||||
direction BT
|
||||
i2 -->f2
|
||||
end
|
||||
end
|
||||
A --> TOP --> B
|
||||
B1 --> B2
|
||||
`,
|
||||
{ flowchart: { subGraphTitleMargin: { top: 8, bottom: 16 } } }
|
||||
);
|
||||
});
|
||||
it('Should render subgraphs with title margins set (LR) and htmlLabels set to false', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart LR
|
||||
|
||||
subgraph TOP
|
||||
direction TB
|
||||
subgraph B1
|
||||
direction RL
|
||||
i1 -->f1
|
||||
end
|
||||
subgraph B2
|
||||
direction BT
|
||||
i2 -->f2
|
||||
end
|
||||
end
|
||||
A --> TOP --> B
|
||||
B1 --> B2
|
||||
`,
|
||||
{
|
||||
htmlLabels: false,
|
||||
flowchart: { htmlLabels: false, subGraphTitleMargin: { top: 10, bottom: 5 } },
|
||||
}
|
||||
);
|
||||
});
|
||||
it('Should render subgraphs with title margins and edge labels', () => {
|
||||
imgSnapshotTest(
|
||||
`flowchart LR
|
||||
|
||||
subgraph TOP
|
||||
direction TB
|
||||
subgraph B1
|
||||
direction RL
|
||||
i1 --lb1-->f1
|
||||
end
|
||||
subgraph B2
|
||||
direction BT
|
||||
i2 --lb2-->f2
|
||||
end
|
||||
end
|
||||
A --lb3--> TOP --lb4--> B
|
||||
B1 --lb5--> B2
|
||||
`,
|
||||
{ flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -245,10 +245,7 @@ describe('Gantt diagram', () => {
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(
|
||||
Cypress.config().viewportWidth * 0.95,
|
||||
Cypress.config().viewportWidth * 1.05
|
||||
);
|
||||
expect(maxWidthValue).to.be.within(984 * 0.95, 984 * 1.05);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -288,11 +285,11 @@ describe('Gantt diagram', () => {
|
||||
{ gantt: { useMaxWidth: false } }
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
// const height = parseFloat(svg.attr('height'));
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(width).to.be.within(
|
||||
Cypress.config().viewportWidth * 0.95,
|
||||
Cypress.config().viewportWidth * 1.05
|
||||
);
|
||||
// use within because the absolute value can be slightly different depending on the environment ±5%
|
||||
// expect(height).to.be.within(484 * 0.95, 484 * 1.05);
|
||||
expect(width).to.be.within(984 * 0.95, 984 * 1.05);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
@@ -333,48 +330,6 @@ describe('Gantt diagram', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 2 milliseconds', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat SSS
|
||||
axisFormat %Lms
|
||||
tickInterval 2millisecond
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 000, 6ms
|
||||
Another task : after a1, 6ms
|
||||
section Another
|
||||
Task in sec : a2, 006, 3ms
|
||||
another task : 3ms
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 2 seconds', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat ss
|
||||
axisFormat %Ss
|
||||
tickInterval 2second
|
||||
excludes weekends
|
||||
|
||||
section Section
|
||||
A task : a1, 00, 6s
|
||||
Another task : after a1, 6s
|
||||
section Another
|
||||
Task in sec : 06, 3s
|
||||
another task : 3s
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a gantt diagram with tick is 15 minutes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
@@ -523,32 +478,6 @@ describe('Gantt diagram', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: fix it
|
||||
//
|
||||
// This test is skipped deliberately
|
||||
// because it fails and blocks our development pipeline
|
||||
// It was added as an attempt to fix gantt performance issues
|
||||
//
|
||||
// https://github.com/mermaid-js/mermaid/issues/3274
|
||||
//
|
||||
it.skip('should render a gantt diagram with very large intervals, skipping excludes if interval > 5 years', () => {
|
||||
imgSnapshotTest(
|
||||
`gantt
|
||||
title A long Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %m-%d
|
||||
tickInterval 1day
|
||||
excludes weekends
|
||||
section Section
|
||||
A task : a1, 9999-10-01, 30d
|
||||
Another task : after a1, 20d
|
||||
section Another
|
||||
Task in sec : 2022-10-20, 12d
|
||||
another task : 24d
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render when compact is true', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
|
@@ -26,7 +26,7 @@ describe('Git Graph diagram', () => {
|
||||
`gitGraph
|
||||
commit id: "Normal Commit"
|
||||
commit id: "Reverse Commit" type: REVERSE
|
||||
commit id: "Highlight Commit" type: HIGHLIGHT
|
||||
commit id: "Hightlight Commit" type: HIGHLIGHT
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -36,7 +36,7 @@ describe('Git Graph diagram', () => {
|
||||
`gitGraph
|
||||
commit id: "Normal Commit with tag" tag: "v1.0.0"
|
||||
commit id: "Reverse Commit with tag" type: REVERSE tag: "RC_1"
|
||||
commit id: "Highlight Commit" type: HIGHLIGHT tag: "8.8.4"
|
||||
commit id: "Hightlight Commit" type: HIGHLIGHT tag: "8.8.4"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -102,7 +102,7 @@ describe('Git Graph diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('8: should render a simple gitgraph with more than 8 branches & overriding variables', () => {
|
||||
it('8: should render a simple gitgraph with more than 8 branchs & overriding variables', () => {
|
||||
imgSnapshotTest(
|
||||
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
|
||||
'gitBranchLabel0': '#ffffff',
|
||||
@@ -358,7 +358,7 @@ gitGraph
|
||||
`gitGraph TB:
|
||||
commit id: "Normal Commit"
|
||||
commit id: "Reverse Commit" type: REVERSE
|
||||
commit id: "Highlight Commit" type: HIGHLIGHT
|
||||
commit id: "Hightlight Commit" type: HIGHLIGHT
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -368,7 +368,7 @@ gitGraph
|
||||
`gitGraph TB:
|
||||
commit id: "Normal Commit with tag" tag: "v1.0.0"
|
||||
commit id: "Reverse Commit with tag" type: REVERSE tag: "RC_1"
|
||||
commit id: "Highlight Commit" type: HIGHLIGHT tag: "8.8.4"
|
||||
commit id: "Hightlight Commit" type: HIGHLIGHT tag: "8.8.4"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
@@ -434,7 +434,7 @@ gitGraph
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('22: should render a simple gitgraph with more than 8 branches & overriding variables | Vertical Branch', () => {
|
||||
it('22: should render a simple gitgraph with more than 8 branchs & overriding variables | Vertical Branch', () => {
|
||||
imgSnapshotTest(
|
||||
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
|
||||
'gitBranchLabel0': '#ffffff',
|
||||
@@ -701,246 +701,4 @@ gitGraph TB:
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('34: should render a simple gitgraph with two branches from same commit', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
branch feature-001
|
||||
commit id:"3-abcdefg"
|
||||
commit id:"4-abcdefg"
|
||||
checkout main
|
||||
branch feature-002
|
||||
commit id:"5-abcdefg"
|
||||
checkout feature-001
|
||||
merge feature-002
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('35: should render a simple gitgraph with two branches from same commit | Vertical Branch', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph TB:
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
branch feature-001
|
||||
commit id:"3-abcdefg"
|
||||
commit id:"4-abcdefg"
|
||||
checkout main
|
||||
branch feature-002
|
||||
commit id:"5-abcdefg"
|
||||
checkout feature-001
|
||||
merge feature-002
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('36: should render GitGraph with branch that is not used immediately', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph LR:
|
||||
commit id:"1-abcdefg"
|
||||
branch x
|
||||
checkout main
|
||||
commit id:"2-abcdefg"
|
||||
checkout x
|
||||
commit id:"3-abcdefg"
|
||||
checkout main
|
||||
merge x
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('37: should render GitGraph with branch that is not used immediately | Vertical Branch', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph TB:
|
||||
commit id:"1-abcdefg"
|
||||
branch x
|
||||
checkout main
|
||||
commit id:"2-abcdefg"
|
||||
checkout x
|
||||
commit id:"3-abcdefg"
|
||||
checkout main
|
||||
merge x
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('38: should render GitGraph with branch and sub-branch neither of which used immediately', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph LR:
|
||||
commit id:"1-abcdefg"
|
||||
branch x
|
||||
checkout main
|
||||
commit id:"2-abcdefg"
|
||||
checkout x
|
||||
commit id:"3-abcdefg"
|
||||
checkout main
|
||||
merge x
|
||||
checkout x
|
||||
branch y
|
||||
checkout x
|
||||
commit id:"4-abcdefg"
|
||||
checkout y
|
||||
commit id:"5-abcdefg"
|
||||
checkout x
|
||||
merge y
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('39: should render GitGraph with branch and sub-branch neither of which used immediately | Vertical Branch', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph TB:
|
||||
commit id:"1-abcdefg"
|
||||
branch x
|
||||
checkout main
|
||||
commit id:"2-abcdefg"
|
||||
checkout x
|
||||
commit id:"3-abcdefg"
|
||||
checkout main
|
||||
merge x
|
||||
checkout x
|
||||
branch y
|
||||
checkout x
|
||||
commit id:"4-abcdefg"
|
||||
checkout y
|
||||
commit id:"5-abcdefg"
|
||||
checkout x
|
||||
merge y
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('40: should render a simple gitgraph with cherry pick merge commit', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph
|
||||
commit id: "ZERO"
|
||||
branch feature
|
||||
branch release
|
||||
checkout feature
|
||||
commit id: "A"
|
||||
commit id: "B"
|
||||
checkout main
|
||||
merge feature id: "M"
|
||||
checkout release
|
||||
cherry-pick id: "M" parent:"B"`
|
||||
);
|
||||
});
|
||||
it('41: should render default GitGraph with parallelCommits set to false', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
branch develop
|
||||
commit id:"3-abcdefg"
|
||||
commit id:"4-abcdefg"
|
||||
checkout main
|
||||
branch feature
|
||||
commit id:"5-abcdefg"
|
||||
commit id:"6-abcdefg"
|
||||
checkout main
|
||||
commit id:"7-abcdefg"
|
||||
commit id:"8-abcdefg"
|
||||
`,
|
||||
{ gitGraph: { parallelCommits: false } }
|
||||
);
|
||||
});
|
||||
it('42: should render GitGraph with parallel commits', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
branch develop
|
||||
commit id:"3-abcdefg"
|
||||
commit id:"4-abcdefg"
|
||||
checkout main
|
||||
branch feature
|
||||
commit id:"5-abcdefg"
|
||||
commit id:"6-abcdefg"
|
||||
checkout main
|
||||
commit id:"7-abcdefg"
|
||||
commit id:"8-abcdefg"
|
||||
`,
|
||||
{ gitGraph: { parallelCommits: true } }
|
||||
);
|
||||
});
|
||||
it('43: should render GitGraph with parallel commits | Vertical Branch', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph TB:
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
branch develop
|
||||
commit id:"3-abcdefg"
|
||||
commit id:"4-abcdefg"
|
||||
checkout main
|
||||
branch feature
|
||||
commit id:"5-abcdefg"
|
||||
commit id:"6-abcdefg"
|
||||
checkout main
|
||||
commit id:"7-abcdefg"
|
||||
commit id:"8-abcdefg"
|
||||
`,
|
||||
{ gitGraph: { parallelCommits: true } }
|
||||
);
|
||||
});
|
||||
it('44: should render GitGraph with unconnected branches and no parallel commits', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph
|
||||
branch dev
|
||||
branch v2
|
||||
branch feat
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
checkout main
|
||||
commit id:"3-abcdefg"
|
||||
checkout dev
|
||||
commit id:"4-abcdefg"
|
||||
checkout v2
|
||||
commit id:"5-abcdefg"
|
||||
checkout main
|
||||
commit id:"6-abcdefg"
|
||||
`,
|
||||
{ gitGraph: { parallelCommits: false } }
|
||||
);
|
||||
});
|
||||
it('45: should render GitGraph with unconnected branches and parallel commits', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph
|
||||
branch dev
|
||||
branch v2
|
||||
branch feat
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
checkout main
|
||||
commit id:"3-abcdefg"
|
||||
checkout dev
|
||||
commit id:"4-abcdefg"
|
||||
checkout v2
|
||||
commit id:"5-abcdefg"
|
||||
checkout main
|
||||
commit id:"6-abcdefg"
|
||||
`,
|
||||
{ gitGraph: { parallelCommits: true } }
|
||||
);
|
||||
});
|
||||
it('46: should render GitGraph with unconnected branches and parallel commits | Vertical Branch', () => {
|
||||
imgSnapshotTest(
|
||||
`gitGraph TB:
|
||||
branch dev
|
||||
branch v2
|
||||
branch feat
|
||||
commit id:"1-abcdefg"
|
||||
commit id:"2-abcdefg"
|
||||
checkout main
|
||||
commit id:"3-abcdefg"
|
||||
checkout dev
|
||||
commit id:"4-abcdefg"
|
||||
checkout v2
|
||||
commit id:"5-abcdefg"
|
||||
checkout main
|
||||
commit id:"6-abcdefg"
|
||||
`,
|
||||
{ gitGraph: { parallelCommits: true } }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -1,10 +0,0 @@
|
||||
import { urlSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Marker Unique IDs Per Diagram', () => {
|
||||
it('should render a blue arrow tip in second digram', () => {
|
||||
urlSnapshotTest('http://localhost:9000/marker_unique_id.html', {
|
||||
logLevel: 1,
|
||||
flowchart: { htmlLabels: false },
|
||||
});
|
||||
});
|
||||
});
|
@@ -44,7 +44,7 @@ describe('pie chart', () => {
|
||||
const style = svg.attr('style');
|
||||
expect(style).to.match(/^max-width: [\d.]+px;$/);
|
||||
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
|
||||
expect(maxWidthValue).to.be.within(590, 600); // depends on installed fonts: 596.2 on my PC, 597.5 on CI
|
||||
expect(maxWidthValue).to.eq(984);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('pie chart', () => {
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
const width = parseFloat(svg.attr('width'));
|
||||
expect(width).to.be.within(590, 600); // depends on installed fonts: 596.2 on my PC, 597.5 on CI
|
||||
expect(width).to.eq(984);
|
||||
expect(svg).to.not.have.attr('style');
|
||||
});
|
||||
});
|
||||
|
@@ -160,70 +160,4 @@ describe('Quadrant Chart', () => {
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render x-axis labels in the center, if x-axis has two labels', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
quadrantChart
|
||||
title Reach and engagement of campaigns
|
||||
x-axis Low Reach --> High Reach
|
||||
y-axis Low Engagement
|
||||
quadrant-1 We should expand
|
||||
quadrant-2 Need to promote
|
||||
quadrant-3 Re-evaluate
|
||||
quadrant-4 May be improved
|
||||
Campaign A: [0.3, 0.6]
|
||||
Campaign B: [0.45, 0.23]
|
||||
Campaign C: [0.57, 0.69]
|
||||
Campaign D: [0.78, 0.34]
|
||||
Campaign E: [0.40, 0.34]
|
||||
Campaign F: [0.35, 0.78]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render y-axis labels in the center, if y-axis has two labels', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
quadrantChart
|
||||
title Reach and engagement of campaigns
|
||||
x-axis Low Reach
|
||||
y-axis Low Engagement --> High Engagement
|
||||
quadrant-1 We should expand
|
||||
quadrant-2 Need to promote
|
||||
quadrant-3 Re-evaluate
|
||||
quadrant-4 May be improved
|
||||
Campaign A: [0.3, 0.6]
|
||||
Campaign B: [0.45, 0.23]
|
||||
Campaign C: [0.57, 0.69]
|
||||
Campaign D: [0.78, 0.34]
|
||||
Campaign E: [0.40, 0.34]
|
||||
Campaign F: [0.35, 0.78]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('should render both axes labels on the left and bottom, if both axes have only one label', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
quadrantChart
|
||||
title Reach and engagement of campaigns
|
||||
x-axis Reach -->
|
||||
y-axis Engagement -->
|
||||
quadrant-1 We should expand
|
||||
quadrant-2 Need to promote
|
||||
quadrant-3 Re-evaluate
|
||||
quadrant-4 May be improved
|
||||
Campaign A: [0.3, 0.6]
|
||||
Campaign B: [0.45, 0.23]
|
||||
Campaign C: [0.57, 0.69]
|
||||
Campaign D: [0.78, 0.34]
|
||||
Campaign E: [0.40, 0.34]
|
||||
Campaign F: [0.35, 0.78]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
});
|
||||
|
@@ -930,36 +930,4 @@ context('Sequence diagram', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
context('render after error', () => {
|
||||
it('should render diagram after fixing destroy participant error', () => {
|
||||
cy.on('uncaught:exception', (err) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
renderGraph([
|
||||
`sequenceDiagram
|
||||
Alice->>Bob: Hello Bob, how are you ?
|
||||
Bob->>Alice: Fine, thank you. And you?
|
||||
create participant Carl
|
||||
Alice->>Carl: Hi Carl!
|
||||
create actor D as Donald
|
||||
Carl->>D: Hi!
|
||||
destroy Carl
|
||||
Alice-xCarl: We are too many
|
||||
destroy Bo
|
||||
Bob->>Alice: I agree`,
|
||||
`sequenceDiagram
|
||||
Alice->>Bob: Hello Bob, how are you ?
|
||||
Bob->>Alice: Fine, thank you. And you?
|
||||
create participant Carl
|
||||
Alice->>Carl: Hi Carl!
|
||||
create actor D as Donald
|
||||
Carl->>D: Hi!
|
||||
destroy Carl
|
||||
Alice-xCarl: We are too many
|
||||
destroy Bob
|
||||
Bob->>Alice: I agree`,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -57,7 +57,7 @@ describe('Timeline diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('5: should render a simple timeline with directive overridden colors', () => {
|
||||
it('5: should render a simple timeline with directive overriden colors', () => {
|
||||
imgSnapshotTest(
|
||||
` %%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
|
||||
'cScale0': '#ff0000',
|
||||
|
@@ -1,322 +0,0 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
|
||||
|
||||
describe('XY Chart', () => {
|
||||
it('should render the simplest possible chart', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
line [10, 30, 20]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Should render a complete chart', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('Should render a chart without title', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('y-axis title not required', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Should render a chart without y-axis with different range', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000]
|
||||
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('x axis title not required', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000]
|
||||
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Multiple plots can be rendered', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
line [23, 46, 77, 34]
|
||||
line [45, 32, 33, 12]
|
||||
bar [87, 54, 99, 85]
|
||||
line [78, 88, 22, 4]
|
||||
line [22, 29, 75, 33]
|
||||
bar [52, 96, 35, 10]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Decimals and negative numbers are supported', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
xychart-beta
|
||||
y-axis -2.4 --> 3.5
|
||||
line [+1.3, .6, 2.4, -.34]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render spark line with "plotReservedSpacePercent"', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: dark
|
||||
xyChart:
|
||||
width: 200
|
||||
height: 20
|
||||
plotReservedSpacePercent: 100
|
||||
---
|
||||
xychart-beta
|
||||
line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render spark bar without displaying other property', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: dark
|
||||
xyChart:
|
||||
width: 200
|
||||
height: 20
|
||||
xAxis:
|
||||
showLabel: false
|
||||
showTitle: false
|
||||
showTick: false
|
||||
showAxisLine: false
|
||||
yAxis:
|
||||
showLabel: false
|
||||
showTitle: false
|
||||
showTick: false
|
||||
showAxisLine: false
|
||||
---
|
||||
xychart-beta
|
||||
bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Should use all the config from directive', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
%%{init: {"xyChart": {"width": 1000, "height": 600, "titlePadding": 5, "titleFontSize": 10, "xAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "yAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "plotBorderWidth": 5, "chartOrientation": "horizontal", "plotReservedSpacePercent": 60 }}}%%
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Should use all the config from yaml', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
theme: forest
|
||||
xyChart:
|
||||
width: 1000
|
||||
height: 600
|
||||
titlePadding: 5
|
||||
titleFontSize: 10
|
||||
xAxis:
|
||||
labelFontSize: 20
|
||||
labelPadding: 10
|
||||
titleFontSize: 30
|
||||
titlePadding: 20
|
||||
tickLength: 10
|
||||
tickWidth: 5
|
||||
axisLineWidth: 5
|
||||
yAxis:
|
||||
labelFontSize: 20
|
||||
labelPadding: 10
|
||||
titleFontSize: 30
|
||||
titlePadding: 20
|
||||
tickLength: 10
|
||||
tickWidth: 5
|
||||
axisLineWidth: 5
|
||||
chartOrientation: horizontal
|
||||
plotReservedSpacePercent: 60
|
||||
---
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render with show axis title false', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
xyChart:
|
||||
xAxis:
|
||||
showTitle: false
|
||||
yAxis:
|
||||
showTitle: false
|
||||
---
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render with show axis label false', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
xyChart:
|
||||
xAxis:
|
||||
showLabel: false
|
||||
yAxis:
|
||||
showLabel: false
|
||||
---
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render with show axis tick false', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
xyChart:
|
||||
xAxis:
|
||||
showTick: false
|
||||
yAxis:
|
||||
showTick: false
|
||||
---
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render with show axis line false', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
xyChart:
|
||||
xAxis:
|
||||
showAxisLine: false
|
||||
yAxis:
|
||||
showAxisLine: false
|
||||
---
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('Render all the theme color', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
---
|
||||
config:
|
||||
themeVariables:
|
||||
xyChart:
|
||||
titleColor: "#ff0000"
|
||||
backgroundColor: "#f0f8ff"
|
||||
yAxisLabelColor: "#ee82ee"
|
||||
yAxisTitleColor: "#7fffd4"
|
||||
yAxisTickColor: "#87ceeb"
|
||||
yAxisLineColor: "#ff6347"
|
||||
xAxisLabelColor: "#7fffd4"
|
||||
xAxisTitleColor: "#ee82ee"
|
||||
xAxisTickColor: "#ff6347"
|
||||
xAxisLineColor: "#87ceeb"
|
||||
plotColorPalette: "#008000, #faba63"
|
||||
---
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
});
|
@@ -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]);
|
||||
|
File diff suppressed because one or more lines are too long
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>
|
@@ -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);
|
||||
}
|
||||
|
@@ -58,21 +58,12 @@
|
||||
</head>
|
||||
<body>
|
||||
<pre id="diagram" class="mermaid">
|
||||
flowchart TB
|
||||
C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q & R & S & T & U & V & W & X & Y & Z & A1 & A2 & A3 & A4 & A5 & A6 & A7 & A8
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------->
|
||||
C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q & R & S & T & U & V & W & X & Y & Z & A1 & A2 & A3 & A4 & A5 & A6 & A7 & A8
|
||||
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
flowchart TB
|
||||
A & A & A & A & A & A & A & A ---> C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q & R & S & T & U & V & W & X & Y & Z
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
flowchart TB
|
||||
A1 & A2 & A3 & A4 & A5 & A6 & A7 & A8 --> C & D & E & F & G & H & I & J & K & L & M & N & O & P & Q & R & S & T & U & V & W & X & Y & Z
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid2">
|
||||
flowchart
|
||||
classDef mainCategories fill:#f9d5e5, stroke:#233d4d,stroke-width:2px, font-weight:bold;
|
||||
CS(Customer Awareness Journey):::mainCategories
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram" class="mermaid">
|
||||
flowchart
|
||||
Node1:::class1 --> Node2:::class2
|
||||
Node1:::class1 --> Node3:::class2
|
||||
@@ -450,7 +441,6 @@ mindmap
|
||||
messageFontFamily: 'courier',
|
||||
},
|
||||
fontSize: 16,
|
||||
logLevel: 0,
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
|
@@ -1,53 +0,0 @@
|
||||
<html>
|
||||
<head> </head>
|
||||
<body>
|
||||
<h1>Example</h1>
|
||||
<pre class="mermaid">
|
||||
%%{init:{"theme":"base", "themeVariables": {"lineColor":"red"}}}%%
|
||||
flowchart LR
|
||||
subgraph red
|
||||
A --> B
|
||||
end
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
%%{init:{"theme":"base", "themeVariables": {"lineColor":"blue"}}}%%
|
||||
flowchart LR
|
||||
subgraph black
|
||||
A --> B
|
||||
end
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
lineColor: yellow
|
||||
---
|
||||
flowchart LR
|
||||
subgraph red
|
||||
A --> B
|
||||
end
|
||||
</pre>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
config:
|
||||
theme: base
|
||||
themeVariables:
|
||||
lineColor: green
|
||||
---
|
||||
flowchart LR
|
||||
subgraph black
|
||||
A --> B
|
||||
end
|
||||
</pre>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({ startOnLoad: false, logLevel: 0 });
|
||||
await mermaid.run();
|
||||
|
||||
if (window.Cypress) {
|
||||
window.rendered = true;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -1,6 +1,15 @@
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
|
||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
@@ -113,21 +122,26 @@
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
// theme: 'dark',
|
||||
// theme: 'dark',
|
||||
// arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 0,
|
||||
// flowchart: { useMaxWidth: true },
|
||||
graph: { curve: 'cardinal', htmlLabels: false },
|
||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorMargin: 50, showSequenceNumbers: true },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
fontFamily: '"arial", sans-serif',
|
||||
curve: 'cardinal',
|
||||
securityLevel: 'strict',
|
||||
startOnLoad: false,
|
||||
});
|
||||
|
||||
await mermaid.run();
|
||||
|
||||
if (window.Cypress) {
|
||||
window.rendered = true;
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -1,109 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
|
||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
/* background:#333; */
|
||||
font-family: 'Arial';
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
h1 {
|
||||
color: grey;
|
||||
}
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
.mermaid svg {
|
||||
/* font-size: 18px !important; */
|
||||
}
|
||||
.malware {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 150px;
|
||||
background: red;
|
||||
color: black;
|
||||
display: flex;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: monospace;
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
<div class="flex">
|
||||
<div id="diagram" class="mermaid"></div>
|
||||
<div id="res" class=""></div>
|
||||
</div>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
mermaid.initialize({
|
||||
theme: 'forest',
|
||||
arrowMarkerAbsolute: true,
|
||||
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
|
||||
logLevel: 0,
|
||||
state: {
|
||||
defaultRenderer: 'dagre-wrapper',
|
||||
},
|
||||
flowchart: {
|
||||
// defaultRenderer: 'dagre-wrapper',
|
||||
nodeSpacing: 10,
|
||||
curve: 'cardinal',
|
||||
htmlLabels: true,
|
||||
},
|
||||
htmlLabels: false,
|
||||
// gantt: { axisFormat: '%m/%d/%Y' },
|
||||
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
|
||||
// sequenceDiagram: { actorMargin: 300 } // deprecated
|
||||
// fontFamily: '"times", sans-serif',
|
||||
// fontFamily: 'courier',
|
||||
fontSize: 18,
|
||||
curve: 'basis',
|
||||
securityLevel: 'strict',
|
||||
startOnLoad: false,
|
||||
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
|
||||
// themeVariables: {relationLabelColor: 'red'}
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
|
||||
let diagram = 'classDiagram\n';
|
||||
diagram += '`Class<img src=x on';
|
||||
diagram += 'error=xssAttack()>` <|-- `Class2<img src=x on';
|
||||
diagram += 'error=xssAttack()>`';
|
||||
|
||||
console.log(diagram);
|
||||
// document.querySelector('#diagram').innerHTML = diagram;
|
||||
const { svg } = await mermaid.render('diagram', diagram);
|
||||
document.querySelector('#res').innerHTML = svg;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
@@ -94,6 +84,14 @@
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
let diagram = 'graph LR\n';
|
||||
diagram += 'B-->D("<img onerror=location=`java';
|
||||
// diagram += "script\u003aalert\u0028document.domain\u0029\` src=x>\"\);\n";
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
@@ -42,16 +42,6 @@
|
||||
font-size: 72px;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function xssAttack() {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'the-malware';
|
||||
div.className = 'malware';
|
||||
div.innerHTML = 'XSS Succeeded';
|
||||
document.getElementsByTagName('body')[0].appendChild(div);
|
||||
throw new Error('XSS Succeeded');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user