Compare commits

..

110 Commits

Author SHA1 Message Date
Sidharth Vinod
11b599d77c chore: Fix type imports 2023-08-22 13:44:11 +05:30
Knut Sveidqvist
6e0f41180f Merge pull request #4761 from mermaid-js/sidv/RemoveCircularDeps
Remove Circular Dependencies
2023-08-22 07:37:32 +00:00
Sidharth Vinod
52f05552a1 chore: Remove duplicate CI action 2023-08-22 11:42:12 +05:30
Sidharth Vinod
bb01b3972d chore: Add circular dependency check in CI 2023-08-22 11:38:55 +05:30
Sidharth Vinod
1a50a326cb refactor: Remove circular dependencies 2023-08-22 11:38:25 +05:30
Sidharth Vinod
31c0a0cbab deps: Update unocss and webpack to address vulnerability. 2023-08-21 10:26:41 +05:30
Sidharth Vinod
f4c62436ea Merge pull request #4753 from mermaid-js/renovate/patch-all-patch
chore(deps): update all patch dependencies (patch)
2023-08-21 03:53:05 +00:00
renovate[bot]
21d0998db8 chore(deps): update all patch dependencies 2023-08-21 03:01:21 +00:00
Sidharth Vinod
0899f7918a Merge pull request #4752 from mermaid-js/chore/add-more-release-notes-categories
ci(release-drafter): add more release notes categories
2023-08-21 02:55:59 +00:00
Alois Klink
7a547abd89 ci(release-drafter): add more release notes categories
Add the following new categories to the release notes:
  - **BREAKING CHANGES**
  - Performance
  - Documentation

I've also made the `feature`, `fix`, and `chore` label point to
their appropriate section.
2023-08-20 23:42:18 +01:00
Sidharth Vinod
72fa3488b5 Merge pull request #4501 from Yokozuna59/standardized-pie-definitions
standardized pie definitions
2023-08-20 14:56:42 +00:00
Sidharth Vinod
cfffba2817 chore: Add comments on redirectMaps 2023-08-20 00:59:58 +05:30
Reda Al Sulais
e7ee3eb9ea Merge branch 'develop' into standardized-pie-definitions 2023-08-19 01:07:55 +03:00
Nikolay Rozhkov
b85c011cd1 Merge pull request #4742 from nirname/docs/2910_small-fixes
Fixes to Docs sidebar, main page and badges
2023-08-17 17:59:12 +00:00
Nikolay Rozhkov
aec97d68cc Fixed wrong collapsible option at the left menu, updated vue
Fixed badges

Rely on classes instead of DOM structure

Prettier

Update docs
2023-08-17 16:37:22 +03:00
Sidharth Vinod
7b7e281ec7 Merge pull request #4740 from jason-curtis/issue-2509-subgraph-direction-limitation
Documentation for #2509
2023-08-17 04:57:56 +00:00
Sidharth Vinod
35cc34d422 chore: Move SVG import to comment. 2023-08-17 10:28:50 +05:30
Jason Curtis
edb6ceae43 build docs 2023-08-17 03:48:00 +00:00
Jason Curtis
ef8b75a6da Remove whitespace on empty line 2023-08-17 03:32:42 +00:00
Reda Al Sulais
02daf5417b Merge branch 'standardized-pie-definitions' of https://github.com/Yokozuna59/mermaid into standardized-pie-definitions 2023-08-17 02:22:12 +03:00
Reda Al Sulais
77d8e15dc4 remove chart from pie.spec.ts description 2023-08-17 02:21:15 +03:00
Yokozuna59
62142089f1 Update docs 2023-08-16 22:47:51 +00:00
Reda Al Sulais
defe40692a change defaultConfig type to RequiredDeep and use it in pieDb 2023-08-17 01:42:41 +03:00
Jason Curtis
337ff3c32b Documentation for #2509 2023-08-16 21:12:32 +00:00
Reda Al Sulais
744cc792f4 Merge branch 'develop' into standardized-pie-definitions 2023-08-15 23:27:03 +03:00
Reda Al Sulais
99978da55b Merge pull request #4724 from Yokozuna59/chore/convert-svgDrawCommon-to-ts
convert `diagrams/common/svgDrawCommon.js` to ts
2023-08-15 16:41:11 +00:00
Reda Al Sulais
50eb3cf1c9 Merge branch 'develop' into chore/convert-svgDrawCommon-to-ts 2023-08-15 19:37:02 +03:00
Reda Al Sulais
c1b9c54fc9 use DiagramStylesProvider in pieStyles.ts 2023-08-15 17:56:18 +03:00
Reda Al Sulais
0d179c501e Merge branch 'develop' into standardized-pie-definitions 2023-08-15 14:01:09 +03:00
Sidharth Vinod
085e8f78b3 Merge pull request #4731 from mermaid-js/renovate/patch-all-patch
Update all patch dependencies (patch)
2023-08-14 11:05:49 +00:00
Sidharth Vinod
f9d4a62609 Merge pull request #4732 from mermaid-js/renovate/all-minor
Update all minor dependencies (minor)
2023-08-14 11:04:27 +00:00
renovate[bot]
2a8374312f Update all minor dependencies 2023-08-14 03:07:45 +00:00
renovate[bot]
b60410161d Update all patch dependencies 2023-08-14 02:02:28 +00:00
Reda Al Sulais
d219f92a19 Merge branch 'develop' into chore/convert-svgDrawCommon-to-ts 2023-08-13 17:45:37 +03:00
Reda Al Sulais
3f3a7340e3 remove setConfig and resetConfig in pie 2023-08-12 21:18:07 +03:00
Reda Al Sulais
cb5f70c139 add structuredClone in pie getConfig 2023-08-12 21:09:14 +03:00
Reda Al Sulais
99c1758490 make more RectData required and remove optional assignment 2023-08-12 20:25:10 +03:00
Reda Al Sulais
541ee1eade Merge remote-tracking branch 'upstream/develop' into chore/convert-svgDrawCommon-to-ts 2023-08-11 23:07:45 +03:00
Reda Al Sulais
f01ad644e3 Merge remote-tracking branch 'upstream/develop' into standardized-pie-definitions
Signed-off-by: Reda Al Sulais <u.yokozuna@gmail.com>
2023-08-11 22:44:05 +03:00
Reda Al Sulais
9538233573 use lineBreakRegex in svgDrawCommon 2023-08-11 21:16:53 +03:00
Reda Al Sulais
5a2ea7c297 fix svgDrawCommon import by adding .js 2023-08-11 21:13:17 +03:00
Reda Al Sulais
22b172d873 add types to svgDrawCommon.ts 2023-08-11 20:56:00 +03:00
Reda Al Sulais
92098e23eb convert svgDrawCommon to TS 2023-08-11 20:54:39 +03:00
Reda Al Sulais
f9d978859e Merge branch 'develop' into standardized-pie-definitions 2023-08-11 19:56:22 +03:00
Reda Al Sulais
8a8e062342 cleanAndMerge pieConfig 2023-08-11 03:55:04 +03:00
Reda Al Sulais
1721282182 Merge branch 'develop' into standardized-pie-definitions 2023-08-11 01:46:33 +03:00
Reda Al Sulais
120bdabee1 Merge branch 'develop' into standardized-pie-definitions 2023-08-11 01:04:06 +03:00
Reda Al Sulais
a5a3ffc768 Merge pull request #2 from mermaid-js/sidv/cleanMerge
Sidv/clean merge
2023-08-11 00:49:38 +03:00
Sidharth Vinod
dfeb25127b remove cleanClone 2023-08-11 02:50:24 +05:30
Sidharth Vinod
396bda8d95 feat: Add cleanAndMerge and tests 2023-08-11 02:47:46 +05:30
Sidharth Vinod
cc70d37166 chore: Rename utils.spec.ts 2023-08-11 02:38:57 +05:30
Reda Al Sulais
3f5da06bb0 move db assignment from beforeEach to beforeAll 2023-08-10 22:43:39 +03:00
Reda Al Sulais
be106befff create structuredCleanClone helper function 2023-08-10 23:10:06 +03:00
Reda Al Sulais
c4113541e1 Merge remote-tracking branch 'upstream/develop' into standardized-pie-definitions 2023-08-10 21:44:29 +03:00
Reda Al Sulais
fb49f25eef add more types to pieRenderer 2023-08-10 21:36:39 +03:00
Reda Al Sulais
a9681d1b1c add resetConfig to clear in pieDb 2023-08-10 21:19:56 +03:00
Reda Al Sulais
545d361d3f rename reset to resetConfig 2023-08-10 21:01:32 +03:00
Reda Al Sulais
38dc17f426 use structedClone in pieDb 2023-08-09 14:12:08 +03:00
Reda Al Sulais
453c67e5ea remove PieDiagramConfig and import generated one 2023-08-09 13:47:08 +03:00
Reda Al Sulais
a69a97fdd9 remove unnecessary lines in pie files 2023-08-09 13:41:01 +03:00
Reda Al Sulais
820cc48c11 Merge remote-tracking branch 'upstream/develop' into standardized-pie-definitions 2023-08-09 13:31:34 +03:00
Reda Al Sulais
ca1cdb1d94 remove unused HTML import in pieRenderer 2023-08-08 19:00:27 +03:00
Reda Al Sulais
5485517b27 remove SVG import logs in setupGraphViewbox 2023-08-08 18:55:46 +03:00
Reda Al Sulais
96380600d9 Merge remote-tracking branch 'upstream/develop' into standardized-pie-definitions 2023-08-08 15:02:23 +03:00
Reda Al Sulais
9563b22132 refactor pieRenderer 2023-08-06 21:23:42 +03:00
Reda Al Sulais
e6a18eea91 Merge remote-tracking branch upstream/develop into standardized-pie-definitions 2023-08-06 18:30:51 +03:00
Reda Al Sulais
71205f5bd6 Merge branch 'mermaid-js:develop' into standardized-pie-definitions 2023-08-05 21:33:03 +03:00
Reda Al Sulais
23b6d53f80 Merge branch 'standardized-pie-definitions' of https://github.com/Yokozuna59/mermaid into standardized-pie-definitions 2023-08-04 01:33:29 +03:00
Reda Al Sulais
f4671e4e3a Merge branch 'mermaid-js:develop' into standardized-pie-definitions 2023-08-04 01:32:12 +03:00
Reda Al Sulais
c954e0eb1d rename styles.ts into pieStyles.ts 2023-08-03 21:00:22 +03:00
Reda Al Sulais
de37efefd7 add type for PieStyleOptions 2023-08-03 20:58:39 +03:00
Reda Al Sulais
bdfd8974d4 change default useWidth in pie to 984 2023-08-03 20:56:18 +03:00
Reda Al Sulais
aecf451ed1 update pnpm-lock.yaml 2023-08-02 15:55:02 +03:00
Reda Al Sulais
7d69ad2d5b Merge remote-tracking branch upstream/develop into standardized-pie-definitions 2023-08-02 15:54:23 +03:00
Reda Al Sulais
74fa9956a3 run pnpm lint:fix 2023-08-02 15:46:18 +03:00
Reda Al Sulais
e33340331a add type-fest to mermaid dev deps 2023-08-02 15:44:44 +03:00
Yokozuna59
760548335c Merge branch 'develop' into standardized-pie-definitions 2023-06-27 01:45:35 +03:00
Yokozuna59
41c5152015 Merge branch 'mermaid-js:develop' into standardized-pie-definitions 2023-06-21 15:23:21 +03:00
Yokozuna59
796a761a7d remove unnecessary types in pieRenderer 2023-06-21 00:32:14 +03:00
Yokozuna59
09c4a26509 rename PieDb interface and more types with null sefety 2023-06-21 00:22:56 +03:00
Yokozuna59
ce9d0e2e6a use global useMaxWidth until making setConfig updates pie setConfig 2023-06-19 22:07:15 +03:00
Yokozuna59
ae8860eec3 add undefined to getConfig return type until handle other diagrams 2023-06-19 21:56:35 +03:00
Yokozuna59
67d287f85e use local config instead of glolal one in pieRenderer 2023-06-19 21:51:38 +03:00
Yokozuna59
9c2b95fc3c add config functions to DiagramDB interface 2023-06-19 20:13:15 +03:00
Yokozuna59
3a22d4a501 shorten vitest pie.spec.ts by removing one time use variables 2023-06-19 17:57:39 +03:00
Yokozuna59
cecf759b0b add showData unit test case 2023-06-19 17:52:28 +03:00
Yokozuna59
35c6b671de add config unit test cases for pie chart 2023-06-19 17:46:19 +03:00
Yokozuna59
c894c1f5b5 add Required and RequiredDeep to pieDb, add config functions 2023-06-19 17:45:38 +03:00
Yokozuna59
a92571d588 add type-fest to mermaid dev deps 2023-06-19 17:40:01 +03:00
Yokozuna59
34a47706fd update pnpm-lock.yaml 2023-06-19 13:41:36 +03:00
Yokozuna59
906d909d87 Merge branch develop into standardized-pie-definitions 2023-06-19 13:39:41 +03:00
Yokozuna59
1d0aa763de document pie useWidth and update other pie config attributes 2023-06-18 17:53:28 +03:00
Yokozuna59
f6dc089ddf add D3Sections type with variable renaming 2023-06-18 17:39:15 +03:00
Yokozuna59
95e01b4935 move PieDiagramConfig into pieTypes 2023-06-18 01:45:21 +03:00
Yokozuna59
9145a9e69e Merge branch 'mermaid-js:develop' into standardized-pie-definitions 2023-06-17 15:21:39 +03:00
Yokozuna59
6941814729 update pnpm-lock.yaml 2023-06-17 15:01:20 +03:00
Yokozuna59
32d3001e2a Merge branch 'develop' into standardized-pie-definitions 2023-06-17 14:54:44 +03:00
Yokozuna59
452e543e77 Merge branch 'develop' into standardized-pie-definitions 2023-06-17 14:43:51 +03:00
Yokozuna59
23a5832fc9 add null safety in pieRenderer 2023-06-17 04:05:07 +03:00
Yokozuna59
8794fa0b38 clean up pieRenderer.ts 2023-06-17 03:28:08 +03:00
Yokozuna59
f2338f5b66 remove vanilla-extract from dev dependencies 2023-06-17 00:42:29 +03:00
Yokozuna59
5aba2fed8b remove cy.get of info diagram 2023-06-17 00:01:54 +03:00
Yokozuna59
bd6795032f return pieRender back to original and update it partially 2023-06-16 23:58:56 +03:00
Yokozuna59
c17b723295 convert pie.spec.js to ts, remove cy.get and useless comment, add showData unit test case 2023-06-16 23:45:13 +03:00
Yokozuna59
231a9630df do deep cloning on section field in pie clear 2023-06-16 23:30:41 +03:00
Yokozuna59
bdb967e0a8 fix renderer import in pieDiagram 2023-06-16 23:09:10 +03:00
Yokozuna59
ea3fbbd58d initial converting pie files 2023-06-16 23:05:06 +03:00
Yokozuna59
afea3e8f37 clean up demos/pie.html 2023-06-16 23:03:42 +03:00
Yokozuna59
4e7dbf76cc add SVG type in configureSvgSize function 2023-06-16 23:03:07 +03:00
Yokozuna59
a3901f691a add @types/d3-scale and @types/d3-shape` to mermaid dev deps 2023-06-16 22:47:40 +03:00
107 changed files with 2007 additions and 2326 deletions

View File

@@ -1,20 +0,0 @@
/**
* Shared common options for both ESBuild and Vite
*/
export const packageOptions = {
mermaid: {
name: 'mermaid',
packageName: 'mermaid',
file: 'mermaid.ts',
},
'mermaid-example-diagram': {
name: 'mermaid-example-diagram',
packageName: 'mermaid-example-diagram',
file: 'detector.ts',
},
'mermaid-zenuml': {
name: 'mermaid-zenuml',
packageName: 'mermaid-zenuml',
file: 'detector.ts',
},
} as const;

View File

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

View File

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

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,93 +0,0 @@
import express from 'express';
import type { NextFunction, Request, Response } from 'express';
import cors from 'cors';
import { getBuildConfig } from './util.js';
import { context } from 'esbuild';
import chokidar from 'chokidar';
const mermaidCtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid' })
);
const mermaidIIFECtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid', format: 'iife' })
);
const externalCtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid-example-diagram' })
);
const zenumlCtx = await context(
getBuildConfig({ minify: false, core: false, entryName: 'mermaid-zenuml' })
);
const contexts = [mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
const rebuildAll = async () => {
await Promise.all(contexts.map((ctx) => ctx.rebuild()));
};
let clients: { id: number; response: Response }[] = [];
function eventsHandler(request: Request, response: Response, next: NextFunction) {
const headers = {
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
'Cache-Control': 'no-cache',
};
response.writeHead(200, headers);
const clientId = Date.now();
clients.push({
id: clientId,
response,
});
request.on('close', () => {
clients = clients.filter((client) => client.id !== clientId);
});
}
let timeoutId: NodeJS.Timeout | undefined = undefined;
/**
* Debounce file change events to avoid rebuilding multiple times.
*/
function handleFileChange() {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(async () => {
await rebuildAll();
sendEventsToAll();
timeoutId = undefined;
}, 100);
}
function sendEventsToAll() {
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
}
async function createServer() {
const app = express();
chokidar
.watch('**/src/**/*.{js,ts,yaml,json}', {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
.on('all', async (event, path) => {
// Ignore other events.
if (!['add', 'change'].includes(event)) {
return;
}
console.log(`${path} changed. Rebuilding...`);
handleFileChange();
});
app.use(cors());
app.get('/events', eventsHandler);
app.use(express.static('./packages/mermaid/dist'));
app.use(express.static('./packages/mermaid-zenuml/dist'));
app.use(express.static('./packages/mermaid-example-diagram/dist'));
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));
app.listen(9000, () => {
console.log(`Listening on http://localhost:9000`);
});
}
createServer();

