Compare commits

..

1 Commits

Author SHA1 Message Date
Steph
dc2373e94c add light syntax highlighting theme 2023-10-29 20:19:15 -07:00
159 changed files with 1006 additions and 2720 deletions

View File

@@ -1,30 +0,0 @@
/**
* 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',
},
'mermaid-flowchart-elk': {
name: 'mermaid-flowchart-elk',
packageName: 'mermaid-flowchart-elk',
file: 'detector.ts',
},
} as const;

View File

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

View File

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

View File

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

View File

@@ -1,65 +0,0 @@
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();

View File

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

View File

@@ -1,35 +0,0 @@
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;

View File

@@ -1,116 +0,0 @@
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';
import { packageOptions } from '../.build/common.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);
for (const { packageName } of Object.values(packageOptions)) {
app.use(express.static(`./packages/${packageName}/dist`));
}
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));
app.listen(9000, () => {
console.log(`Listening on http://localhost:9000`);
});
}
createServer();

View File

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

View File

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

View File

@@ -1,22 +1,4 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/release-drafter/release-drafter/master/schema.json 'Type: Bug / Error': ['bug/*', fix/*]
autolabeler: 'Type: Enhancement': ['feature/*', 'feat/*']
- label: 'Type: Bug / Error' 'Type: Other': ['other/*', 'chore/*', 'test/*', 'refactor/*']
branch: 'Area: Documentation': ['docs/*']
- '/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.

View File

@@ -25,6 +25,8 @@ categories:
change-template: '- $TITLE (#$NUMBER) @$AUTHOR' change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
sort-by: title sort-by: title
sort-direction: ascending sort-direction: ascending
branches:
- develop
exclude-labels: exclude-labels:
- 'Skip changelog' - 'Skip changelog'
no-changes-template: 'This release contains minor changes and bugfixes.' no-changes-template: 'This release contains minor changes and bugfixes.'

View File

@@ -16,12 +16,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: 18 node-version: 18
@@ -29,7 +29,7 @@ jobs:
- name: Install Packages - name: Install Packages
run: pnpm install --frozen-lockfile 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')) }} if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release')) }}
run: pnpm --filter mermaid run docs:verify-version run: pnpm --filter mermaid run docs:verify-version

View File

@@ -19,13 +19,13 @@ jobs:
matrix: matrix:
node-version: [18.x] node-version: [18.x]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Check for difference in README.md and docs/README.md - name: Check for difference in README.md and docs/README.md
run: | run: |

View File

@@ -15,7 +15,7 @@ jobs:
name: check tests name: check tests
if: github.repository_owner == 'mermaid-js' if: github.repository_owner == 'mermaid-js'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: testomatio/check-tests@stable - uses: testomatio/check-tests@stable

View File

@@ -29,7 +29,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

View File

@@ -1,6 +1,6 @@
# Dependency Review Action # 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 # 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 # 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 runs-on: ubuntu-latest
steps: steps:
- name: 'Checkout Repository' - name: 'Checkout Repository'
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: 'Dependency Review' - name: 'Dependency Review'
uses: actions/dependency-review-action@v3 uses: actions/dependency-review-action@v3

View File

@@ -30,13 +30,13 @@ jobs:
run: | run: |
echo "::error,title=Not using Applitols::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: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View File

@@ -17,13 +17,13 @@ jobs:
node-version: [18.x] node-version: [18.x]
containers: [1, 2, 3, 4] containers: [1, 2, 3, 4]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View File

@@ -26,7 +26,7 @@ jobs:
# lychee only uses the GITHUB_TOKEN to avoid rate-limiting # lychee only uses the GITHUB_TOKEN to avoid rate-limiting
contents: read contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: Restore lychee cache - name: Restore lychee cache
uses: actions/cache@v3 uses: actions/cache@v3

View File

@@ -20,13 +20,13 @@ jobs:
matrix: matrix:
node-version: [18.x] node-version: [18.x]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View 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

View File

@@ -1,31 +1,13 @@
name: Apply labels to PR name: Apply labels to PR
on: on:
pull_request_target: pull_request_target:
# required for pr-labeler to support PRs from forks types: [opened]
# ===================== ⛔ ☢️ 🚫 ⚠️ 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
jobs: jobs:
pr-labeler: pr-labeler:
runs-on: ubuntu-latest 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: steps:
- name: Label PR - name: Label PR
uses: release-drafter/release-drafter@v5 uses: TimonVS/pr-labeler-action@v4
with:
config-name: pr-labeler.yml
disable-autolabeler: false
disable-releaser: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -23,12 +23,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: 18 node-version: 18

View File

@@ -5,19 +5,11 @@ on:
branches: branches:
- develop - develop
permissions:
contents: read
jobs: jobs:
draft-release: draft-release:
runs-on: ubuntu-latest 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: steps:
- name: Draft Release - name: Draft Release
uses: release-drafter/release-drafter@v5 uses: toolmantim/release-drafter@v5
with:
disable-autolabeler: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -9,14 +9,14 @@ jobs:
publish-preview: publish-preview:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: 18.x node-version: 18.x

View File

@@ -8,14 +8,14 @@ jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: fregante/setup-git-user@v2 - uses: fregante/setup-git-user@v2
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json
- name: Setup Node.js v18 - name: Setup Node.js v18
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: 18.x node-version: 18.x

View File

@@ -12,13 +12,13 @@ jobs:
matrix: matrix:
node-version: [18.x] node-version: [18.x]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: pnpm/action-setup@v2 - uses: pnpm/action-setup@v2
# uses version from "packageManager" field in package.json # uses version from "packageManager" field in package.json
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4 uses: actions/setup-node@v3
with: with:
cache: pnpm cache: pnpm
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}

View File

@@ -8,7 +8,7 @@ jobs:
update-browser-list: update-browser-list:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- run: npx browserslist@latest --update-db - run: npx browserslist@latest --update-db
- name: Commit changes - name: Commit changes
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v9

4
.gitignore vendored
View File

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

View File

@@ -10,6 +10,3 @@ stats
.nyc_output .nyc_output
# Autogenerated by `pnpm run --filter mermaid types:build-config` # Autogenerated by `pnpm run --filter mermaid types:build-config`
packages/mermaid/src/config.type.ts packages/mermaid/src/config.type.ts
# autogenereated by langium-cli
generated/

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,22 +44,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> <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 ## About
<!-- <Main description> --> <!-- <Main description> -->
@@ -74,7 +58,7 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
<br/> <br/>
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).<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/config/Tutorials.md) page. [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-community.md). Use Mermaid with your 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-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-community.md).

View File

@@ -61,7 +61,6 @@
"gzipped", "gzipped",
"huynh", "huynh",
"huynhicode", "huynhicode",
"iife",
"inkdrop", "inkdrop",
"jaoude", "jaoude",
"jgreywolf", "jgreywolf",
@@ -75,7 +74,6 @@
"knut", "knut",
"knutsveidqvist", "knutsveidqvist",
"laganeckas", "laganeckas",
"langium",
"linetype", "linetype",
"lintstagedrc", "lintstagedrc",
"logmsg", "logmsg",
@@ -87,7 +85,6 @@
"mdbook", "mdbook",
"mermaidjs", "mermaidjs",
"mermerd", "mermerd",
"metafile",
"mindaugas", "mindaugas",
"mindmap", "mindmap",
"mindmaps", "mindmaps",
@@ -101,7 +98,6 @@
"nirname", "nirname",
"npmjs", "npmjs",
"orlandoni", "orlandoni",
"outdir",
"pathe", "pathe",
"pbrolin", "pbrolin",
"phpbb", "phpbb",

View File

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

View File

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

View File

@@ -501,16 +501,4 @@ describe('Class diagram', () => {
B : -methods() 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');
});
});
}); });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mermaid Flowchart ELK Test Page</title>
</head>
<body>
<h1>Flowchart ELK</h1>
<pre class="mermaid">
flowchart-elk TD
A([Start]) ==> B[Step 1]
B ==> C{Flow 1}
C -- Choice 1.1 --> D[Step 2.1]
C -- Choice 1.3 --> I[Step 2.3]
C == Choice 1.2 ==> E[Step 2.2]
D --> F{Flow 2}
E ==> F{Flow 2}
F{Flow 2} == Choice 2.1 ==> H[Feedback node]
H[Feedback node] ==> B[Step 1]
F{Flow 2} == Choice 2.2 ==> G((Finish))
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import flowchartELK from './mermaid-flowchart-elk.esm.mjs';
await mermaid.registerExternalDiagrams([flowchartELK]);
mermaid.initialize({
logLevel: 3,
});
</script>
</body>
</html>

View File

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

View File

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

View File

@@ -10,8 +10,9 @@
## First search to see if someone has already asked (and hopefully been answered) or suggested the same thing. ## First search to see if someone has already asked (and hopefully been answered) or suggested the same thing.
- [Search in Discussions](https://github.com/orgs/mermaid-js/discussions) - Search in Discussions
- [Search in Issues (Open & Closed)](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue) - Search in open Issues
- Search in closed Issues
If you find an open issue or discussion thread that is similar to your question but isn't answered, you can let us know that you are also interested in it. If you find an open issue or discussion thread that is similar to your question but isn't answered, you can let us know that you are also interested in it.
Use the GitHub reactions to add a thumbs-up to the issue or discussion thread. Use the GitHub reactions to add a thumbs-up to the issue or discussion thread.

View File

@@ -10,8 +10,8 @@ When mermaid starts, configuration is extracted to determine a configuration to
- The default configuration - The default configuration
- Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**. - Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**.
- Frontmatter (v10.5.0+) - diagram authors can update selected configuration parameters in the frontmatter of the diagram. These are applied to the render config. - Frontmatter (v10.5.0+) - diagram authors can update select configuration parameters in the frontmatter of the diagram. These are applied to the render config.
- Directives (Deprecated by Frontmatter) - diagram authors can update selected configuration parameters directly in the diagram code via directives. These are applied to the render config. - Directives (Deprecated by Frontmatter) - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config.
**The render config** is configuration that is used when rendering by applying these configurations. **The render config** is configuration that is used when rendering by applying these configurations.

View File

@@ -16,4 +16,4 @@
#### Defined in #### Defined in
[mermaidAPI.ts:60](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L60) [mermaidAPI.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L59)

View File

@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
#### Defined in #### Defined in
[mermaidAPI.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L80) [mermaidAPI.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L79)
--- ---
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
#### Defined in #### Defined in
[mermaidAPI.ts:70](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L70) [mermaidAPI.ts:69](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L69)

View File

@@ -25,7 +25,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
#### Defined in #### Defined in
[mermaidAPI.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L64) [mermaidAPI.ts:63](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L63)
## Variables ## Variables
@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:603](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L603) [mermaidAPI.ts:641](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L641)
## Functions ## Functions
@@ -127,7 +127,7 @@ Return the last node appended
#### Defined in #### Defined in
[mermaidAPI.ts:263](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L263) [mermaidAPI.ts:299](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L299)
--- ---
@@ -153,7 +153,7 @@ the cleaned up svgCode
#### Defined in #### Defined in
[mermaidAPI.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L209) [mermaidAPI.ts:245](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L245)
--- ---
@@ -178,7 +178,7 @@ the string with all the user styles
#### Defined in #### Defined in
[mermaidAPI.ts:139](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L139) [mermaidAPI.ts:175](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L175)
--- ---
@@ -201,7 +201,7 @@ the string with all the user styles
#### Defined in #### Defined in
[mermaidAPI.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L186) [mermaidAPI.ts:222](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L222)
--- ---
@@ -228,7 +228,47 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in #### Defined in
[mermaidAPI.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L124) [mermaidAPI.ts:160](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L160)
---
### decodeEntities
**decodeEntities**(`text`): `string`
#### Parameters
| Name | Type | Description |
| :----- | :------- | :----------------- |
| `text` | `string` | text to be decoded |
#### Returns
`string`
#### Defined in
[mermaidAPI.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L146)
---
### encodeEntities
**encodeEntities**(`text`): `string`
#### Parameters
| Name | Type | Description |
| :----- | :------- | :----------------- |
| `text` | `string` | text to be encoded |
#### Returns
`string`
#### Defined in
[mermaidAPI.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L117)
--- ---
@@ -254,7 +294,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in #### Defined in
[mermaidAPI.ts:240](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L240) [mermaidAPI.ts:276](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L276)
--- ---
@@ -279,4 +319,4 @@ Remove any existing elements from the given document
#### Defined in #### Defined in
[mermaidAPI.ts:313](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L313) [mermaidAPI.ts:349](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L349)

View File

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

View File

@@ -35,19 +35,12 @@ Below are a list of community plugins and integrations created with Mermaid.
- [Notion](https://notion.so) ✅ - [Notion](https://notion.so) ✅
- [Observable](https://observablehq.com/@observablehq/mermaid) ✅ - [Observable](https://observablehq.com/@observablehq/mermaid) ✅
- [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) ✅ - [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) ✅
- [NotesHub](https://noteshub.app) ✅
- [GitBook](https://gitbook.com) - [GitBook](https://gitbook.com)
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid) - [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
- [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli) - [Markdown with Mermaid CLI](https://github.com/miao1007/gitbook-plugin-mermaid-cli)
- [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf) - [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf)
- [LiveBook](https://livebook.dev) ✅ - [LiveBook](https://livebook.dev) ✅
- [Atlassian Products](https://www.atlassian.com) - [Atlassian Products](https://www.atlassian.com)
- [Mermaid for Confluence](https://marketplace.atlassian.com/apps/1224722/mermaid-for-confluence?hosting=cloud&tab=overview)
- [Mermaid Integration for Confluence](https://marketplace.atlassian.com/apps/1222792/mermaid-integration-for-confluence?hosting=cloud&tab=overview)
- [Mermaid Diagrams for Confluence](https://marketplace.atlassian.com/apps/1226945/mermaid-diagrams-for-confluence?hosting=cloud&tab=overview)
- [Mermaid Macro for Confluence](https://marketplace.atlassian.com/apps/1231150/mermaid-macro-for-confluence?hosting=cloud&tab=overview)
- [EliteSoft Mermaid Charts and Diagrams](https://marketplace.atlassian.com/apps/1227286/elitesoft-mermaid-charts-and-diagrams?hosting=cloud&tab=overview)
- [Mermaid for Jira Cloud - Draw UML diagrams easily](https://marketplace.atlassian.com/apps/1223053/mermaid-for-jira-cloud-draw-uml-diagrams-easily?hosting=cloud&tab=overview)
- [Mermaid Charts & Diagrams for Confluence](https://marketplace.atlassian.com/apps/1222572/) - [Mermaid Charts & Diagrams for Confluence](https://marketplace.atlassian.com/apps/1222572/)
- [Mermaid Charts & Diagrams for Jira](https://marketplace.atlassian.com/apps/1224537/) - [Mermaid Charts & Diagrams for Jira](https://marketplace.atlassian.com/apps/1224537/)
- [Mermaid Live Editor for Confluence Cloud](https://marketplace.atlassian.com/apps/1231571/mermaid-live-editor-for-confluence?hosting=cloud&tab=overview) - [Mermaid Live Editor for Confluence Cloud](https://marketplace.atlassian.com/apps/1231571/mermaid-live-editor-for-confluence?hosting=cloud&tab=overview)
@@ -106,8 +99,6 @@ Communication tools and platforms
- [phpbb-ext-mermaid](https://github.com/AlfredoRamos/phpbb-ext-mermaid) - [phpbb-ext-mermaid](https://github.com/AlfredoRamos/phpbb-ext-mermaid)
- [NodeBB](https://nodebb.org) - [NodeBB](https://nodebb.org)
- [Mermaid Plugin](https://www.npmjs.com/package/nodebb-plugin-mermaid) - [Mermaid Plugin](https://www.npmjs.com/package/nodebb-plugin-mermaid)
- [Slack](https://slack.com)
- [Mermaid for Slack](https://github.com/JackuB/mermaid-for-slack)
### Wikis ### Wikis
@@ -178,7 +169,6 @@ Communication tools and platforms
### Document Generation ### Document Generation
- [Docusaurus](https://docusaurus.io/docs/markdown-features/diagrams) ✅
- [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/Features/diagrams-and-charts) - [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/Features/diagrams-and-charts)
- [Sphinx](https://www.sphinx-doc.org/en/master/) - [Sphinx](https://www.sphinx-doc.org/en/master/)
- [sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid) - [sphinxcontrib-mermaid](https://github.com/mgaitan/sphinxcontrib-mermaid)

View File

@@ -128,7 +128,7 @@ b. The importing of mermaid library through the `mermaid.esm.mjs` or `mermaid.es
```html ```html
<body> <body>
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true }); mermaid.initialize({ startOnLoad: true });
</script> </script>
</body> </body>
@@ -168,7 +168,7 @@ Rendering in Mermaid is initialized by `mermaid.initialize()` call. However, doi
</pre> </pre>
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true }); mermaid.initialize({ startOnLoad: true });
</script> </script>
</body> </body>

View File

@@ -317,7 +317,7 @@ To select a version:
Replace `<version>` with the desired version number. Replace `<version>` with the desired version number.
Latest Version: <https://cdn.jsdelivr.net/npm/mermaid@11> Latest Version: <https://cdn.jsdelivr.net/npm/mermaid@10>
## Deploying Mermaid ## Deploying Mermaid
@@ -335,12 +335,12 @@ To Deploy Mermaid:
```html ```html
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true }); mermaid.initialize({ startOnLoad: true });
</script> </script>
``` ```
**Doing so commands the mermaid parser to look for the `<div>` or `<pre>` tags with `class="mermaid"`. From these tags, mermaid tries to read the diagram/chart definitions and render them into SVG charts.** **Doing so commands the mermaid parser to look for the `<div>` or `<pre>` tags with `class="mermaid"`. From these tags, mermaid tries read the diagram/chart definitions and render them into SVG charts.**
**Examples can be found in** [Other examples](../syntax/examples.md) **Examples can be found in** [Other examples](../syntax/examples.md)

View File

@@ -399,7 +399,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")
title Component diagram for Internet Banking System - API Application title Component diagram for Internet Banking System - API Application
Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.") Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.")
Container(ma, "Mobile App", "Xamarin", "Provides a limited subset to the internet banking functionality to customers via their mobile mobile device.") Container(ma, "Mobile App", "Xamarin", "Provides a limited subset ot the internet banking functionality to customers via their mobile mobile device.")
ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.") ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
System_Ext(mbs, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") System_Ext(mbs, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
@@ -439,7 +439,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")
title Component diagram for Internet Banking System - API Application title Component diagram for Internet Banking System - API Application
Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.") Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.")
Container(ma, "Mobile App", "Xamarin", "Provides a limited subset to the internet banking functionality to customers via their mobile mobile device.") Container(ma, "Mobile App", "Xamarin", "Provides a limited subset ot the internet banking functionality to customers via their mobile mobile device.")
ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.") ContainerDb(db, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
System_Ext(mbs, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.") System_Ext(mbs, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")

View File

@@ -425,6 +425,8 @@ And `Link` can be one of:
A namespace groups classes. A namespace groups classes.
Code:
```mermaid-example ```mermaid-example
classDiagram classDiagram
namespace BaseShapes { namespace BaseShapes {

View File

@@ -467,7 +467,7 @@ flowchart TB
A & B--> C & D A & B--> C & D
``` ```
If you describe the same diagram using the basic syntax, it will take four lines. A If you describe the same diagram using the the basic syntax, it will take four lines. A
word of warning, one could go overboard with this making the flowchart harder to read in word of warning, one could go overboard with this making the flowchart harder to read in
markdown form. The Swedish word `lagom` comes to mind. It means, not too much and not too little. markdown form. The Swedish word `lagom` comes to mind. It means, not too much and not too little.
This goes for expressive syntaxes as well. This goes for expressive syntaxes as well.

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
"version": "10.2.4", "version": "10.2.4",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"packageManager": "pnpm@8.10.4", "packageManager": "pnpm@8.9.2",
"keywords": [ "keywords": [
"diagram", "diagram",
"markdown", "markdown",
@@ -15,15 +15,15 @@
"git graph" "git graph"
], ],
"scripts": { "scripts": {
"build": "pnpm build:esbuild && pnpm build:types", "build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
"build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts", "build:mermaid": "pnpm build:vite --mermaid",
"build:mermaid": "pnpm build:esbuild --mermaid", "build:viz": "pnpm build:mermaid --visualize",
"build:viz": "pnpm build:esbuild --visualize", "build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
"build:types": "tsc -p ./packages/parser/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-flowchart-elk/tsconfig.json --emitDeclarationOnly",
"build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch", "build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch",
"dev": "ts-node-esm --transpileOnly .esbuild/server.ts", "build:watch": "pnpm build:vite --watch",
"dev:vite": "ts-node-esm --transpileOnly .vite/server.ts", "build": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite", "dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev",
"release": "pnpm build", "release": "pnpm build",
"lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .", "lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .",
"lint:fix": "eslint --cache --cache-strategy content --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts", "lint:fix": "eslint --cache --cache-strategy content --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts",
@@ -32,8 +32,8 @@
"cypress": "cypress run", "cypress": "cypress run",
"cypress:open": "cypress open", "cypress:open": "cypress open",
"e2e": "start-server-and-test dev http://localhost:9000/ cypress", "e2e": "start-server-and-test dev http://localhost:9000/ cypress",
"e2e:coverage": "start-server-and-test dev:coverage http://localhost:9000/ cypress",
"coverage:cypress:clean": "rimraf .nyc_output coverage/cypress", "coverage:cypress:clean": "rimraf .nyc_output coverage/cypress",
"e2e:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm e2e",
"coverage:merge": "ts-node-esm scripts/coverage.ts", "coverage:merge": "ts-node-esm scripts/coverage.ts",
"coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge", "coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge",
"ci": "vitest run", "ci": "vitest run",
@@ -83,7 +83,6 @@
"@vitest/spy": "^0.34.0", "@vitest/spy": "^0.34.0",
"@vitest/ui": "^0.34.0", "@vitest/ui": "^0.34.0",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"chokidar": "^3.5.3",
"concurrently": "^8.0.1", "concurrently": "^8.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"cypress": "^12.10.0", "cypress": "^12.10.0",
@@ -108,7 +107,6 @@
"jison": "^0.4.18", "jison": "^0.4.18",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jsdom": "^22.0.0", "jsdom": "^22.0.0",
"langium-cli": "2.0.1",
"lint-staged": "^13.2.1", "lint-staged": "^13.2.1",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",

View File

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

View File

@@ -1,55 +0,0 @@
{
"name": "@mermaid-js/flowchart-elk",
"version": "1.0.0",
"description": "Flowchart plugin for mermaid with ELK layout",
"module": "dist/mermaid-flowchart-elk.core.mjs",
"types": "dist/packages/mermaid-flowchart-elk/src/detector.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/mermaid-flowchart-elk.core.mjs",
"types": "./dist/packages/mermaid-flowchart-elk/src/detector.d.ts"
},
"./*": "./*"
},
"keywords": [
"diagram",
"markdown",
"flowchart",
"elk",
"mermaid"
],
"scripts": {
"prepublishOnly": "pnpm -w run build"
},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"
},
"author": "Knut Sveidqvist",
"license": "MIT",
"standard": {
"ignore": [
"**/parser/*.js",
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
},
"dependencies": {
"d3": "^7.4.0",
"dagre-d3-es": "7.0.10",
"khroma": "^2.0.0",
"elkjs": "^0.8.2"
},
"devDependencies": {
"concurrently": "^8.0.0",
"rimraf": "^5.0.0",
"mermaid": "workspace:*"
},
"files": [
"dist"
]
}

View File

@@ -1,32 +0,0 @@
import type {
ExternalDiagramDefinition,
DiagramDetector,
DiagramLoader,
} from '../../mermaid/src/diagram-api/types.js';
const id = 'flowchart-elk';
const detector: DiagramDetector = (txt, config): boolean => {
if (
// If diagram explicitly states flowchart-elk
/^\s*flowchart-elk/.test(txt) ||
// If a flowchart/graph diagram has their default renderer set to elk
(/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
) {
return true;
}
return false;
};
const loader: DiagramLoader = async () => {
const { diagram } = await import('./diagram-definition.js');
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@@ -1,12 +0,0 @@
// @ts-ignore: JISON typing missing
import parser from '../../mermaid/src/diagrams/flowchart/parser/flow.jison';
import * as db from '../../mermaid/src/diagrams/flowchart/flowDb.js';
import styles from '../../mermaid/src/diagrams/flowchart/styles.js';
import renderer from './flowRenderer-elk.js';
export const diagram = {
db,
renderer,
parser,
styles,
};

View File

@@ -1,9 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "../..",
"outDir": "./dist"
},
"include": ["./src/**/*.ts"],
"typeRoots": ["./src/types"]
}

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "11.0.0-alpha.2", "version": "10.6.0",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"module": "./dist/mermaid.core.mjs", "module": "./dist/mermaid.core.mjs",
@@ -60,6 +60,8 @@
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^6.0.1", "@braintree/sanitize-url": "^6.0.1",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"cytoscape": "^3.23.0", "cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0", "cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0", "cytoscape-fcose": "^2.1.0",
@@ -68,13 +70,15 @@
"dagre-d3-es": "7.0.10", "dagre-d3-es": "7.0.10",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"dompurify": "^3.0.5", "dompurify": "^3.0.5",
"elkjs": "^0.8.2",
"khroma": "^2.0.0", "khroma": "^2.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mdast-util-from-markdown": "^1.3.0", "mdast-util-from-markdown": "^1.3.0",
"mermaid-parser": "workspace:^", "non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.3", "stylis": "^4.1.3",
"ts-dedent": "^2.2.0", "ts-dedent": "^2.2.0",
"uuid": "^9.0.0" "uuid": "^9.0.0",
"web-worker": "^1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@adobe/jsonschema2md": "^7.1.4", "@adobe/jsonschema2md": "^7.1.4",
@@ -82,7 +86,6 @@
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/d3-sankey": "^0.12.1", "@types/d3-sankey": "^0.12.1",
"@types/d3-scale": "^4.0.3", "@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"@types/d3-selection": "^3.0.5", "@types/d3-selection": "^3.0.5",
"@types/d3-shape": "^3.1.1", "@types/d3-shape": "^3.1.1",
"@types/dompurify": "^3.0.2", "@types/dompurify": "^3.0.2",