View File

@@ -1,81 +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));
interface MermaidBuildOptions {
minify: boolean;
core?: boolean;
metafile?: boolean;
format?: 'esm' | 'iife';
entryName: keyof typeof packageOptions;
}
const buildOptions = (override: BuildOptions): BuildOptions => {
return {
bundle: true,
minify: true,
keepNames: true,
platform: 'browser',
tsconfig: 'tsconfig.json',
resolveExtensions: ['.ts', '.js', '.json', '.jison', '.yaml'],
external: ['require', 'fs', 'path'],
outdir: 'dist',
plugins: [jisonPlugin, jsonSchemaPlugin],
sourcemap: 'external',
...override,
};
};
export const getBuildConfig = ({
minify,
core,
entryName,
metafile,
format,
}: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
let output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[`${name}${core ? '.core' : format === 'iife' ? '' : '.esm'}${
minify ? '.min' : ''
}`]: `src/${file}`,
},
metafile,
logLevel: 'info',
});
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies
external.push(...Object.keys(dependencies));
output.external = external;
}
if (format === 'iife') {
output.format = 'iife';
output.splitting = false;
output.globalName = '__esbuild_esm_mermaid';
output.footer = {
js: 'globalThis.mermaid = globalThis.__esbuild_esm_mermaid.default;',
};
output.outExtension = { '.js': '.js' };
} else {
output.format = 'esm';
output.splitting = true;
output.outExtension = { '.js': '.mjs' };
}
return output;
};

View File