View File

@@ -1,10 +1,8 @@
import * as configApi from './config.js'; import * as configApi from './config.js';
import { log } from './logger.js'; import { log } from './logger.js';
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js'; import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
import { detectType, getDiagramLoaderAndPriority } from './diagram-api/detectType.js'; import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
import { UnknownDiagramError } from './errors.js'; import { UnknownDiagramError } from './errors.js';
import { encodeEntities } from './utils.js';
import type { DetailedError } from './utils.js'; import type { DetailedError } from './utils.js';
import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js'; import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js';
@@ -23,7 +21,6 @@ export class Diagram {
private detectError?: UnknownDiagramError; private detectError?: UnknownDiagramError;
constructor(public text: string, public metadata: Pick<DiagramMetadata, 'title'> = {}) { constructor(public text: string, public metadata: Pick<DiagramMetadata, 'title'> = {}) {
this.text = encodeEntities(text);
this.text += '\n'; this.text += '\n';
const cnf = configApi.getConfig(); const cnf = configApi.getConfig();
try { try {
@@ -38,10 +35,7 @@ export class Diagram {
this.db = diagram.db; this.db = diagram.db;
this.renderer = diagram.renderer; this.renderer = diagram.renderer;
this.parser = diagram.parser; this.parser = diagram.parser;
if (this.parser.parser) {
// The parser.parser.yy is only present in JISON parsers. So, we'll only set if required.
this.parser.parser.yy = this.db; this.parser.parser.yy = this.db;
}
this.init = diagram.init; this.init = diagram.init;
this.parse(); this.parse();
} }
@@ -92,14 +86,14 @@ export const getDiagramFromText = async (
// Trying to find the diagram // Trying to find the diagram
getDiagram(type); getDiagram(type);
} catch (error) { } catch (error) {
const { loader, priority } = getDiagramLoaderAndPriority(type); const loader = getDiagramLoader(type);
if (!loader) { if (!loader) {
throw new UnknownDiagramError(`Diagram ${type} not found.`); throw new UnknownDiagramError(`Diagram ${type} not found.`);
} }
// Diagram not available, loading it. // Diagram not available, loading it.
// new diagram will try getDiagram again and if fails then it is a valid throw // new diagram will try getDiagram again and if fails then it is a valid throw
const { id, diagram } = await loader(); const { id, diagram } = await loader();
registerDiagram(id, diagram, priority); registerDiagram(id, diagram);
} }
return new Diagram(text, metadata); return new Diagram(text, metadata);
}; };

View File

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

View File

@@ -2,7 +2,7 @@ import { select } from 'd3';
import { log } from '../logger.js'; import { log } from '../logger.js';
import { getConfig } from '../diagram-api/diagramAPI.js'; import { getConfig } from '../diagram-api/diagramAPI.js';
import { evaluate } from '../diagrams/common/common.js'; import { evaluate } from '../diagrams/common/common.js';
import { decodeEntities } from '../utils.js'; import { decodeEntities } from '../mermaidAPI.js';
/** /**
* @param dom * @param dom

View File

@@ -1,9 +1,9 @@
import createLabel from '../createLabel.js'; import createLabel from '../createLabel.js';
import { createText } from '../../rendering-util/createText.js'; import { createText } from '../../rendering-util/createText.js';
import { getConfig } from '../../diagram-api/diagramAPI.js'; import { getConfig } from '../../diagram-api/diagramAPI.js';
import { decodeEntities } from '../../mermaidAPI.js';
import { select } from 'd3'; import { select } from 'd3';
import { evaluate, sanitizeText } from '../../diagrams/common/common.js'; import { evaluate, sanitizeText } from '../../diagrams/common/common.js';
import { decodeEntities } from '../../utils.js';
export const labelHelper = async (parent, node, _classes, isNode) => { export const labelHelper = async (parent, node, _classes, isNode) => {
let classes; let classes;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,6 @@ describe('diagram detection', () => {
addDetector( addDetector(
'loki', 'loki',
(str) => str.startsWith('loki'), (str) => str.startsWith('loki'),
0,
() => () =>
Promise.resolve({ Promise.resolve({
id: 'loki', id: 'loki',
@@ -31,6 +30,9 @@ describe('diagram detection', () => {
parse: () => { parse: () => {
// no-op // no-op
}, },
parser: {
yy: {},
},
}, },
renderer: { renderer: {
draw: () => { draw: () => {
@@ -46,37 +48,6 @@ describe('diagram detection', () => {
expect(diagram.type).toBe('loki'); expect(diagram.type).toBe('loki');
}); });
test('should allow external diagrams to override internal ones with same ID', async () => {
addDetector(
'flowchart-elk',
(str) => str.startsWith('flowchart-elk'),
1,
() =>
Promise.resolve({
id: 'flowchart-elk',
diagram: {
db: {
getDiagramTitle: () => 'overridden',
},
parser: {
parse: () => {
// no-op
},
},
renderer: {
draw: () => {
// no-op
},
},
styles: {},
},
})
);
const diagram = (await getDiagramFromText('flowchart-elk TD; A-->B')) as Diagram;
expect(diagram).toBeInstanceOf(Diagram);
expect(diagram.db.getDiagramTitle?.()).toBe('overridden');
});
test('should throw the right error for incorrect diagram', async () => { test('should throw the right error for incorrect diagram', async () => {
await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(` await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
"Parse error on line 2: "Parse error on line 2:
@@ -98,18 +69,4 @@ Expecting 'TXT', got 'NEWLINE'"
'"No diagram type detected matching given configuration for text: thor TD; A-->B"' '"No diagram type detected matching given configuration for text: thor TD; A-->B"'
); );
}); });
test('should consider entity codes when present in diagram defination', async () => {
const diagram = await getDiagramFromText(`sequenceDiagram
A->>B: I #9829; you!
B->>A: I #9829; you #infin; times more!`);
// @ts-ignore: we need to add types for sequenceDb which will be done in separate PR
const messages = diagram.db?.getMessages?.();
if (!messages) {
throw new Error('Messages not found!');
}
expect(messages[0].message).toBe('I fl°°9829¶ß you!');
expect(messages[1].message).toBe('I fl°°9829¶ß you fl°infin¶ß times more!');
});
}); });

View File

@@ -231,7 +231,7 @@ export const addRelations = function (relations: ClassRelation[], g: graphlib.Gr
//Set relationship style and line type //Set relationship style and line type
classes: 'relation', classes: 'relation',
pattern: edge.relation.lineType == 1 ? 'dashed' : 'solid', pattern: edge.relation.lineType == 1 ? 'dashed' : 'solid',
id: `id_${edge.id1}_${edge.id2}_${cnt}`, id: 'id' + cnt,
// Set link type for rendering // Set link type for rendering
arrowhead: edge.type === 'arrow_open' ? 'none' : 'normal', arrowhead: edge.type === 'arrow_open' ? 'none' : 'normal',
//Set edge extra labels //Set edge extra labels

View File

@@ -681,82 +681,3 @@ describe('given text representing a method, ', function () {
}); });
}); });
}); });
describe('given text representing an attribute', () => {
describe('when the attribute has no modifiers', () => {
it('should parse the display text correctly', () => {
const str = 'name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe('');
});
});
describe('when the attribute has public "+" modifier', () => {
it('should parse the display text correctly', () => {
const str = '+name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('+name String');
expect(displayDetails.cssStyle).toBe('');
});
});
describe('when the attribute has protected "#" modifier', () => {
it('should parse the display text correctly', () => {
const str = '#name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('#name String');
expect(displayDetails.cssStyle).toBe('');
});
});
describe('when the attribute has private "-" modifier', () => {
it('should parse the display text correctly', () => {
const str = '-name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('-name String');
expect(displayDetails.cssStyle).toBe('');
});
});
describe('when the attribute has internal "~" modifier', () => {
it('should parse the display text correctly', () => {
const str = '~name String';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('~name String');
expect(displayDetails.cssStyle).toBe('');
});
});
describe('when the attribute has static "$" modifier', () => {
it('should parse the display text correctly and apply static css style', () => {
const str = 'name String$';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(staticCssStyle);
});
});
describe('when the attribute has abstract "*" modifier', () => {
it('should parse the display text correctly and apply abstract css style', () => {
const str = 'name String*';
const displayDetails = new ClassMember(str, 'attribute').getDisplayDetails();
expect(displayDetails.displayText).toBe('name String');
expect(displayDetails.cssStyle).toBe(abstractCssStyle);
});
});
});

View File

@@ -106,7 +106,7 @@ export class ClassMember {
this.visibility = firstChar as Visibility; this.visibility = firstChar as Visibility;
} }
if (lastChar.match(/[$*]/)) { if (lastChar.match(/[*?]/)) {
potentialClassifier = lastChar; potentialClassifier = lastChar;
} }

View File

@@ -38,20 +38,6 @@ describe('when securityLevel is antiscript, all script must be removed', () => {
compareRemoveScript(`<img onerror="alert('hello');">`, `<img>`); compareRemoveScript(`<img onerror="alert('hello');">`, `<img>`);
}); });
it('should detect unsecured target attribute, if value is _blank then generate a secured link', () => {
compareRemoveScript(
`<a href="https://mermaid.js.org/" target="_blank">note about mermaid</a>`,
`<a href="https://mermaid.js.org/" target="_blank" rel="noopener">note about mermaid</a>`
);
});
it('should detect unsecured target attribute from links', () => {
compareRemoveScript(
`<a href="https://mermaid.js.org/" target="_self">note about mermaid</a>`,
`<a href="https://mermaid.js.org/" target="_self">note about mermaid</a>`
);
});
it('should detect iframes', () => { it('should detect iframes', () => {
compareRemoveScript( compareRemoveScript(
`<iframe src="http://abc.com/script1.js"></iframe> `<iframe src="http://abc.com/script1.js"></iframe>

View File

@@ -25,27 +25,7 @@ export const getRows = (s?: string): string[] => {
* @returns The safer text * @returns The safer text
*/ */
export const removeScript = (txt: string): string => { export const removeScript = (txt: string): string => {
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target'; return DOMPurify.sanitize(txt);
DOMPurify.addHook('beforeSanitizeAttributes', (node: Element) => {
if (node.tagName === 'A' && node.hasAttribute('target')) {
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') || '');
}
});
const sanitizedText = DOMPurify.sanitize(txt);
DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => {
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) || '');
node.removeAttribute(TEMPORARY_ATTRIBUTE);
if (node.getAttribute('target') === '_blank') {
node.setAttribute('rel', 'noopener');
}
}
});
return sanitizedText;
}; };
const sanitizeMore = (text: string, config: MermaidConfig) => { const sanitizeMore = (text: string, config: MermaidConfig) => {

View File

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

View File

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

View File

@@ -3,7 +3,6 @@ import type {
DiagramDetector, DiagramDetector,
DiagramLoader, DiagramLoader,
} from '../../../diagram-api/types.js'; } from '../../../diagram-api/types.js';
import { log } from '../../../logger.js';
const id = 'flowchart-elk'; const id = 'flowchart-elk';
@@ -14,21 +13,13 @@ const detector: DiagramDetector = (txt, config): boolean => {
// If a flowchart/graph diagram has their default renderer set to elk // If a flowchart/graph diagram has their default renderer set to elk
(/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk') (/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
) { ) {
// This will log at the end, hopefully.
setTimeout(
() =>
log.warn(
'flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](link) for more details. This diagram will be rendered using `dagre` layout as a fallback.'
),
500
);
return true; return true;
} }
return false; return false;
}; };
const loader: DiagramLoader = async () => { const loader: DiagramLoader = async () => {
const { diagram } = await import('../flowDiagram-v2.js'); const { diagram } = await import('./flowchart-elk-definition.js');
return { id, diagram }; return { id, diagram };
}; };