@@ -48,6 +48,7 @@ module.exports = {
'no-prototype-builtins': 'off',
'no-unused-vars': 'off',
'cypress/no-async-tests': 'off',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/ban-ts-comment': [

View File

@@ -1,14 +1,27 @@
name-template: '$NEXT_PATCH_VERSION'
tag-template: '$NEXT_PATCH_VERSION'
categories:
- title: '🚨 **Breaking Changes**'
labels:
- 'Breaking Change'
- title: '🚀 Features'
labels:
- 'Type: Enhancement'
- 'feature' # deprecated, new PRs shouldn't have this
- title: '🐛 Bug Fixes'
labels:
- 'Type: Bug / Error'
- 'fix' # deprecated, new PRs shouldn't have this
- title: '🧰 Maintenance'
label: 'Type: Other'
labels:
- 'Type: Other'
- 'chore' # deprecated, new PRs shouldn't have this
- title: '⚡️ Performance'
labels:
- 'Type: Performance'
- title: '📚 Documentation'
labels:
- 'Area: Documentation'
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
sort-by: title
sort-direction: ascending

View File

@@ -62,8 +62,22 @@ jobs:
ERROR_MESSAGE+=' `pnpm run --filter mermaid types:build-config`'
ERROR_MESSAGE+=' on your local machine.'
echo "::error title=Lint failure::${ERROR_MESSAGE}"
# make sure to return an error exitcode so that GitHub actions shows a red-cross
exit 1
# make sure to return an error exitcode so that GitHub actions shows a red-cross
exit 1
fi
- name: Verify no circular dependencies
working-directory: ./packages/mermaid
shell: bash
run: |
if ! pnpm run --filter mermaid checkCircle; then
ERROR_MESSAGE='Circular dependency detected.'
ERROR_MESSAGE+=' This should be fixed by removing the circular dependency.'
ERROR_MESSAGE+=' Run `pnpm run --filter mermaid checkCircle` on your local machine'
ERROR_MESSAGE+=' to see the circular dependency.'
echo "::error title=Lint failure::${ERROR_MESSAGE}"
# make sure to return an error exitcode so that GitHub actions shows a red-cross
exit 1
fi
- name: Verify Docs

View File

@@ -3,11 +3,11 @@ import { resolve } from 'path';
import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js';
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
import { readFileSync } from 'fs';
import typescript from '@rollup/plugin-typescript';
import { visualizer } from 'rollup-plugin-visualizer';
import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js';
import istanbul from 'vite-plugin-istanbul';
import { packageOptions } from '../.build/common.js';
const visualize = process.argv.includes('--visualize');
const watch = process.argv.includes('--watch');
@@ -36,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 {
minify: boolean | 'esbuild';
core?: boolean;
@@ -54,8 +72,34 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
sourcemap,
entryFileNames: `${name}.esm${minify ? '.min' : ''}.mjs`,
},
{
name,
format: 'umd',
sourcemap,
entryFileNames: `${name}${minify ? '.min' : ''}.js`,
},
];
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies
// Adapted from the RegEx used by `rollup-plugin-node`
external.push(new RegExp('^(?:' + Object.keys(dependencies).join('|') + ')(?:/.+)?$'));
// This needs to be an array. Otherwise vite will build esm & umd with same name and overwrite esm with umd.
output = [
{
name,
format: 'esm',
sourcemap,
entryFileNames: `${name}.core.mjs`,
},
];
}
const config: InlineConfig = {
configFile: false,
build: {
@@ -102,6 +146,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
const buildPackage = async (entryName: keyof typeof packageOptions) => {
await build(getBuildConfig({ minify: false, entryName }));
await build(getBuildConfig({ minify: 'esbuild', entryName }));
await build(getBuildConfig({ minify: false, core: true, entryName }));
};
const main = async () => {

View File

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

View File

@@ -1,5 +1,108 @@
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 { 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',
'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.
@@ -16,13 +119,32 @@ export default function jsonSchemaPlugin(): PluginOption {
return;
}
const jsonSchema = loadSchema(src, idAsUrl.pathname);
return {
code: idAsUrl.searchParams.get('only-defaults')
? getDefaults(jsonSchema)
: getSchema(jsonSchema),
map: null, // no source map
};
if (idAsUrl.searchParams.get('only-defaults')) {
const jsonSchema = load(src, {
filename: idAsUrl.pathname,
// only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
// e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
schema: JSON_SCHEMA,
}) as JSONSchemaType<MermaidConfig>;
return {
code: `export default ${JSON.stringify(generateDefaults(jsonSchema), undefined, 2)};`,
map: null, // no source map
};
} else {
return {
code: `export default ${JSON.stringify(
load(src, {
filename: idAsUrl.pathname,
// only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
// e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
schema: JSON_SCHEMA,
}),
undefined,
2
)};`,
map: null, // provide source map if available
};
}
},
};
}

View File

@@ -1,13 +0,0 @@
/**
* Mocked pie (picChart) diagram renderer
*/
import { vi } from 'vitest';
export const draw = vi.fn().mockImplementation(() => {
return '';
});
export default {
draw,
};

8
__mocks__/pieRenderer.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* Mocked pie (picChart) diagram renderer
*/
import { vi } from 'vitest';
const draw = vi.fn().mockImplementation(() => '');
export const renderer = { draw };

View File

@@ -80,7 +80,6 @@
"mdbook",
"mermaidjs",
"mermerd",
"metafile",
"mindaugas",
"mindmap",
"mindmaps",
@@ -107,6 +106,7 @@
"rects",
"reda",
"redmine",
"regexes",
"rehype",
"roledescription",
"rozhkov",

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

@@ -1,89 +1,85 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
describe('Pie Chart', () => {
describe('pie chart', () => {
it('should render a simple pie diagram', () => {
imgSnapshotTest(
`pie title Sports in Sweden
"Bandy": 40
"Ice-Hockey": 80
"Football": 90
`
pie title Sports in Sweden
"Bandy" : 40
"Ice-Hockey" : 80
"Football" : 90
`,
{}
);
cy.get('svg');
});
it('should render a simple pie diagram with long labels', () => {
imgSnapshotTest(
`pie title NETFLIX
"Time spent looking for movie": 90
"Time spent watching it": 10
`
pie title NETFLIX
"Time spent looking for movie" : 90
"Time spent watching it" : 10
`,
{}
);
cy.get('svg');
});
it('should render a simple pie diagram with capital letters for labels', () => {
imgSnapshotTest(
`pie title What Voldemort doesn't have?
"FRIENDS": 2
"FAMILY": 3
"NOSE": 45
`
pie title What Voldemort doesn't have?
"FRIENDS" : 2
"FAMILY" : 3
"NOSE" : 45
`,
{}
);
cy.get('svg');
});
it('should render a pie diagram when useMaxWidth is true (default)', () => {
renderGraph(
`
pie title Sports in Sweden
"Bandy" : 40
"Ice-Hockey" : 80
"Football" : 90
`pie title Sports in Sweden
"Bandy": 40
"Ice-Hockey": 80
"Football": 90
`,
{ pie: { useMaxWidth: true } }
);
cy.get('svg').should((svg) => {
expect(svg).to.have.attr('width', '100%');
// expect(svg).to.have.attr('height');
// const height = parseFloat(svg.attr('height'));
// expect(height).to.eq(450);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.eq(984);
});
});
it('should render a pie diagram when useMaxWidth is false', () => {
renderGraph(
`
pie title Sports in Sweden
"Bandy" : 40
"Ice-Hockey" : 80
"Football" : 90
`pie title Sports in Sweden
"Bandy": 40
"Ice-Hockey": 80
"Football": 90
`,
{ pie: { useMaxWidth: false } }
);
cy.get('svg').should((svg) => {
// const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
// expect(height).to.eq(450);
expect(width).to.eq(984);
expect(svg).to.not.have.attr('style');
});
});
it('should render a pie diagram when textPosition is set', () => {
it('should render a pie diagram when textPosition is setted', () => {
imgSnapshotTest(
`
pie
"Dogs": 50
"Cats": 25
`,
`pie
"Dogs": 50
"Cats": 25
`,
{ logLevel: 1, pie: { textPosition: 0.9 } }
);
cy.get('svg');
});
it('should render a pie diagram with showData', () => {
imgSnapshotTest(
`pie showData
"Dogs": 50
"Cats": 25
`
);
});
});

View File

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

View File

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

View File

@@ -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>
<head>
<script src="./viewer.js" type="module"></script>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<style>
.malware {
@@ -32,6 +33,12 @@
</script>
</head>
<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>
</html>

View File

@@ -30,33 +30,5 @@ graph TB
console.log(svg);
el.innerHTML = svg;
</script>
<script>
// Set to false to disable live reload
const liveReload = true;
// Connect to the server and reload the page if the server sends a reload message
const connectToEvents = () => {
const events = new EventSource('/events');
const loadTime = Date.now();
events.onmessage = (event) => {
const time = JSON.parse(event.data);
if (time && time > loadTime) {
location.reload();
}
};
events.onerror = (error) => {
console.error(error);
events.close();
// Try to reconnect after 1 second in case of errors
setTimeout(connectToEvents, 1000);
};
events.onopen = () => {
console.log('Connected to live reload server');
};
};
if (liveReload) {
connectToEvents();
}
</script>
</body>
</html>

View File

@@ -7,7 +7,6 @@
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
}
</style>
@@ -17,37 +16,32 @@
<h1>Pie chart demos</h1>
<pre class="mermaid">
pie title Pets adopted by volunteers
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"Dogs" : 386
"Cats" : 85
"Rats" : 15
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"Dogs": 386
"Cats": 85
"Rats": 15
</pre>
<hr />
<pre class="mermaid">
%%{init: {"pie": {"textPosition": 0.9}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%%
pie
title Key elements in Product X
%%{init: {"pie": {"textPosition": 0.9}, "themeVariables": {"pieOuterStrokeWidth": "5px"}}}%%
pie
title Key elements in Product X
accTitle: Key elements in Product X
accDescr: This is a pie chart showing the key elements in Product X.
"Calcium" : 42.96
"Potassium" : 50.05
"Magnesium" : 10.01
"Iron" : 5
accDescr: This is a pie chart showing the key elements in Product X.
"Calcium": 42.96
"Potassium": 50.05
"Magnesium": 10.01
"Iron": 5
</pre>
<script type="module">
import mermaid from '/mermaid.esm.mjs';
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
theme: 'forest',
// themeCSS: '.node rect { fill: red; }',
logLevel: 3,
securityLevel: 'loose',
// flowchart: { curve: 'basis' },
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50 },
// sequenceDiagram: { actorMargin: 300 } // deprecated
});
</script>
</body>

View File

@@ -1,7 +1,7 @@
version: '3.9'
services:
mermaid:
image: node:18.17.0-alpine3.18
image: node:18.17.1-alpine3.18
stdin_open: true
tty: true
working_dir: /mermaid
@@ -17,7 +17,7 @@ services:
- 9000:9000
- 3333:3333
cypress:
image: cypress/included:12.17.2
image: cypress/included:12.17.4
stdin_open: true
tty: true
working_dir: /mermaid

View File

@@ -14,13 +14,13 @@
#### Defined in
[defaultConfig.ts:266](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L266)
[defaultConfig.ts:268](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L268)
---
### default
`Const` **default**: `Partial`<`MermaidConfig`>
`Const` **default**: `RequiredDeep`<`MermaidConfig`>
Default mermaid configuration options.
@@ -30,4 +30,4 @@ Non-JSON JS default values are listed in this file, e.g. functions, or
#### Defined in
[defaultConfig.ts:16](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L16)
[defaultConfig.ts:18](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L18)

View File

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

View File

@@ -14,8 +14,12 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
<img src="/header.png" alt="" />
<div class='badges'>
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
</div>
<!-- Mermaid book banner -->
[![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!](img/book-banner-post-release.jpg)](https://mermaid-js.github.io/mermaid/landing/)
@@ -285,7 +289,7 @@ To select a version:
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
@@ -303,7 +307,7 @@ To Deploy Mermaid:
```html
<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 });
</script>
```
@@ -389,8 +393,12 @@ The above command generates files into the `dist` folder and publishes them to \
## Contributors
<div class='badges'>
[![Good first issue](https://img.shields.io/github/labels/mermaid-js/mermaid/Good%20first%20issue%21)](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%21%22) [![Contributors](https://img.shields.io/github/contributors/mermaid-js/mermaid)](https://github.com/mermaid-js/mermaid/graphs/contributors) [![Commits](https://img.shields.io/github/commit-activity/m/mermaid-js/mermaid)](https://github.com/mermaid-js/mermaid/graphs/contributors)
</div>
Mermaid is a growing community and is always accepting new contributors. There's a lot of different ways to help out and we're always looking for extra hands! Look at [this issue](https://github.com/mermaid-js/mermaid/issues/866) if you want to know where to start helping out.
Detailed information about how to contribute can be found in the [contribution guide](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
@@ -424,20 +432,14 @@ A quick note from Knut Sveidqvist:
_Mermaid was created by Knut Sveidqvist for easier documentation._
<style scoped>
#contributors + p,
#about-mermaid + p + p + blockquote + img + p
{
display: flex
.badges > p {
display: flex;
}
.badges > p > a {
margin: 0 0.5rem;
}
#contributors + p a,
#about-mermaid + p + p + blockquote + img + p a
{
margin: 0 0.5rem
}
.dark #VPContent > div > div > div.content > div > main > div > div > img
{
.dark #VPContent > div > div > div.content > div > main > div > div > img {
filter: invert(1) hue-rotate(217deg) contrast(0.72);
}
</style>

View File

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

View File

@@ -748,6 +748,48 @@ flowchart LR
B1 --> B2
```
#### Limitation
If any of a subgraph's nodes are linked to the outside, subgraph direction will be ignored. Instead the subgraph will inherit the direction of the parent graph:
```mermaid-example
flowchart LR
subgraph subgraph1
direction TB
top1[top] --> bottom1[bottom]
end
subgraph subgraph2
direction TB
top2[top] --> bottom2[bottom]
end
%% ^ These subgraphs are identical, except for the links to them:
%% Link *to* subgraph1: subgraph1 direction is mantained
outside --> subgraph1
%% Link *within* subgraph2:
%% subgraph2 inherits the direction of the top-level graph (LR)
outside ---> top2
```
```mermaid
flowchart LR
subgraph subgraph1
direction TB
top1[top] --> bottom1[bottom]
end
subgraph subgraph2
direction TB
top2[top] --> bottom2[bottom]
end
%% ^ These subgraphs are identical, except for the links to them:
%% Link *to* subgraph1: subgraph1 direction is mantained
outside --> subgraph1
%% Link *within* subgraph2:
%% subgraph2 inherits the direction of the top-level graph (LR)
outside ---> top2
```
## Markdown Strings
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
"version": "10.2.4",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@8.6.11",
"packageManager": "pnpm@8.6.12",
"keywords": [
"diagram",
"markdown",
@@ -15,14 +15,14 @@
"git graph"
],
"scripts": {
"build": "pnpm run -r clean && pnpm build:esbuild && pnpm build:types",
"build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts",
"build:mermaid": "pnpm build:esbuild --mermaid",
"build:viz": "pnpm build:esbuild --visualize",
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
"build:mermaid": "pnpm build:vite --mermaid",
"build:viz": "pnpm build:mermaid --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",
"dev": "ts-node-esm --transpileOnly .esbuild/server.ts",
"dev:vite": "ts-node-esm --transpileOnly .vite/server.ts",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
"build:watch": "pnpm build:vite --watch",
"build": "pnpm run -r clean && pnpm build:types && pnpm build:vite",
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev",
"release": "pnpm build",
"lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .",
"lint:fix": "eslint --cache --cache-strategy content --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts",
@@ -31,8 +31,8 @@
"cypress": "cypress run",
"cypress:open": "cypress open",
"e2e": "start-server-and-test dev http://localhost:9000/ cypress",
"e2e:coverage": "start-server-and-test dev:coverage http://localhost:9000/ cypress",
"coverage:cypress:clean": "rimraf .nyc_output coverage/cypress",
"e2e:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm e2e",
"coverage:merge": "ts-node-esm scripts/coverage.ts",
"coverage": "pnpm test:coverage --run && pnpm e2e:coverage && pnpm coverage:merge",
"ci": "vitest run",
@@ -78,11 +78,10 @@
"@types/rollup-plugin-visualizer": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"@vitest/coverage-v8": "^0.33.0",
"@vitest/spy": "^0.33.0",
"@vitest/ui": "^0.33.0",
"@vitest/coverage-v8": "^0.34.0",
"@vitest/spy": "^0.34.0",
"@vitest/ui": "^0.34.0",
"ajv": "^8.12.0",
"chokidar": "^3.5.3",
"concurrently": "^8.0.1",
"cors": "^2.8.5",
"cypress": "^12.10.0",
@@ -120,10 +119,10 @@
"typescript": "^5.1.3",
"vite": "^4.3.9",
"vite-plugin-istanbul": "^4.1.0",
"vitest": "^0.33.0"
"vitest": "^0.34.0"
},
"volta": {
"node": "18.17.0"
"node": "18.17.1"
},
"nyc": {
"report-dir": "coverage/cypress"

View File

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

22
packages/mermaid/.madgerc Normal file
View File

@@ -0,0 +1,22 @@
{
"detectiveOptions": {
"ts": {
"skipTypeImports": true
},
"es6": {
"skipTypeImports": true
}
},
"fileExtensions": [
"js",
"ts"
],
"excludeRegExp": [
"node_modules",
"docs",
"vitepress",
"detector",
"Detector"
],
"tsConfig": "./tsconfig.json"
}

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.0.0-alpha.2",
"version": "10.3.1",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -38,6 +38,7 @@
"docs:verify-version": "ts-node-esm scripts/update-release-version.mts --verify",
"types:build-config": "ts-node-esm --transpileOnly scripts/create-types-from-json-schema.mts",
"types:verify-config": "ts-node-esm scripts/create-types-from-json-schema.mts --verify",
"checkCircle": "npx madge --circular ./src",
"release": "pnpm build",
"prepublishOnly": "cpy '../../README.*' ./ --cwd=. && pnpm -w run build"
},
@@ -59,6 +60,8 @@
},
"dependencies": {
"@braintree/sanitize-url": "^6.0.1",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
@@ -71,18 +74,20 @@
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"mdast-util-from-markdown": "^1.3.0",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.3",
"ts-dedent": "^2.2.0",
"uuid": "^9.0.0"
"uuid": "^9.0.0",
"web-worker": "^1.2.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^7.1.4",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"@types/cytoscape": "^3.19.9",
"@types/d3": "^7.4.0",
"@types/d3-sankey": "^0.12.1",
"@types/d3-scale": "^4.0.3",
"@types/d3-selection": "^3.0.5",
"@types/d3-shape": "^3.1.1",
"@types/dompurify": "^3.0.2",
"@types/jsdom": "^21.1.1",
"@types/lodash-es": "^4.17.7",
@@ -111,6 +116,7 @@
"remark-gfm": "^3.0.1",
"rimraf": "^5.0.0",
"start-server-and-test": "^2.0.0",
"type-fest": "^4.1.0",
"typedoc": "^0.24.5",
"typedoc-plugin-markdown": "^3.15.2",
"typescript": "^5.0.4",

View File

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

View File

@@ -1,4 +1,4 @@
import { MermaidConfig } from '../config.type.js';
import type { MermaidConfig } from '../config.type.js';
import { log } from '../logger.js';
import type {
DetectorRecord,
@@ -6,14 +6,10 @@ import type {
DiagramLoader,
ExternalDiagramDefinition,
} from './types.js';
import { frontMatterRegex } from './frontmatter.js';
import { getDiagram, registerDiagram } from './diagramAPI.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import { UnknownDiagramError } from '../errors.js';
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
const anyComment = /\s*%%.*\n/gm;
const detectors: Record<string, DetectorRecord> = {};
export const detectors: Record<string, DetectorRecord> = {};
/**
* Detects the type of the graph text.
@@ -38,7 +34,10 @@ const detectors: Record<string, DetectorRecord> = {};
* @returns A graph definition key
*/
export const detectType = function (text: string, config?: MermaidConfig): string {
text = text.replace(frontMatterRegex, '').replace(directive, '').replace(anyComment, '\n');
text = text
.replace(frontMatterRegex, '')
.replace(directiveRegex, '')
.replace(anyCommentRegex, '\n');
for (const [key, { detector }] of Object.entries(detectors)) {
const diagram = detector(text, config);
if (diagram) {
@@ -70,39 +69,6 @@ export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinitio
}
};
export const loadRegisteredDiagrams = async () => {
log.debug(`Loading registered diagrams`);
// Load all lazy loaded diagrams in parallel
const results = await Promise.allSettled(
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
if (loader) {
try {
getDiagram(key);
} catch (error) {
try {
// Register diagram if it is not already registered
const { diagram, id } = await loader();
registerDiagram(id, diagram, detector);
} catch (err) {
// Remove failed diagram from detectors
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
delete detectors[key];
throw err;
}
}
}
})
);
const failed = results.filter((result) => result.status === 'rejected');
if (failed.length > 0) {
log.error(`Failed to load ${failed.length} external diagrams`);
for (const res of failed) {
log.error(res);
}
throw new Error(`Failed to load ${failed.length} external diagrams`);
}
};
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
if (detectors[key]) {
log.error(`Detector with key ${key} already exists`);

View File

@@ -5,7 +5,7 @@ import er from '../diagrams/er/erDetector.js';
import git from '../diagrams/git/gitGraphDetector.js';
import gantt from '../diagrams/gantt/ganttDetector.js';
import { info } from '../diagrams/info/infoDetector.js';
import pie from '../diagrams/pie/pieDetector.js';
import { pie } from '../diagrams/pie/pieDetector.js';
import quadrantChart from '../diagrams/quadrant-chart/quadrantDetector.js';
import requirement from '../diagrams/requirement/requirementDetector.js';
import sequence from '../diagrams/sequence/sequenceDetector.js';

View File

@@ -1,7 +1,7 @@
import { detectType } from './detectType.js';
import { getDiagram, registerDiagram } from './diagramAPI.js';
import { addDiagrams } from './diagram-orchestration.js';
import { DiagramDetector } from './types.js';
import type { DiagramDetector } from './types.js';
import { getDiagramFromText } from '../Diagram.js';
import { it, describe, expect, beforeAll } from 'vitest';

View File

@@ -4,7 +4,7 @@ import { getConfig as _getConfig } from '../config.js';
import { sanitizeText as _sanitizeText } from '../diagrams/common/common.js';
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox.js';
import { addStylesForDiagram } from '../styles.js';
import { DiagramDefinition, DiagramDetector } from './types.js';
import type { DiagramDefinition, DiagramDetector } from './types.js';
import * as _commonDb from '../commonDb.js';
import { parseDirective as _parseDirective } from '../directiveUtils.js';

View File

@@ -1,14 +1,8 @@
import { DiagramDB } from './types.js';
import type { DiagramDB } from './types.js';
import { frontMatterRegex } from './regexes.js';
// The "* as yaml" part is necessary for tree-shaking
import * as yaml from 'js-yaml';
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
// Note that JS doesn't support the "\A" anchor, which means we can't use
// multiline mode.
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
type FrontMatterMetadata = {
title?: string;
// Allows custom display modes. Currently used for compact mode in gantt charts.

View File

@@ -0,0 +1,36 @@
import { log } from '../logger.js';
import { detectors } from './detectType.js';
import { getDiagram, registerDiagram } from './diagramAPI.js';
export const loadRegisteredDiagrams = async () => {
log.debug(`Loading registered diagrams`);
// Load all lazy loaded diagrams in parallel
const results = await Promise.allSettled(
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
if (loader) {
try {
getDiagram(key);
} catch (error) {
try {
// Register diagram if it is not already registered
const { diagram, id } = await loader();
registerDiagram(id, diagram, detector);
} catch (err) {
// Remove failed diagram from detectors
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
delete detectors[key];
throw err;
}
}
}
})
);
const failed = results.filter((result) => result.status === 'rejected');
if (failed.length > 0) {
log.error(`Failed to load ${failed.length} external diagrams`);
for (const res of failed) {
log.error(res);
}
throw new Error(`Failed to load ${failed.length} external diagrams`);
}
};

View File

@@ -0,0 +1,11 @@
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
// Note that JS doesn't support the "\A" anchor, which means we can't use
// multiline mode.
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
export const directiveRegex =
/%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
export const anyCommentRegex = /\s*%%.*\n/gm;

View File

@@ -1,5 +1,5 @@
import { Diagram } from '../Diagram.js';
import type { MermaidConfig } from '../config.type.js';
import type { Diagram } from '../Diagram.js';
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js';
import type * as d3 from 'd3';
export interface InjectUtils {
@@ -16,11 +16,19 @@ export interface InjectUtils {
* Generic Diagram DB that may apply to any diagram type.
*/
export interface DiagramDB {
// config
getConfig?: () => BaseDiagramConfig | undefined;
// db
clear?: () => void;
setDiagramTitle?: (title: string) => void;
setDisplayMode?: (title: string) => void;
getDiagramTitle?: () => string;
setAccTitle?: (title: string) => void;
getAccTitle?: () => string;
setAccDescription?: (describetion: string) => void;
getAccDescription?: () => string;
setDisplayMode?: (title: string) => void;
bindFunctions?: (element: Element) => void;
}

View File

@@ -3,8 +3,8 @@ import c4Parser from './parser/c4Diagram.jison';
import c4Db from './c4Db.js';
import c4Renderer from './c4Renderer.js';
import c4Styles from './styles.js';
import { MermaidConfig } from '../../config.type.js';
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { MermaidConfig } from '../../config.type.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
parser: c4Parser,

View File

@@ -1,5 +1,5 @@
import common from '../common/common.js';
import * as svgDrawCommon from '../common/svgDrawCommon';
import * as svgDrawCommon from '../common/svgDrawCommon.js';
import { sanitizeUrl } from '@braintree/sanitize-url';
export const drawRect = function (elem, rectData) {

View File

@@ -1,5 +1,6 @@
// @ts-nocheck - don't check until handle it
import { select, Selection } from 'd3';
import type { Selection } from 'd3';
import { select } from 'd3';
import { log } from '../../logger.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
@@ -14,7 +15,7 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../../commonDb.js';
import {
import type {
ClassRelation,
ClassNode,
ClassNote,

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/classDiagram.jison';
import db from './classDb.js';

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/classDiagram.jison';
import db from './classDb.js';

View File

@@ -8,7 +8,7 @@ import utils from '../../utils.js';
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import common from '../common/common.js';
import { ClassRelation, ClassNote, ClassMap, EdgeData, NamespaceMap } from './classTypes.js';
import type { ClassRelation, ClassNote, ClassMap, EdgeData, NamespaceMap } from './classTypes.js';
const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());

View File

@@ -1,15 +1,15 @@
import { sanitizeText, removeScript, parseGenericTypes } from './common.js';
describe('when securityLevel is antiscript, all script must be removed', function () {
describe('when securityLevel is antiscript, all script must be removed', () => {
/**
* @param {string} original The original text
* @param {string} result The expected sanitized text
* @param original - The original text
* @param result - The expected sanitized text
*/
function compareRemoveScript(original, result) {
function compareRemoveScript(original: string, result: string) {
expect(removeScript(original).trim()).toEqual(result);
}
it('should remove all script block, script inline.', function () {
it('should remove all script block, script inline.', () => {
const labelString = `1
Act1: Hello 1<script src="http://abc.com/script1.js"></script>1
<b>Act2</b>:
@@ -25,7 +25,7 @@ describe('when securityLevel is antiscript, all script must be removed', functio
compareRemoveScript(labelString, exactlyString);
});
it('should remove all javascript urls', function () {
it('should remove all javascript urls', () => {
compareRemoveScript(
`This is a <a href="javascript:runHijackingScript();">clean link</a> + <a href="javascript:runHijackingScript();">clean link</a>
and <a href="javascript&colon;bipassedMining();">me too</a>`,
@@ -34,11 +34,11 @@ describe('when securityLevel is antiscript, all script must be removed', functio
);
});
it('should detect malicious images', function () {
it('should detect malicious images', () => {
compareRemoveScript(`<img onerror="alert('hello');">`, `<img>`);
});
it('should detect iframes', function () {
it('should detect iframes', () => {
compareRemoveScript(
`<iframe src="http://abc.com/script1.js"></iframe>
<iframe src="http://example.com/iframeexample"></iframe>`,
@@ -47,8 +47,8 @@ describe('when securityLevel is antiscript, all script must be removed', functio
});
});
describe('Sanitize text', function () {
it('should remove script tag', function () {
describe('Sanitize text', () => {
it('should remove script tag', () => {
const maliciousStr = 'javajavascript:script:alert(1)';
const result = sanitizeText(maliciousStr, {
securityLevel: 'strict',
@@ -58,8 +58,8 @@ describe('Sanitize text', function () {
});
});
describe('generic parser', function () {
it('should parse generic types', function () {
describe('generic parser', () => {
it('should parse generic types', () => {
expect(parseGenericTypes('test~T~')).toEqual('test<T>');
expect(parseGenericTypes('test~Array~Array~string~~~')).toEqual('test<Array<Array<string>>>');
expect(parseGenericTypes('test~Array~Array~string[]~~~')).toEqual(

View File

@@ -1,6 +1,7 @@
import DOMPurify from 'dompurify';
import { MermaidConfig } from '../../config.type.js';
import type { MermaidConfig } from '../../config.type.js';
// Remove and ignore br:s
export const lineBreakRegex = /<br\s*\/?>/gi;
/**

View File

@@ -0,0 +1,58 @@
export interface RectData {
x: number;
y: number;
fill: string;
width: number;
height: number;
stroke: string;
class?: string;
color?: string;
rx?: number;
ry?: number;
attrs?: Record<string, string | number>;
anchor?: string;
}
export interface Bound {
startx: number;
stopx: number;
starty: number;
stopy: number;
fill: string;
stroke: string;
}
export interface TextData {
x: number;
y: number;
anchor: string;
text: string;
textMargin: number;
class?: string;
}
export interface TextObject {
x: number;
y: number;
width: number;
height: number;
fill?: string;
anchor?: string;
'text-anchor': string;
style: string;
textMargin: number;
rx: number;
ry: number;
tspan: boolean;
valign?: string;
}
export type D3RectElement = d3.Selection<SVGRectElement, unknown, Element | null, unknown>;
export type D3UseElement = d3.Selection<SVGUseElement, unknown, Element | null, unknown>;
export type D3ImageElement = d3.Selection<SVGImageElement, unknown, Element | null, unknown>;
export type D3TextElement = d3.Selection<SVGTextElement, unknown, Element | null, unknown>;
export type D3TSpanElement = d3.Selection<SVGTSpanElement, unknown, Element | null, unknown>;

View File

@@ -1,114 +0,0 @@
import { sanitizeUrl } from '@braintree/sanitize-url';
export const drawRect = function (elem, rectData) {
const rectElem = elem.append('rect');
rectElem.attr('x', rectData.x);
rectElem.attr('y', rectData.y);
rectElem.attr('fill', rectData.fill);
rectElem.attr('stroke', rectData.stroke);
rectElem.attr('width', rectData.width);
rectElem.attr('height', rectData.height);
rectElem.attr('rx', rectData.rx);
rectElem.attr('ry', rectData.ry);
if (rectData.attrs !== 'undefined' && rectData.attrs !== null) {
for (let attrKey in rectData.attrs) {
rectElem.attr(attrKey, rectData.attrs[attrKey]);
}
}
if (rectData.class !== 'undefined') {
rectElem.attr('class', rectData.class);
}
return rectElem;
};
/**
* Draws a background rectangle
*
* @param {any} elem Diagram (reference for bounds)
* @param {any} bounds Shape of the rectangle
*/
export const drawBackgroundRect = function (elem, bounds) {
const rectElem = drawRect(elem, {
x: bounds.startx,
y: bounds.starty,
width: bounds.stopx - bounds.startx,
height: bounds.stopy - bounds.starty,
fill: bounds.fill,
stroke: bounds.stroke,
class: 'rect',
});
rectElem.lower();
};
export const drawText = function (elem, textData) {
// Remove and ignore br:s
const nText = textData.text.replace(/<br\s*\/?>/gi, ' ');
const textElem = elem.append('text');
textElem.attr('x', textData.x);
textElem.attr('y', textData.y);
textElem.attr('class', 'legend');
textElem.style('text-anchor', textData.anchor);
if (textData.class !== undefined) {
textElem.attr('class', textData.class);
}
const span = textElem.append('tspan');
span.attr('x', textData.x + textData.textMargin * 2);
span.text(nText);
return textElem;
};
export const drawImage = function (elem, x, y, link) {
const imageElem = elem.append('image');
imageElem.attr('x', x);
imageElem.attr('y', y);
var sanitizedLink = sanitizeUrl(link);
imageElem.attr('xlink:href', sanitizedLink);
};
export const drawEmbeddedImage = function (elem, x, y, link) {
const imageElem = elem.append('use');
imageElem.attr('x', x);
imageElem.attr('y', y);
const sanitizedLink = sanitizeUrl(link);
imageElem.attr('xlink:href', '#' + sanitizedLink);
};
export const getNoteRect = function () {
return {
x: 0,
y: 0,
width: 100,
height: 100,
fill: '#EDF2AE',
stroke: '#666',
anchor: 'start',
rx: 0,
ry: 0,
};
};
export const getTextObj = function () {
return {
x: 0,
y: 0,
width: 100,
height: 100,
fill: undefined,
anchor: undefined,
'text-anchor': 'start',
style: '#666',
textMargin: 0,
rx: 0,
ry: 0,
tspan: true,
valign: undefined,
};
};

View File

@@ -0,0 +1,126 @@
import { sanitizeUrl } from '@braintree/sanitize-url';
import type { Group, SVG } from '../../diagram-api/types.js';
import type {
Bound,
D3ImageElement,
D3RectElement,
D3TSpanElement,
D3TextElement,
D3UseElement,
RectData,
TextData,
TextObject,
} from './commonTypes.js';
import { lineBreakRegex } from './common.js';
export const drawRect = (element: SVG | Group, rectData: RectData): D3RectElement => {
const rectElement: D3RectElement = element.append('rect');
rectElement.attr('x', rectData.x);
rectElement.attr('y', rectData.y);
rectElement.attr('fill', rectData.fill);
rectElement.attr('stroke', rectData.stroke);
rectElement.attr('width', rectData.width);
rectElement.attr('height', rectData.height);
rectData.rx !== undefined && rectElement.attr('rx', rectData.rx);
rectData.ry !== undefined && rectElement.attr('ry', rectData.ry);
if (rectData.attrs !== undefined) {
for (const attrKey in rectData.attrs) {
rectElement.attr(attrKey, rectData.attrs[attrKey]);
}
}
rectData.class !== undefined && rectElement.attr('class', rectData.class);
return rectElement;
};
/**
* Draws a background rectangle
*
* @param element - Diagram (reference for bounds)
* @param bounds - Shape of the rectangle
*/
export const drawBackgroundRect = (element: SVG | Group, bounds: Bound): void => {
const rectData: RectData = {
x: bounds.startx,
y: bounds.starty,
width: bounds.stopx - bounds.startx,
height: bounds.stopy - bounds.starty,
fill: bounds.fill,
stroke: bounds.stroke,
class: 'rect',
};
const rectElement: D3RectElement = drawRect(element, rectData);
rectElement.lower();
};
export const drawText = (element: SVG | Group, textData: TextData): D3TextElement => {
const nText: string = textData.text.replace(lineBreakRegex, ' ');
const textElem: D3TextElement = element.append('text');
textElem.attr('x', textData.x);
textElem.attr('y', textData.y);
textElem.attr('class', 'legend');
textElem.style('text-anchor', textData.anchor);
textData.class !== undefined && textElem.attr('class', textData.class);
const tspan: D3TSpanElement = textElem.append('tspan');
tspan.attr('x', textData.x + textData.textMargin * 2);
tspan.text(nText);
return textElem;
};
export const drawImage = (elem: SVG | Group, x: number, y: number, link: string): void => {
const imageElement: D3ImageElement = elem.append('image');
imageElement.attr('x', x);
imageElement.attr('y', y);
const sanitizedLink: string = sanitizeUrl(link);
imageElement.attr('xlink:href', sanitizedLink);
};
export const drawEmbeddedImage = (
element: SVG | Group,
x: number,
y: number,
link: string
): void => {
const imageElement: D3UseElement = element.append('use');
imageElement.attr('x', x);
imageElement.attr('y', y);
const sanitizedLink: string = sanitizeUrl(link);
imageElement.attr('xlink:href', `#${sanitizedLink}`);
};
export const getNoteRect = (): RectData => {
const noteRectData: RectData = {
x: 0,
y: 0,
width: 100,
height: 100,
fill: '#EDF2AE',
stroke: '#666',
anchor: 'start',
rx: 0,
ry: 0,
};
return noteRectData;
};
export const getTextObj = (): TextObject => {
const testObject: TextObject = {
x: 0,
y: 0,
width: 100,
height: 100,
'text-anchor': 'start',
style: '#666',
textMargin: 0,
rx: 0,
ry: 0,
tspan: true,
};
return testObject;
};

View File

@@ -1,4 +1,5 @@
import { findCommonAncestor, TreeData } from './render-utils.js';
import type { TreeData } from './render-utils.js';
import { findCommonAncestor } from './render-utils.js';
describe('when rendering a flowchart using elk ', () => {
let lookupDb: TreeData;
beforeEach(() => {

View File

@@ -3,7 +3,7 @@ import flowParser from './parser/flow.jison';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowStyles from './styles.js';
import { MermaidConfig } from '../../config.type.js';
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../config.js';
export const diagram = {

View File

@@ -4,7 +4,7 @@ import flowDb from './flowDb.js';
import flowRenderer from './flowRenderer.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowStyles from './styles.js';
import { MermaidConfig } from '../../config.type.js';
import type { MermaidConfig } from '../../config.type.js';
export const diagram = {
parser: flowParser,

View File

@@ -3,7 +3,7 @@ import ganttParser from './parser/gantt.jison';
import ganttDb from './ganttDb.js';
import ganttRenderer from './ganttRenderer.js';
import ganttStyles from './styles.js';
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
parser: ganttParser,

View File

@@ -3,7 +3,7 @@ import gitGraphParser from './parser/gitGraph.jison';
import gitGraphDb from './gitGraphAst.js';
import gitGraphRenderer from './gitGraphRenderer.js';
import gitGraphStyles from './styles.js';
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
parser: gitGraphParser,

View File

@@ -1,10 +0,0 @@
name,amounts
Foo, 33
Rishab, 12
Alexis, 41
Tom, 16
Courtney, 59
Christina, 38
Jack, 21
Mickey, 25
Paul, 30
1 name amounts
2 Foo 33
3 Rishab 12
4 Alexis 41
5 Tom 16
6 Courtney 59
7 Christina 38
8 Jack 21
9 Mickey 25
10 Paul 30

View File

@@ -1,132 +0,0 @@
import pieDb from '../pieDb.js';
import pie from './pie.jison';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('when parsing pie', function () {
beforeEach(function () {
pie.parser.yy = pieDb;
pie.parser.yy.clear();
});
it('should handle very simple pie', function () {
const res = pie.parser.parse(`pie
"ash" : 100
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(100);
});
it('should handle simple pie', function () {
const res = pie.parser.parse(`pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with comments', function () {
const res = pie.parser.parse(`pie
%% comments
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with a directive', function () {
const res = pie.parser.parse(`%%{init: {'logLevel':0}}%%
pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60);
});
it('should handle simple pie with a title', function () {
const res = pie.parser.parse(`pie title a 60/40 pie
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a 60/40 pie');
});
it('should handle simple pie without an acc description (accDescr)', function () {
const res = pie.parser.parse(`pie title a neat chart
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const description = pieDb.getAccDescription();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a neat chart');
expect(description).toBe('');
});
it('should handle simple pie with an acc description (accDescr)', function () {
const res = pie.parser.parse(`pie title a neat chart
accDescr: a neat description
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const description = pieDb.getAccDescription();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a neat chart');
expect(description).toBe('a neat description');
});
it('should handle simple pie with a multiline acc description (accDescr)', function () {
const res = pie.parser.parse(`pie title a neat chart
accDescr {
a neat description
on multiple lines
}
"ash" : 60
"bat" : 40
`);
const sections = pieDb.getSections();
const title = pieDb.getDiagramTitle();
const description = pieDb.getAccDescription();
const section1 = sections['ash'];
expect(section1).toBe(60);
expect(title).toBe('a neat chart');
expect(description).toBe('a neat description\non multiple lines');
});
it('should handle simple pie with positive decimal', function () {
const res = pie.parser.parse(`pie
"ash" : 60.67
"bat" : 40
`);
const sections = pieDb.getSections();
const section1 = sections['ash'];
expect(section1).toBe(60.67);
});
it('should handle simple pie with negative decimal', function () {
expect(() => {
pie.parser.parse(`pie
"ash" : 60.67
"bat" : 40..12
`);
}).toThrowError();
});
});

View File

@@ -0,0 +1,180 @@
// @ts-ignore: JISON doesn't support types
import { parser } from './parser/pie.jison';
import { DEFAULT_PIE_DB, db } from './pieDb.js';
import { setConfig } from '../../config.js';
setConfig({
securityLevel: 'strict',
});
describe('pie', () => {
beforeAll(() => {
parser.yy = db;
});
beforeEach(() => {
parser.yy.clear();
});
describe('parse', () => {
it('should handle very simple pie', () => {
parser.parse(`pie
"ash": 100
`);
const sections = db.getSections();
expect(sections['ash']).toBe(100);
});
it('should handle simple pie', () => {
parser.parse(`pie
"ash" : 60
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with showData', () => {
parser.parse(`pie showData
"ash" : 60
"bat" : 40
`);
expect(db.getShowData()).toBeTruthy();
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with comments', () => {
parser.parse(`pie
%% comments
"ash" : 60
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with a directive', () => {
parser.parse(`%%{init: {'logLevel':0}}%%
pie
"ash" : 60
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with a title', () => {
parser.parse(`pie title a 60/40 pie
"ash" : 60
"bat" : 40
`);
expect(db.getDiagramTitle()).toBe('a 60/40 pie');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with an acc title (accTitle)', () => {
parser.parse(`pie title a neat chart
accTitle: a neat acc title
"ash" : 60
"bat" : 40
`);
expect(db.getDiagramTitle()).toBe('a neat chart');
expect(db.getAccTitle()).toBe('a neat acc title');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with an acc description (accDescr)', () => {
parser.parse(`pie title a neat chart
accDescr: a neat description
"ash" : 60
"bat" : 40
`);
expect(db.getDiagramTitle()).toBe('a neat chart');
expect(db.getAccDescription()).toBe('a neat description');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with a multiline acc description (accDescr)', () => {
parser.parse(`pie title a neat chart
accDescr {
a neat description
on multiple lines
}
"ash" : 60
"bat" : 40
`);
expect(db.getDiagramTitle()).toBe('a neat chart');
expect(db.getAccDescription()).toBe('a neat description\non multiple lines');
const sections = db.getSections();
expect(sections['ash']).toBe(60);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with positive decimal', () => {
parser.parse(`pie
"ash" : 60.67
"bat" : 40
`);
const sections = db.getSections();
expect(sections['ash']).toBe(60.67);
expect(sections['bat']).toBe(40);
});
it('should handle simple pie with negative decimal', () => {
expect(() => {
parser.parse(`pie
"ash" : -60.67
"bat" : 40.12
`);
}).toThrowError();
});
});
describe('config', () => {
it.todo('setConfig', () => {
// db.setConfig({ useWidth: 850, useMaxWidth: undefined });
const config = db.getConfig();
expect(config.useWidth).toBe(850);
expect(config.useMaxWidth).toBeTruthy();
});
it('getConfig', () => {
expect(db.getConfig()).toStrictEqual(DEFAULT_PIE_DB.config);
});
it.todo('resetConfig', () => {
// db.setConfig({ textPosition: 0 });
// db.resetConfig();
expect(db.getConfig().textPosition).toStrictEqual(DEFAULT_PIE_DB.config.textPosition);
});
});
});

View File

@@ -1,69 +0,0 @@
import { log } from '../../logger.js';
import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
import {
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
getAccDescription,
setAccDescription,
clear as commonClear,
} from '../../commonDb.js';
let sections = {};
let showData = false;
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
const addSection = function (id, value) {
id = common.sanitizeText(id, configApi.getConfig());
if (sections[id] === undefined) {
sections[id] = value;
log.debug('Added new section :', id);
}
};
const getSections = () => sections;
const setShowData = function (toggle) {
showData = toggle;
};
const getShowData = function () {
return showData;
};
const cleanupValue = function (value) {
if (value.substring(0, 1) === ':') {
value = value.substring(1).trim();
return Number(value.trim());
} else {
return Number(value.trim());
}
};
const clear = function () {
sections = {};
showData = false;
commonClear();
};
export default {
parseDirective,
getConfig: () => configApi.getConfig().pie,
addSection,
getSections,
cleanupValue,
clear,
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
setShowData,
getShowData,
getAccDescription,
setAccDescription,
};

View File

@@ -0,0 +1,84 @@
import { log } from '../../logger.js';
import { parseDirective as _parseDirective } from '../../directiveUtils.js';
import { getConfig as commonGetConfig } from '../../config.js';
import { sanitizeText } from '../common/common.js';
import {
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
getAccDescription,
setAccDescription,
clear as commonClear,
} from '../../commonDb.js';
import type { ParseDirectiveDefinition } from '../../diagram-api/types.js';
import type { PieFields, PieDB, Sections } from './pieTypes.js';
import type { RequiredDeep } from 'type-fest';
import type { PieDiagramConfig } from '../../config.type.js';
import DEFAULT_CONFIG from '../../defaultConfig.js';
export const DEFAULT_PIE_CONFIG: Required<PieDiagramConfig> = DEFAULT_CONFIG.pie;
export const DEFAULT_PIE_DB: RequiredDeep<PieFields> = {
sections: {},
showData: false,
config: DEFAULT_PIE_CONFIG,
} as const;
let sections: Sections = DEFAULT_PIE_DB.sections;
let showData: boolean = DEFAULT_PIE_DB.showData;
const config: Required<PieDiagramConfig> = structuredClone(DEFAULT_PIE_CONFIG);
const getConfig = (): Required<PieDiagramConfig> => structuredClone(config);
const parseDirective: ParseDirectiveDefinition = (statement, context, type) => {
_parseDirective(this, statement, context, type);
};
const clear = (): void => {
sections = structuredClone(DEFAULT_PIE_DB.sections);
showData = DEFAULT_PIE_DB.showData;
commonClear();
};
const addSection = (label: string, value: number): void => {
label = sanitizeText(label, commonGetConfig());
if (sections[label] === undefined) {
sections[label] = value;
log.debug(`added new section: ${label}, with value: ${value}`);
}
};
const getSections = (): Sections => sections;
const cleanupValue = (value: string): number => {
if (value.substring(0, 1) === ':') {
value = value.substring(1).trim();
}
return Number(value.trim());
};
const setShowData = (toggle: boolean): void => {
showData = toggle;
};
const getShowData = (): boolean => showData;
export const db: PieDB = {
getConfig,
parseDirective,
clear,
setDiagramTitle,
getDiagramTitle,
setAccTitle,
getAccTitle,
setAccDescription,
getAccDescription,
addSection,
getSections,
cleanupValue,
setShowData,
getShowData,
};

View File

@@ -15,10 +15,8 @@ const loader: DiagramLoader = async () => {
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
export const pie: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@@ -1,9 +1,9 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/pie.jison';
import db from './pieDb.js';
import styles from './styles.js';
import renderer from './pieRenderer.js';
import { db } from './pieDb.js';
import styles from './pieStyles.js';
import { renderer } from './pieRenderer.js';
export const diagram: DiagramDefinition = {
parser,

View File

@@ -1,204 +0,0 @@
/** Created by AshishJ on 11-09-2019. */
import { select, scaleOrdinal, pie as d3pie, arc } from 'd3';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import * as configApi from '../../config.js';
import { parseFontSize } from '../../utils.js';
let conf = configApi.getConfig();
/**
* Draws a Pie Chart with the data given in text.
*
* @param text
* @param id
*/
let width;
const height = 450;
export const draw = (txt, id, _version, diagObj) => {
try {
conf = configApi.getConfig();
log.debug('Rendering info diagram\n' + txt);
const securityLevel = configApi.getConfig().securityLevel;
// Handle root and Document for when rendering in sandbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
// Parse the Pie Chart definition
const elem = doc.getElementById(id);
width = elem.parentElement.offsetWidth;
if (width === undefined) {
width = 1200;
}
if (conf.useWidth !== undefined) {
width = conf.useWidth;
}
if (conf.pie.useWidth !== undefined) {
width = conf.pie.useWidth;
}
const diagram = root.select('#' + id);
configureSvgSize(diagram, height, width, conf.pie.useMaxWidth);
// Set viewBox
elem.setAttribute('viewBox', '0 0 ' + width + ' ' + height);
// Fetch the default direction, use TD if none was found
var margin = 40;
var legendRectSize = 18;
var legendSpacing = 4;
var radius = Math.min(width, height) / 2 - margin;
var svg = diagram
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var data = diagObj.db.getSections();
var sum = 0;
Object.keys(data).forEach(function (key) {
sum += data[key];
});
const themeVariables = conf.themeVariables;
var myGeneratedColors = [
themeVariables.pie1,
themeVariables.pie2,
themeVariables.pie3,
themeVariables.pie4,
themeVariables.pie5,
themeVariables.pie6,
themeVariables.pie7,
themeVariables.pie8,
themeVariables.pie9,
themeVariables.pie10,
themeVariables.pie11,
themeVariables.pie12,
];
const textPosition = conf.pie?.textPosition ?? 0.75;
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
outerStrokeWidth ??= 2;
// Set the color scale
var color = scaleOrdinal().range(myGeneratedColors);
// Compute the position of each group on the pie:
var pieData = Object.entries(data).map(function (el, idx) {
return {
order: idx,
name: el[0],
value: el[1],
};
});
var pie = d3pie()
.value(function (d) {
return d.value;
})
.sort(function (a, b) {
// Sort slices in clockwise direction
return a.order - b.order;
});
var dataReady = pie(pieData);
// Shape helper to build arcs:
var arcGenerator = arc().innerRadius(0).outerRadius(radius);
var labelArcGenerator = arc()
.innerRadius(radius * textPosition)
.outerRadius(radius * textPosition);
svg
.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', radius + outerStrokeWidth / 2)
.attr('class', 'pieOuterCircle');
// Build the pie chart: each part of the pie is a path that we build using the arc function.
svg
.selectAll('mySlices')
.data(dataReady)
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', function (d) {
return color(d.data.name);
})
.attr('class', 'pieCircle');
// Now add the percentage.
// Use the centroid method to get the best coordinates.
svg
.selectAll('mySlices')
.data(dataReady)
.enter()
.append('text')
.text(function (d) {
return ((d.data.value / sum) * 100).toFixed(0) + '%';
})
.attr('transform', function (d) {
return 'translate(' + labelArcGenerator.centroid(d) + ')';
})
.style('text-anchor', 'middle')
.attr('class', 'slice');
svg
.append('text')
.text(diagObj.db.getDiagramTitle())
.attr('x', 0)
.attr('y', -(height - 50) / 2)
.attr('class', 'pieTitleText');
// Add the legends/annotations for each section
var legend = svg
.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function (d, i) {
const height = legendRectSize + legendSpacing;
const offset = (height * color.domain().length) / 2;
const horizontal = 12 * legendRectSize;
const vertical = i * height - offset;
return 'translate(' + horizontal + ',' + vertical + ')';
});
legend
.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend
.data(dataReady)
.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function (d) {
if (diagObj.db.getShowData() || conf.showData || conf.pie.showData) {
return d.data.name + ' [' + d.data.value + ']';
} else {
return d.data.name;
}
});
} catch (e) {
log.error('Error while rendering info diagram');
log.error(e);
}
};
export default {
draw,
};

View File

@@ -0,0 +1,180 @@
import type d3 from 'd3';
import { scaleOrdinal, pie as d3pie, arc } from 'd3';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import { getConfig } from '../../config.js';
import { cleanAndMerge, parseFontSize } from '../../utils.js';
import type { DrawDefinition, Group, SVG } from '../../diagram-api/types.js';
import type { D3Sections, PieDB, Sections } from './pieTypes.js';
import type { MermaidConfig, PieDiagramConfig } from '../../config.type.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
const createPieArcs = (sections: Sections): d3.PieArcDatum<D3Sections>[] => {
// Compute the position of each group on the pie:
const pieData: D3Sections[] = Object.entries(sections).map(
(element: [string, number]): D3Sections => {
return {
label: element[0],
value: element[1],
};
}
);
const pie: d3.Pie<unknown, D3Sections> = d3pie<D3Sections>().value(
(d3Section: D3Sections): number => d3Section.value
);
return pie(pieData);
};
/**
* Draws a Pie Chart with the data given in text.
*
* @param text - pie chart code
* @param id - diagram id
* @param _version - MermaidJS version from package.json.
* @param diagObj - A standard diagram containing the DB and the text and type etc of the diagram.
*/
export const draw: DrawDefinition = (text, id, _version, diagObj) => {
log.debug('rendering pie chart\n' + text);
const db = diagObj.db as PieDB;
const globalConfig: MermaidConfig = getConfig();
const pieConfig: Required<PieDiagramConfig> = cleanAndMerge(db.getConfig(), globalConfig.pie);
const height = 450;
// TODO: remove document width
const width: number =
document.getElementById(id)?.parentElement?.offsetWidth ?? pieConfig.useWidth;
const svg: SVG = selectSvgElement(id);
// Set viewBox
svg.attr('viewBox', `0 0 ${width} ${height}`);
configureSvgSize(svg, height, width, pieConfig.useMaxWidth);
const MARGIN = 40;
const LEGEND_RECT_SIZE = 18;
const LEGEND_SPACING = 4;
const group: Group = svg.append('g');
group.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
const { themeVariables } = globalConfig;
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
outerStrokeWidth ??= 2;
const textPosition: number = pieConfig.textPosition;
const radius: number = Math.min(width, height) / 2 - MARGIN;
// Shape helper to build arcs:
const arcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Sections>> = arc<
d3.PieArcDatum<D3Sections>
>()
.innerRadius(0)
.outerRadius(radius);
const labelArcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Sections>> = arc<
d3.PieArcDatum<D3Sections>
>()
.innerRadius(radius * textPosition)
.outerRadius(radius * textPosition);
group
.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', radius + outerStrokeWidth / 2)
.attr('class', 'pieOuterCircle');
const sections: Sections = db.getSections();
const arcs: d3.PieArcDatum<D3Sections>[] = createPieArcs(sections);
const myGeneratedColors = [
themeVariables.pie1,
themeVariables.pie2,
themeVariables.pie3,
themeVariables.pie4,
themeVariables.pie5,
themeVariables.pie6,
themeVariables.pie7,
themeVariables.pie8,
themeVariables.pie9,
themeVariables.pie10,
themeVariables.pie11,
themeVariables.pie12,
];
// Set the color scale
const color: d3.ScaleOrdinal<string, 12, never> = scaleOrdinal(myGeneratedColors);
// Build the pie chart: each part of the pie is a path that we build using the arc function.
group
.selectAll('mySlices')
.data(arcs)
.enter()
.append('path')
.attr('d', arcGenerator)
.attr('fill', (datum: d3.PieArcDatum<D3Sections>) => {
return color(datum.data.label);
})
.attr('class', 'pieCircle');
let sum = 0;
Object.keys(sections).forEach((key: string): void => {
sum += sections[key];
});
// Now add the percentage.
// Use the centroid method to get the best coordinates.
group
.selectAll('mySlices')
.data(arcs)
.enter()
.append('text')
.text((datum: d3.PieArcDatum<D3Sections>): string => {
return ((datum.data.value / sum) * 100).toFixed(0) + '%';
})
.attr('transform', (datum: d3.PieArcDatum<D3Sections>): string => {
return 'translate(' + labelArcGenerator.centroid(datum) + ')';
})
.style('text-anchor', 'middle')
.attr('class', 'slice');
group
.append('text')
.text(db.getDiagramTitle())
.attr('x', 0)
.attr('y', -(height - 50) / 2)
.attr('class', 'pieTitleText');
// Add the legends/annotations for each section
const legend = group
.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', (_datum, index: number): string => {
const height = LEGEND_RECT_SIZE + LEGEND_SPACING;
const offset = (height * color.domain().length) / 2;
const horizontal = 12 * LEGEND_RECT_SIZE;
const vertical = index * height - offset;
return 'translate(' + horizontal + ',' + vertical + ')';
});
legend
.append('rect')
.attr('width', LEGEND_RECT_SIZE)
.attr('height', LEGEND_RECT_SIZE)
.style('fill', color)
.style('stroke', color);
legend
.data(arcs)
.append('text')
.attr('x', LEGEND_RECT_SIZE + LEGEND_SPACING)
.attr('y', LEGEND_RECT_SIZE - LEGEND_SPACING)
.text((datum: d3.PieArcDatum<D3Sections>): string => {
const { label, value } = datum.data;
if (db.getShowData()) {
return `${label} [${value}]`;
}
return label;
});
};
export const renderer = { draw };

View File

@@ -1,4 +1,7 @@
const getStyles = (options) =>
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
import type { PieStyleOptions } from './pieTypes.js';
const getStyles: DiagramStylesProvider = (options: PieStyleOptions) =>
`
.pieCircle{
stroke: ${options.pieStrokeColor};

View File

@@ -0,0 +1,64 @@
import type { PieDiagramConfig } from '../../config.type.js';
import type { DiagramDB, ParseDirectiveDefinition } from '../../diagram-api/types.js';
export interface PieFields {
sections: Sections;
showData: boolean;
config: PieDiagramConfig;
}
export interface PieStyleOptions {
fontFamily: string;
pie1: string;
pie2: string;
pie3: string;
pie4: string;
pie5: string;
pie6: string;
pie7: string;
pie8: string;
pie9: string;
pie10: string;
pie11: string;
pie12: string;
pieTitleTextSize: string;
pieTitleTextColor: string;
pieSectionTextSize: string;
pieSectionTextColor: string;
pieLegendTextSize: string;
pieLegendTextColor: string;
pieStrokeColor: string;
pieStrokeWidth: string;
pieOuterStrokeWidth: string;
pieOuterStrokeColor: string;
pieOpacity: string;
}
export type Sections = Record<string, number>;
export interface D3Sections {
label: string;
value: number;
}
export interface PieDB extends DiagramDB {
// config
getConfig: () => Required<PieDiagramConfig>;
// common db
parseDirective: ParseDirectiveDefinition;
clear: () => void;
setDiagramTitle: (title: string) => void;
getDiagramTitle: () => string;
setAccTitle: (title: string) => void;
getAccTitle: () => string;
setAccDescription: (describetion: string) => void;
getAccDescription: () => string;
// diagram db
addSection: (label: string, value: number) => void;
getSections: () => Sections;
cleanupValue: (value: string) => number;
setShowData: (toggle: boolean) => void;
getShowData: () => boolean;
}

View File

@@ -1,6 +1,7 @@
// @ts-ignore: JISON doesn't support types
import { parser } from './quadrant.jison';
import { Mock, vi } from 'vitest';
import type { Mock } from 'vitest';
import { vi } from 'vitest';
const parserFnConstructor = (str: string) => {
return () => {

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/quadrant.jison';
import db from './quadrantDb.js';

View File

@@ -3,8 +3,8 @@ import { select } from 'd3';
import * as configApi from '../../config.js';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import { Diagram } from '../../Diagram.js';
import {
import type { Diagram } from '../../Diagram.js';
import type {
QuadrantBuildType,
QuadrantLineType,
QuadrantPointType,

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/requirementDiagram.jison';
import db from './requirementDb.js';

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: jison doesn't export types
import parser from './parser/sankey.jison';
import db from './sankeyDB.js';

View File

@@ -1,4 +1,4 @@
import { Diagram } from '../../Diagram.js';
import type { Diagram } from '../../Diagram.js';
import * as configApi from '../../config.js';
import {
@@ -7,6 +7,7 @@ import {
schemeTableau10 as d3schemeTableau10,
} from 'd3';
import type { SankeyNode as d3SankeyNode } from 'd3-sankey';
import {
sankey as d3Sankey,
sankeyLinkHorizontal as d3SankeyLinkHorizontal,
@@ -14,7 +15,6 @@ import {
sankeyRight as d3SankeyRight,
sankeyCenter as d3SankeyCenter,
sankeyJustify as d3SankeyJustify,
SankeyNode as d3SankeyNode,
} from 'd3-sankey';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import { Uid } from '../../rendering-util/uid.js';

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/sequenceDiagram.jison';
import db from './sequenceDb.js';

View File

@@ -3,12 +3,12 @@ import { select, selectAll } from 'd3';
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
import { log } from '../../logger.js';
import common from '../common/common.js';
import * as svgDrawCommon from '../common/svgDrawCommon';
import * as svgDrawCommon from '../common/svgDrawCommon.js';
import * as configApi from '../../config.js';
import assignWithDepth from '../../assignWithDepth.js';
import utils from '../../utils.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import { Diagram } from '../../Diagram.js';
import type { Diagram } from '../../Diagram.js';
let conf = {};

View File

@@ -1,5 +1,5 @@
import common from '../common/common.js';
import * as svgDrawCommon from '../common/svgDrawCommon';
import * as svgDrawCommon from '../common/svgDrawCommon.js';
import { addFunction } from '../../interactionDb.js';
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
import { sanitizeUrl } from '@braintree/sanitize-url';

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/stateDiagram.jison';
import db from './stateDb.js';

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/stateDiagram.jison';
import db from './stateDb.js';

View File

@@ -1,11 +1,12 @@
// @ts-nocheck - don't check until handle it
import { select, Selection } from 'd3';
import type { Selection } from 'd3';
import { select } from 'd3';
import svgDraw from './svgDraw.js';
import { log } from '../../logger.js';
import { getConfig } from '../../config.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import { Diagram } from '../../Diagram.js';
import { MermaidConfig } from '../../config.type.js';
import type { Diagram } from '../../Diagram.js';
import type { MermaidConfig } from '../../config.type.js';
interface Block<TDesc, TSection> {
number: number;

View File

@@ -1,4 +1,4 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/journey.jison';
import db from './journeyDb.js';

View File

@@ -1,5 +1,5 @@
import { arc as d3arc } from 'd3';
import * as svgDrawCommon from '../common/svgDrawCommon';
import * as svgDrawCommon from '../common/svgDrawCommon.js';
export const drawRect = function (elem, rectData) {
return svgDrawCommon.drawRect(elem, rectData);

View File

@@ -101,7 +101,7 @@ function sidebarAll() {
return [
{
text: '📔 Introduction',
collapsible: true,
collapsed: false,
items: [
{ text: 'About Mermaid', link: '/intro/' },
{ text: 'Deployment', link: '/intro/n00b-gettingStarted' },
@@ -123,7 +123,7 @@ function sidebarSyntax() {
return [
{
text: '📊 Diagram Syntax',
collapsible: true,
collapsed: false,
items: [
{ text: 'Flowchart', link: '/syntax/flowchart' },
{ text: 'Sequence Diagram', link: '/syntax/sequenceDiagram' },
@@ -154,7 +154,7 @@ function sidebarConfig() {
return [
{
text: '⚙️ Deployment and Configuration',
collapsible: true,
collapsed: false,
items: [
{ text: 'Configuration', link: '/config/configuration' },
{ text: 'Tutorials', link: '/config/Tutorials' },
@@ -176,7 +176,7 @@ function sidebarEcosystem() {
return [
{
text: '📚 Ecosystem',
collapsible: true,
collapsed: false,
items: [
{ text: 'Showcases', link: '/ecosystem/showcases' },
{ text: 'Use-Cases and Integrations', link: '/ecosystem/integrations' },
@@ -189,7 +189,7 @@ function sidebarCommunity() {
return [
{
text: '🙌 Contributions and Community',
collapsible: true,
collapsed: false,
items: [
{ text: 'Overview for Beginners', link: '/community/n00b-overview' },
...sidebarCommunityDevelopContribute(),
@@ -207,7 +207,7 @@ function sidebarCommunityDevelopContribute() {
{
text: 'Contributing to Mermaid',
link: page_path + '#contributing-to-mermaid',
collapsible: true,
collapsed: false,
items: [
{
text: 'Technical Requirements and Setup',
@@ -238,7 +238,7 @@ function sidebarNews() {
return [
{
text: '📰 Latest News',
collapsible: true,
collapsed: false,
items: [
{ text: 'Announcements', link: '/news/announcements' },
{ text: 'Blog', link: '/news/blog' },

View File

@@ -24,6 +24,9 @@ const getBaseFile = (url: URL): Redirect => {
return { path, id };
};
/**
* Used to redirect old documentation pages to corresponding new pages.
*/
const idRedirectMap: Record<string, string> = {
'8.6.0_docs': '',
accessibility: 'config/theming',
@@ -68,6 +71,9 @@ const idRedirectMap: Record<string, string> = {
'user-journey': 'syntax/userJourney',
};
/**
* Used to redirect pages that have been moved in the vitepress site.
*/
const urlRedirectMap: Record<string, string> = {
'/misc/faq.html': 'configure/faq.html',
'/syntax/c4c.html': 'syntax/c4.html',

View File

@@ -8,8 +8,12 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
<img src="/header.png" alt="" />
<div class='badges'>
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
</div>
<!-- Mermaid book banner -->
[![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!](img/book-banner-post-release.jpg)](https://mermaid-js.github.io/mermaid/landing/)
@@ -166,8 +170,12 @@ The above command generates files into the `dist` folder and publishes them to <
## Contributors
<div class='badges'>
[![Good first issue](https://img.shields.io/github/labels/mermaid-js/mermaid/Good%20first%20issue%21)](https://github.com/mermaid-js/mermaid/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%21%22) [![Contributors](https://img.shields.io/github/contributors/mermaid-js/mermaid)](https://github.com/mermaid-js/mermaid/graphs/contributors) [![Commits](https://img.shields.io/github/commit-activity/m/mermaid-js/mermaid)](https://github.com/mermaid-js/mermaid/graphs/contributors)
</div>
Mermaid is a growing community and is always accepting new contributors. There's a lot of different ways to help out and we're always looking for extra hands! Look at [this issue](https://github.com/mermaid-js/mermaid/issues/866) if you want to know where to start helping out.
Detailed information about how to contribute can be found in the [contribution guide](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
@@ -201,20 +209,14 @@ A quick note from Knut Sveidqvist:
_Mermaid was created by Knut Sveidqvist for easier documentation._
<style scoped>
#contributors + p,
#about-mermaid + p + p + blockquote + img + p
{
display: flex
.badges > p {
display: flex;
}
.badges > p > a {
margin: 0 0.5rem;
}
#contributors + p a,
#about-mermaid + p + p + blockquote + img + p a
{
margin: 0 0.5rem
}
.dark #VPContent > div > div > div.content > div > main > div > div > img
{
.dark #VPContent > div > div > div.content > div > main > div > div > img {
filter: invert(1) hue-rotate(217deg) contrast(0.72);
}
</style>

View File

@@ -17,21 +17,22 @@
"dependencies": {
"@vueuse/core": "^10.1.0",
"jiti": "^1.18.2",
"vue": "^3.2.47"
"vue": "^3.3",
"mermaid": "workspace:^"
},
"devDependencies": {
"@iconify-json/carbon": "^1.1.16",
"@unocss/reset": "^0.54.0",
"@unocss/reset": "^0.55.2",
"@vite-pwa/vitepress": "^0.2.0",
"@vitejs/plugin-vue": "^4.2.1",
"fast-glob": "^3.2.12",
"https-localhost": "^4.7.1",
"pathe": "^1.1.0",
"unocss": "^0.54.0",
"unocss": "^0.55.2",
"unplugin-vue-components": "^0.25.0",
"vite": "^4.3.9",
"vite-plugin-pwa": "^0.16.0",
"vitepress": "1.0.0-beta.7",
"vitepress": "1.0.0-rc.4",
"workbox-window": "^7.0.0"
}
}

View File

@@ -471,6 +471,29 @@ flowchart LR
B1 --> B2
```
#### Limitation
If any of a subgraph's nodes are linked to the outside, subgraph direction will be ignored. Instead the subgraph will inherit the direction of the parent graph:
```mermaid-example
flowchart LR
subgraph subgraph1
direction TB
top1[top] --> bottom1[bottom]
end
subgraph subgraph2
direction TB
top2[top] --> bottom2[bottom]
end
%% ^ These subgraphs are identical, except for the links to them:
%% Link *to* subgraph1: subgraph1 direction is mantained
outside --> subgraph1
%% Link *within* subgraph2:
%% subgraph2 inherits the direction of the top-level graph (LR)
outside ---> top2
```
## Markdown Strings
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.

View File

@@ -3,20 +3,18 @@
* functionality and to render the diagrams to svg code!
*/
import { dedent } from 'ts-dedent';
import { MermaidConfig } from './config.type.js';
import type { MermaidConfig } from './config.type.js';
import { log } from './logger.js';
import utils from './utils.js';
import { mermaidAPI, ParseOptions, RenderResult } from './mermaidAPI.js';
import {
registerLazyLoadedDiagrams,
loadRegisteredDiagrams,
detectType,
} from './diagram-api/detectType.js';
import type { ParseOptions, RenderResult } from './mermaidAPI.js';
import { mermaidAPI } from './mermaidAPI.js';
import { registerLazyLoadedDiagrams, detectType } from './diagram-api/detectType.js';
import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js';
import type { ParseErrorFunction } from './Diagram.js';
import { isDetailedError } from './utils.js';
import type { DetailedError } from './utils.js';
import { ExternalDiagramDefinition } from './diagram-api/types.js';
import { UnknownDiagramError } from './errors.js';
import type { ExternalDiagramDefinition } from './diagram-api/types.js';
import type { UnknownDiagramError } from './errors.js';
export type {
MermaidConfig,

View File

@@ -34,7 +34,7 @@ vi.mock('./diagrams/state/stateRenderer-v2.js');
// -------------------------------------
import mermaid from './mermaid.js';
import { MermaidConfig } from './config.type.js';
import type { MermaidConfig } from './config.type.js';
import mermaidAPI, { removeExistingElements } from './mermaidAPI.js';
import {

View File

@@ -25,7 +25,7 @@ import getStyles from './styles.js';
import theme from './themes/index.js';
import utils, { directiveSanitizer } from './utils.js';
import DOMPurify from 'dompurify';
import { MermaidConfig } from './config.type.js';
import type { MermaidConfig } from './config.type.js';
import { evaluate } from './diagrams/common/common.js';
import isEmpty from 'lodash-es/isEmpty.js';
import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.js';

View File

@@ -4,7 +4,7 @@ import { log } from '../logger.js';
import { decodeEntities } from '../mermaidAPI.js';
import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text.js';
import { splitLineToFitWidth } from './splitText.js';
import { MarkdownLine, MarkdownWord } from './types.js';
import type { MarkdownLine, MarkdownWord } from './types.js';
function applyStyle(dom, styleFn) {
if (styleFn) {

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