View File

@@ -1,16 +1,16 @@
import { select, line, curveLinear } from 'd3'; import { select, line, curveLinear } from 'd3';
import { insertNode } from '../../mermaid/src/dagre-wrapper/nodes.js'; import { insertNode } from '../../../dagre-wrapper/nodes.js';
import insertMarkers from '../../mermaid/src/dagre-wrapper/markers.js'; import insertMarkers from '../../../dagre-wrapper/markers.js';
import { insertEdgeLabel } from '../../mermaid/src/dagre-wrapper/edges.js'; import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
import { findCommonAncestor } from './render-utils.js'; import { findCommonAncestor } from './render-utils.js';
import { labelHelper } from '../../mermaid/src/dagre-wrapper/shapes/util.js'; import { labelHelper } from '../../../dagre-wrapper/shapes/util.js';
import { getConfig } from '../../mermaid/src/config.js'; import { getConfig } from '../../../config.js';
import { log } from '../../mermaid/src/logger.js'; import { log } from '../../../logger.js';
import { setupGraphViewbox } from '../../mermaid/src/setupGraphViewbox.js'; import { setupGraphViewbox } from '../../../setupGraphViewbox.js';
import common from '../../mermaid/src/diagrams/common/common.js'; import common from '../../common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils.js'; import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
import ELK from 'elkjs/lib/elk.bundled.js'; import ELK from 'elkjs/lib/elk.bundled.js';
import { getLineFunctionsWithOffset } from '../../mermaid/src/utils/lineWithOffset.js'; import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js';
const elk = new ELK(); const elk = new ELK();
@@ -695,7 +695,7 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
* *
* @param text * @param text
* @param diagObj * @param diagObj
* @returns {Record<string, import('../../mermaid/src/diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles * @returns {Record<string, import('../../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/ */
export const getClasses = function (text, diagObj) { export const getClasses = function (text, diagObj) {
log.info('Extracting classes'); log.info('Extracting classes');

View File

@@ -0,0 +1,13 @@
// @ts-ignore: JISON typing missing
import parser from '../parser/flow.jison';
import * as db from '../flowDb.js';
import renderer from './flowRenderer-elk.js';
import styles from './styles.js';
export const diagram = {
db,
renderer,
parser,
styles,
};

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