Compare commits

..

6 Commits

Author SHA1 Message Date
Sidharth Vinod
ea7d28bf1c chore: Rename types to MermaidConfig and MermaidConfigWithDefaults 2024-05-22 23:56:30 +05:30
Sidharth Vinod
159d85e7f7 Merge branch 'develop' into sidv/ConfigType
* develop:
  chore: remove unrefSubSchemas
2024-05-13 12:16:05 +05:30
Sidharth Vinod
d7ce7aecf3 chore: Change where PartialMermaidConfig is defined 2024-05-13 10:54:42 +05:30
Sidharth Vinod
453802d4ce chore: Fix types of MermaidConfig 2024-05-13 09:59:01 +05:30
Sidharth Vinod
cee1cf0ce2 feat: Remove forced optionality of fields inside MermaidConfig 2024-05-13 09:56:50 +05:30
Sidharth Vinod
70198ffefa chore: Update json-schema-to-typescript 2024-05-13 09:19:56 +05:30
169 changed files with 11383 additions and 15302 deletions

View File

@@ -1,7 +1,10 @@
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';
import type {
MermaidConfigWithDefaults,
BaseDiagramConfig,
} from '../packages/mermaid/src/config.type.js';
/**
* All of the keys in the mermaid config that have a mermaid diagram config.
@@ -36,7 +39,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
* @param mermaidConfigSchema - The Mermaid JSON Schema to use.
* @returns The default mermaid config object.
*/
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfigWithDefaults>) {
const ajv = new Ajv2019({
useDefaults: true,
allowUnionTypes: true,
@@ -105,20 +108,23 @@ function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
return mermaidDefaultConfig;
}
export const loadSchema = (src: string, filename: string): JSONSchemaType<MermaidConfig> => {
export const loadSchema = (
src: string,
filename: string
): JSONSchemaType<MermaidConfigWithDefaults> => {
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>;
}) as JSONSchemaType<MermaidConfigWithDefaults>;
return jsonSchema;
};
export const getDefaults = (schema: JSONSchemaType<MermaidConfig>) => {
export const getDefaults = (schema: JSONSchemaType<MermaidConfigWithDefaults>) => {
return `export default ${JSON.stringify(generateDefaults(schema), undefined, 2)};`;
};
export const getSchema = (schema: JSONSchemaType<MermaidConfig>) => {
export const getSchema = (schema: JSONSchemaType<MermaidConfigWithDefaults>) => {
return `export default ${JSON.stringify(schema, undefined, 2)};`;
};

View File

@@ -83,6 +83,7 @@ NODIR
NSTR
outdir
Qcontrolx
regexes
reinit
rels
reqs

View File

@@ -2,7 +2,7 @@ import { build } from 'esbuild';
import { mkdir, writeFile } from 'node:fs/promises';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
import { type MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
const shouldVisualize = process.argv.includes('--visualize');

View File

@@ -1,6 +1,6 @@
import { readFile } from 'node:fs/promises';
import { transformJison } from '../.build/jisonTransformer.js';
import type { Plugin } from 'esbuild';
import { Plugin } from 'esbuild';
export const jisonPlugin: Plugin = {
name: 'jison',

View File

@@ -1,5 +1,5 @@
import type { JSONSchemaType } from 'ajv/dist/2019.js';
import type { MermaidConfig } from '../packages/mermaid/src/config.type.js';
import type { MermaidConfigWithDefaults } from '../packages/mermaid/src/config.type.js';
import { readFile } from 'node:fs/promises';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
@@ -12,13 +12,13 @@ import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
export const jsonSchemaPlugin = {
name: 'json-schema-plugin',
setup(build) {
let schema: JSONSchemaType<MermaidConfig> | undefined = undefined;
let schema: JSONSchemaType<MermaidConfigWithDefaults> | 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> =
const resolvedSchema: JSONSchemaType<MermaidConfigWithDefaults> =
content === source && schema ? schema : loadSchema(source, args.path);
if (content !== source) {
content = source;

View File

@@ -56,7 +56,7 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
const outFileName = getFileName(name, options);
const output: BuildOptions = buildOptions({
let output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[outFileName]: `src/${file}`,

5
.github/lychee.toml vendored
View File

@@ -41,10 +41,7 @@ exclude = [
"https://bundlephobia.com",
# Chrome webstore migration issue. Temporary
"https://chromewebstore.google.com",
# Drupal 403
"https://(www.)?drupal.org"
"https://chromewebstore.google.com"
]
# Exclude all private IPs from checking.

View File

@@ -1,3 +1,9 @@
# We use github cache to save snapshots between runs.
# For PRs and MergeQueues, the target commit is used, and for push events, github.event.previous is used.
# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
# These are then downloaded before running the E2E, providing the reference snapshots.
# If there are any errors, the diff image is uploaded to artifacts, and the user is notified.
name: E2E
on:
@@ -66,6 +72,16 @@ jobs:
mkdir -p cypress/snapshots/stats/base
mv stats cypress/snapshots/stats/base
- name: Cypress run
uses: cypress-io/github-action@v6
id: cypress-snapshot-gen
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
with:
install: false
start: pnpm run dev
wait-on: 'http://localhost:9000'
browser: chrome
e2e:
runs-on: ubuntu-latest
container:
@@ -130,10 +146,6 @@ jobs:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_PARALLEL: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
ARGOS_PARALLEL_TOTAL: 4
ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
@@ -146,3 +158,55 @@ jobs:
fail_ci_if_error: false
verbose: true
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
# We upload the artifacts into numbered archives to prevent overwriting
- name: Upload Artifacts
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: snapshots-${{ matrix.containers }}
retention-days: 1
path: ./cypress/snapshots
combineArtifacts:
needs: e2e
runs-on: ubuntu-latest
if: ${{ always() }}
steps:
# Download all snapshot artifacts and merge them into a single folder
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: snapshots
pattern: snapshots-*
merge-multiple: true
# For successful push events, we save the snapshots cache
- name: Save snapshots cache
id: cache-upload
if: ${{ github.event_name == 'push' && needs.e2e.result != 'failure' }}
uses: actions/cache/save@v4
with:
path: ./snapshots
key: ${{ runner.os }}-snapshots-${{ github.event.after }}
- name: Flatten images to a folder
if: ${{ needs.e2e.result == 'failure' }}
run: |
mkdir errors
cd snapshots
find . -mindepth 2 -type d -name "*__diff_output__*" -exec sh -c 'mv "$0"/*.png ../errors/' {} \;
- name: Upload Error snapshots
if: ${{ needs.e2e.result == 'failure' }}
uses: actions/upload-artifact@v4
id: upload-artifacts
with:
name: error-snapshots
retention-days: 10
path: errors/
- name: Notify Users
if: ${{ needs.e2e.result == 'failure' }}
run: |
echo "::error title=Visual tests failed::You can view images that failed by downloading the error-snapshots artifact: ${{ steps.upload-artifacts.outputs.artifact-url }}"

View File

@@ -37,9 +37,9 @@ jobs:
- name: Run Linting
shell: bash
run: |
if ! pnpm run lint:ci; then
if ! pnpm run lint; then
# print a nice error message on lint failure
ERROR_MESSAGE='Running `pnpm run lint:ci` failed.'
ERROR_MESSAGE='Running `pnpm run lint` failed.'
ERROR_MESSAGE+=' Running `pnpm -w run lint:fix` may fix this issue. '
ERROR_MESSAGE+=" If this error doesn't occur on your local machine,"
ERROR_MESSAGE+=' make sure your packages are up-to-date by running `pnpm install`.'

View File

@@ -1,6 +1,10 @@
export default {
'!(docs/**/*)*.{ts,js,html,md,mts}': [
'biome check --no-errors-on-unmatched --files-ignore-unknown=true --write',
'eslint --cache --cache-strategy content --fix',
// don't cache prettier yet, since we use `prettier-plugin-jsdoc`,
// and prettier doesn't invalidate cache on plugin updates"
// https://prettier.io/docs/en/cli.html#--cache
'prettier --write',
],
'.cspell/*.txt': ['tsx scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'],

18
.prettierignore Normal file
View File

@@ -0,0 +1,18 @@
dist
cypress/platform/xss3.html
.cache
.pnpm-store
coverage
# Autogenerated by PNPM
pnpm-lock.yaml
stats
**/.vitepress/components.d.ts
**/.vitepress/cache
.nyc_output
# Autogenerated by `pnpm run --filter mermaid types:build-config`
packages/mermaid/src/config.type.ts
# autogenereated by langium-cli
generated/
# Ignore the files creates in /demos/dev except for example.html
demos/dev/**
!/demos/dev/example.html

8
.prettierrc.json Normal file
View File

@@ -0,0 +1,8 @@
{
"endOfLine": "auto",
"printWidth": 100,
"singleQuote": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -1,4 +1,4 @@
import { build, type InlineConfig, type PluginOption } from 'vite';
import { build, InlineConfig, type PluginOption } from 'vite';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js';
@@ -48,7 +48,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
const external: (string | RegExp)[] = ['require', 'fs', 'path'];
console.log(entryName, packageOptions[entryName]);
const { name, file, packageName } = packageOptions[entryName];
const output: OutputOptions = [
let output: OutputOptions = [
{
name,
format: 'esm',

View File

@@ -1,4 +1,4 @@
import type { PluginOption } from 'vite';
import { PluginOption } from 'vite';
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
/**

View File

@@ -1,7 +0,0 @@
{
"drips": {
"ethereum": {
"ownedBy": "0x0831DDFe60d009d9448CC976157b539089aB821E"
}
}
}

View File

@@ -35,7 +35,6 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
[![NPM Downloads](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid)
[![Join our Discord!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=discord&label=discord)](https://discord.gg/AgrbSrBer3)
[![Twitter Follow](https://img.shields.io/badge/Social-mermaidjs__-blue?style=social&logo=X)](https://twitter.com/mermaidjs_)
[![Covered by Argos Visual Testing](https://argos-ci.com/badge.svg)](https://argos-ci.com)
<img src="./img/header.png" alt="" />

View File

@@ -1,229 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.2/schema.json",
"files": {
"ignore": [
"**/contributor-names.json",
"**/generated/",
"**/knsv*.html",
"**/local*.html",
"**/stats/",
"**/user-avatars/*",
"./.vscode/**",
"cypress/platform/current.html",
"cypress/platform/experimental.html",
"cypress/platform/xss3.html",
"cypress/screenshots/",
"cypress/snapshots/",
"demos/dev/**",
"packages/mermaid/src/config.type.ts"
]
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 100,
"attributePosition": "auto"
},
"organizeImports": { "enabled": false },
"linter": {
"enabled": true,
"rules": {
"recommended": false,
"complexity": {
"noBannedTypes": "error",
"noExtraBooleanCast": "error",
"noMultipleSpacesInRegularExpressionLiterals": "error",
"noUselessCatch": "error",
"noUselessThisAlias": "error",
"noUselessTypeConstraint": "error",
"noWith": "error",
"useFlatMap": "error"
},
"correctness": {
"noConstAssign": "error",
"noConstantCondition": "error",
"noEmptyCharacterClassInRegex": "error",
"noEmptyPattern": "error",
"noGlobalObjectCalls": "error",
"noInnerDeclarations": "error",
"noInvalidConstructorSuper": "error",
"noNewSymbol": "error",
"noNonoctalDecimalEscape": "error",
"noPrecisionLoss": "error",
"noSelfAssign": "error",
"noSetterReturn": "error",
"noSwitchDeclarations": "error",
"noUndeclaredVariables": "error",
"noUnreachable": "error",
"noUnreachableSuper": "error",
"noUnsafeFinally": "error",
"noUnsafeOptionalChaining": "error",
"noUnusedLabels": "error",
"noUnusedVariables": "off",
"useArrayLiterals": "off",
"useIsNan": "error",
"useValidForDirection": "error",
"useYield": "error"
},
"style": {
"noNamespace": "error",
"useAsConstAssertion": "error",
"useBlockStatements": "error",
"useForOf": "error",
"useImportType": "error",
"useNamingConvention": {
"level": "off",
"options": { "strictCase": false }
}
},
"suspicious": {
"noAssignInExpressions": "warn",
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noClassAssign": "error",
"noCompareNegZero": "error",
"noConsoleLog": "off",
"noControlCharactersInRegex": "error",
"noDebugger": "error",
"noDuplicateCase": "error",
"noDuplicateClassMembers": "error",
"noDuplicateObjectKeys": "error",
"noDuplicateParameters": "error",
"noEmptyBlockStatements": "off",
"noExplicitAny": "off",
"noExtraNonNullAssertion": "error",
"noFallthroughSwitchClause": "error",
"noFunctionAssign": "error",
"noGlobalAssign": "error",
"noImportAssign": "error",
"noMisleadingCharacterClass": "error",
"noMisleadingInstantiator": "error",
"noPrototypeBuiltins": "off",
"noRedeclare": "error",
"noShadowRestrictedNames": "error",
"noUnsafeDeclarationMerging": "error",
"noUnsafeNegation": "error",
"useGetterReturn": "error",
"useIsArray": "error",
"useValidTypeof": "error"
}
}
},
"javascript": {
"formatter": {
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "es5",
"semicolons": "always",
"arrowParentheses": "always",
"bracketSpacing": true,
"bracketSameLine": false,
"quoteStyle": "single",
"attributePosition": "auto"
},
"globals": [
"it",
"describe",
"beforeEach",
"beforeAll",
"afterEach",
"cy",
"expect",
"context",
"Cypress"
]
},
"overrides": [
{
"include": ["cypress/**", "demos/**", "**/scripts"],
"linter": { "rules": { "suspicious": { "noConsoleLog": "off" } } }
},
{ "include": ["*.{js,jsx,mjs,cjs}"], "linter": { "rules": {} } },
{ "include": ["*.{ts,tsx}"], "linter": { "rules": {} } },
{
"include": ["*.spec.{ts,js}", "cypress/**", "demos/**", "**/docs/**"],
"linter": {
"rules": {
"correctness": { "noUnusedVariables": "off" },
"style": {
"useNamingConvention": "off"
}
}
}
},
{
"include": ["*.spec.{ts,js}", "tests/**", "cypress/**/*.js"],
"linter": {
"rules": {
"style": {
"useNamingConvention": "off"
}
}
}
},
{
"include": ["*.html", "*.md", "**/*.md/*"],
"linter": {
"rules": {
"correctness": {
"noUndeclaredVariables": "off",
"noUnusedVariables": "off"
},
"style": { "noVar": "error" }
}
}
},
{ "include": ["*.md"] },
{
"include": ["**/*.md/**"],
"linter": {
"rules": {
"correctness": {
"noUndeclaredVariables": "off",
"noUnusedVariables": "off"
}
}
}
},
{
"include": ["*.ts", "*.tsx", "*.mts", "*.cts"],
"linter": {
"rules": {
"correctness": {
"noConstAssign": "off",
"noGlobalObjectCalls": "off",
"noInvalidConstructorSuper": "off",
"noNewSymbol": "off",
"noSetterReturn": "off",
"noUndeclaredVariables": "off",
"noUnreachable": "off",
"noUnreachableSuper": "off"
},
"style": {
"noArguments": "error",
"noVar": "error",
"useConst": "error"
},
"suspicious": {
"noDuplicateClassMembers": "off",
"noDuplicateObjectKeys": "off",
"noDuplicateParameters": "off",
"noFunctionAssign": "off",
"noImportAssign": "off",
"noRedeclare": "off",
"noUnsafeNegation": "off",
"useGetterReturn": "off"
}
}
}
}
],
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
}
}

View File

@@ -2,8 +2,6 @@ import { defineConfig } from 'cypress';
import { addMatchImageSnapshotPlugin } from 'cypress-image-snapshot/plugin';
import coverage from '@cypress/code-coverage/task';
import eyesPlugin from '@applitools/eyes-cypress';
import { registerArgosTask } from '@argos-ci/cypress/task';
export default eyesPlugin(
defineConfig({
projectId: 'n2sma2',
@@ -19,17 +17,10 @@ export default eyesPlugin(
}
return launchOptions;
});
addMatchImageSnapshotPlugin(on, config);
// copy any needed variables from process.env to config.env
config.env.useAppli = process.env.USE_APPLI ? true : false;
config.env.useArgos = !!process.env.CI;
if (config.env.useArgos) {
registerArgosTask(on, config, {
token: 'fc3a35cf5200db928d65b2047861582d9444032b',
});
} else {
addMatchImageSnapshotPlugin(on, config);
}
// do not forget to return the changed config object!
return config;
},

View File

@@ -95,22 +95,8 @@ export const openURLAndVerifyRendering = (
options: CypressMermaidConfig,
validation?: any
): void => {
const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
if (validation) {
cy.get('svg').should(validation);
}
verifyScreenshot(name);
};
export const verifyScreenshot = (name: string): void => {
const useAppli: boolean = Cypress.env('useAppli');
const useArgos: boolean = Cypress.env('useArgos');
const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
if (useAppli) {
cy.log(`Opening eyes ${Cypress.spec.name} --- ${name}`);
@@ -120,12 +106,21 @@ export const verifyScreenshot = (name: string): void => {
batchName: Cypress.spec.name,
batchId: batchId + Cypress.spec.name,
});
}
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
if (validation) {
cy.get('svg').should(validation);
}
if (useAppli) {
cy.log(`Check eyes ${Cypress.spec.name}`);
cy.eyesCheckWindow('Click!');
cy.log(`Closing eyes ${Cypress.spec.name}`);
cy.eyesClose();
} else if (useArgos) {
cy.argosScreenshot(name);
} else {
cy.matchImageSnapshot(name);
}

View File

@@ -1,4 +1,4 @@
import { renderGraph, verifyScreenshot } from '../../helpers/util.ts';
import { renderGraph } from '../../helpers/util.ts';
describe('Configuration', () => {
describe('arrowMarkerAbsolute', () => {
it('should handle default value false of arrowMarkerAbsolute', () => {
@@ -119,7 +119,8 @@ describe('Configuration', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
verifyScreenshot(
cy.get('svg').should('be.visible');
cy.matchImageSnapshot(
'configuration.spec-should-not-taint-initial-configuration-when-using-multiple-directives'
);
});
@@ -144,7 +145,7 @@ describe('Configuration', () => {
// none of the diagrams should be error diagrams
expect($svg).to.not.contain('Syntax error');
});
verifyScreenshot(
cy.matchImageSnapshot(
'configuration.spec-should-not-render-error-diagram-if-suppressErrorRendering-is-set'
);
});
@@ -161,7 +162,7 @@ describe('Configuration', () => {
// some of the diagrams should be error diagrams
expect($svg).to.contain('Syntax error');
});
verifyScreenshot(
cy.matchImageSnapshot(
'configuration.spec-should-render-error-diagram-if-suppressErrorRendering-is-not-set'
);
});

View File

@@ -10,6 +10,7 @@ describe('XSS', () => {
cy.wait(1000).then(() => {
cy.get('.mermaid').should('exist');
});
cy.get('svg');
});
it('should not allow tags in the css', () => {
@@ -136,9 +137,4 @@ describe('XSS', () => {
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
it('should sanitize backticks block diagram labels properly', () => {
cy.visit('http://localhost:9000/xss25.html');
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
});

View File

@@ -30,6 +30,7 @@ describe('C4 diagram', () => {
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Container diagram', () => {
imgSnapshotTest(
@@ -49,6 +50,7 @@ describe('C4 diagram', () => {
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Component diagram', () => {
imgSnapshotTest(
@@ -67,6 +69,7 @@ describe('C4 diagram', () => {
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Dynamic diagram', () => {
imgSnapshotTest(
@@ -90,6 +93,7 @@ describe('C4 diagram', () => {
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Deployment diagram', () => {
imgSnapshotTest(
@@ -113,5 +117,6 @@ describe('C4 diagram', () => {
`,
{}
);
cy.get('svg');
});
});

View File

@@ -32,6 +32,7 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
cy.get('svg');
});
it('2: should render a simple class diagrams with cardinality', () => {
@@ -60,6 +61,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('3: should render a simple class diagram with different visibilities', () => {
@@ -77,6 +79,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('4: should render a simple class diagram with comments', () => {
@@ -106,6 +109,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('5: should render a simple class diagram with abstract method', () => {
@@ -117,6 +121,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('6: should render a simple class diagram with static method', () => {
@@ -128,6 +133,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('7: should render a simple class diagram with Generic class', () => {
@@ -147,6 +153,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('8: should render a simple class diagram with Generic class and relations', () => {
@@ -167,6 +174,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('9: should render a simple class diagram with clickable link', () => {
@@ -188,6 +196,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('10: should render a simple class diagram with clickable callback', () => {
@@ -209,6 +218,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('11: should render a simple class diagram with return type on method', () => {
@@ -223,6 +233,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('12: should render a simple class diagram with generic types', () => {
@@ -238,6 +249,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('13: should render a simple class diagram with css classes applied', () => {
@@ -255,6 +267,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('14: should render a simple class diagram with css classes applied directly', () => {
@@ -270,6 +283,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('15: should render a simple class diagram with css classes applied to multiple classes', () => {
@@ -284,6 +298,7 @@ describe('Class diagram', () => {
`,
{}
);
cy.get('svg');
});
it('16: should render multiple class diagrams', () => {
@@ -336,6 +351,7 @@ describe('Class diagram', () => {
],
{}
);
cy.get('svg');
});
// it('17: should render a class diagram when useMaxWidth is true (default)', () => {
@@ -405,6 +421,7 @@ describe('Class diagram', () => {
`,
{ logLevel: 1 }
);
cy.get('svg');
});
it('should render class diagram with newlines in title', () => {
@@ -422,6 +439,7 @@ describe('Class diagram', () => {
+quack()
}
`);
cy.get('svg');
});
it('should render class diagram with many newlines in title', () => {

View File

@@ -218,6 +218,7 @@ describe('Entity Relationship Diagram', () => {
`,
{ loglevel: 1 }
);
cy.get('svg');
});
it('should render entities with keys', () => {

View File

@@ -8,6 +8,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render a complete quadrant chart', () => {
imgSnapshotTest(
@@ -29,6 +30,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render without points', () => {
imgSnapshotTest(
@@ -44,6 +46,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should able to render y-axix on right side', () => {
imgSnapshotTest(
@@ -60,6 +63,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should able to render x-axix on bottom', () => {
imgSnapshotTest(
@@ -76,6 +80,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should able to render x-axix on bottom and y-axis on right', () => {
imgSnapshotTest(
@@ -92,6 +97,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render without title', () => {
imgSnapshotTest(
@@ -106,6 +112,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should use all the config', () => {
imgSnapshotTest(
@@ -128,6 +135,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should use all the theme variable', () => {
imgSnapshotTest(
@@ -150,6 +158,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render x-axis labels in the center, if x-axis has two labels', () => {
imgSnapshotTest(
@@ -171,6 +180,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render y-axis labels in the center, if y-axis has two labels', () => {
imgSnapshotTest(
@@ -192,6 +202,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('should render both axes labels on the left and bottom, if both axes have only one label', () => {
imgSnapshotTest(
@@ -213,6 +224,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('it should render data points with styles', () => {
@@ -237,6 +249,7 @@ describe('Quadrant Chart', () => {
`,
{}
);
cy.get('svg');
});
it('it should render data points with styles + classes', () => {

View File

@@ -44,5 +44,6 @@ describe('Requirement diagram', () => {
`,
{}
);
cy.get('svg');
});
});

View File

@@ -1,4 +1,4 @@
// <reference types="Cypress" />
/// <reference types="Cypress" />
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
@@ -68,19 +68,6 @@ context('Sequence diagram', () => {
{ sequence: { actorFontFamily: 'courier' } }
);
});
it('should render bidirectional arrows', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice<<->>John: Hello John, how are you?
Alice<<-->>John: Hi Alice, I can hear you!
John<<->>Alice: This also works the other way
John<<-->>Alice: Yes
Alice->John: Test
John->>Alice: Still works
`
);
});
it('should handle different line breaks', () => {
imgSnapshotTest(
`
@@ -477,18 +464,6 @@ context('Sequence diagram', () => {
{}
);
});
it('should render notes over actors and participant', () => {
imgSnapshotTest(
`
sequenceDiagram
actor Alice
participant Charlie
note over Alice: some note
note over Charlie: other note
`,
{}
);
});
it('should render long messages from an actor to the left to one to the right', () => {
imgSnapshotTest(
`

View File

@@ -8,6 +8,7 @@ describe('State diagram', () => {
`,
{ logLevel: 1, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a simple state diagrams', () => {
imgSnapshotTest(
@@ -19,6 +20,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a long descriptions instead of id when available', () => {
imgSnapshotTest(
@@ -30,6 +32,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a long descriptions with additional descriptions', () => {
imgSnapshotTest(
@@ -41,6 +44,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a single state with short descriptions', () => {
imgSnapshotTest(
@@ -51,6 +55,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a transition descriptions with new lines', () => {
imgSnapshotTest(
@@ -64,6 +69,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a state with a note', () => {
imgSnapshotTest(
@@ -77,6 +83,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a state with on the left side when so specified', () => {
imgSnapshotTest(
@@ -90,6 +97,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a state with a note together with another state', () => {
imgSnapshotTest(
@@ -105,6 +113,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a note with multiple lines in it', () => {
imgSnapshotTest(
@@ -147,6 +156,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a simple state diagrams 2', () => {
imgSnapshotTest(
@@ -159,6 +169,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a simple state diagrams with labels', () => {
imgSnapshotTest(
@@ -174,6 +185,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render state descriptions', () => {
imgSnapshotTest(
@@ -186,6 +198,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render composite states', () => {
imgSnapshotTest(
@@ -204,6 +217,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render multiple composite states', () => {
imgSnapshotTest(
@@ -273,6 +287,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render concurrency states', () => {
imgSnapshotTest(
@@ -296,6 +311,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('v2 should render a state with states in it', () => {
imgSnapshotTest(

View File

@@ -10,6 +10,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a long descriptions instead of id when available', () => {
imgSnapshotTest(
@@ -21,6 +22,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a long descriptions with additional descriptions', () => {
imgSnapshotTest(
@@ -32,6 +34,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a single state with short descriptions', () => {
imgSnapshotTest(
@@ -42,6 +45,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a transition descriptions with new lines', () => {
imgSnapshotTest(
@@ -55,6 +59,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a state with a note', () => {
imgSnapshotTest(
@@ -68,6 +73,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a state with on the left side when so specified', () => {
imgSnapshotTest(
@@ -81,6 +87,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a state with a note together with another state', () => {
imgSnapshotTest(
@@ -96,6 +103,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a note with multiple lines in it', () => {
imgSnapshotTest(
@@ -138,6 +146,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a simple state diagrams 2', () => {
imgSnapshotTest(
@@ -150,6 +159,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a simple state diagrams with labels', () => {
imgSnapshotTest(
@@ -165,6 +175,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render state descriptions', () => {
imgSnapshotTest(
@@ -177,6 +188,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render composite states', () => {
imgSnapshotTest(
@@ -195,6 +207,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render multiple composit states', () => {
imgSnapshotTest(
@@ -264,6 +277,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render concurrency states', () => {
imgSnapshotTest(
@@ -287,6 +301,7 @@ describe('State diagram', () => {
`,
{ logLevel: 0, fontFamily: 'courier' }
);
cy.get('svg');
});
it('should render a state with states in it', () => {
imgSnapshotTest(

View File

@@ -10,6 +10,7 @@ describe('themeCSS balancing, it', () => {
`,
{}
);
cy.get('svg');
});
it('should not allow unbalanced CSS definitions 2', () => {
imgSnapshotTest(
@@ -20,6 +21,7 @@ describe('themeCSS balancing, it', () => {
`,
{}
);
cy.get('svg');
});
});
@@ -43,6 +45,7 @@ describe('Pie Chart', () => {
`,
{ theme }
);
cy.get('svg');
});
it('should render a flowchart diagram', () => {
imgSnapshotTest(
@@ -67,6 +70,7 @@ describe('Pie Chart', () => {
`,
{ theme }
);
cy.get('svg');
});
it('should render a new flowchart diagram', () => {
imgSnapshotTest(
@@ -92,6 +96,7 @@ describe('Pie Chart', () => {
`,
{ theme }
);
cy.get('svg');
});
it('should render a sequence diagram', () => {
imgSnapshotTest(
@@ -120,6 +125,7 @@ describe('Pie Chart', () => {
`,
{ theme }
);
cy.get('svg');
});
it('should render a class diagram', () => {
@@ -169,6 +175,7 @@ describe('Pie Chart', () => {
`,
{ theme }
);
cy.get('svg');
});
it('should render a state diagram', () => {
imgSnapshotTest(
@@ -203,6 +210,7 @@ stateDiagram
`,
{ theme }
);
cy.get('svg');
});
it('should render a state diagram (v2)', () => {
imgSnapshotTest(
@@ -237,6 +245,7 @@ stateDiagram-v2
`,
{ theme }
);
cy.get('svg');
});
it('should render a er diagram', () => {
imgSnapshotTest(
@@ -257,6 +266,7 @@ erDiagram
`,
{ theme }
);
cy.get('svg');
});
it('should render a user journey diagram', () => {
imgSnapshotTest(
@@ -277,6 +287,7 @@ erDiagram
`,
{ theme }
);
cy.get('svg');
});
it('should render a gantt diagram', () => {
cy.clock(new Date('2014-01-06').getTime());
@@ -315,6 +326,7 @@ erDiagram
`,
{ theme }
);
cy.get('svg');
});
});
});

View File

@@ -9,6 +9,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Should render a complete chart', () => {
imgSnapshotTest(
@@ -34,6 +35,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('y-axis title not required', () => {
imgSnapshotTest(
@@ -46,6 +48,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Should render a chart without y-axis with different range', () => {
imgSnapshotTest(
@@ -57,6 +60,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('x axis title not required', () => {
imgSnapshotTest(
@@ -68,6 +72,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Multiple plots can be rendered', () => {
imgSnapshotTest(
@@ -82,6 +87,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Decimals and negative numbers are supported', () => {
imgSnapshotTest(
@@ -92,6 +98,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render spark line with "plotReservedSpacePercent"', () => {
imgSnapshotTest(
@@ -109,6 +116,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render spark bar without displaying other property', () => {
imgSnapshotTest(
@@ -135,6 +143,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Should use all the config from directive', () => {
imgSnapshotTest(
@@ -149,6 +158,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Should use all the config from yaml', () => {
imgSnapshotTest(
@@ -189,6 +199,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render with show axis title false', () => {
imgSnapshotTest(
@@ -210,6 +221,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render with show axis label false', () => {
imgSnapshotTest(
@@ -231,6 +243,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render with show axis tick false', () => {
imgSnapshotTest(
@@ -252,6 +265,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render with show axis line false', () => {
imgSnapshotTest(
@@ -273,6 +287,7 @@ describe('XY Chart', () => {
`,
{}
);
cy.get('svg');
});
it('Render all the theme color', () => {
imgSnapshotTest(
@@ -302,17 +317,6 @@ describe('XY Chart', () => {
`,
{}
);
});
it('should use the correct distances between data points', () => {
imgSnapshotTest(
`
xychart-beta
x-axis 0 --> 2
line [0, 1, 0, 1]
bar [1, 0, 1, 0]
`,
{}
);
cy.get('svg');
});
});

View File

@@ -1,108 +0,0 @@
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background:#333; */
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
</style>
<script>
function xssAttack() {
const div = document.createElement('div');
div.id = 'the-malware';
div.className = 'malware';
div.innerHTML = 'XSS Succeeded';
document.getElementsByTagName('body')[0].appendChild(div);
throw new Error('XSS Succeeded');
}
</script>
</head>
<body>
<div>Security check</div>
<div class="flex">
<div id="diagram" class="mermaid"></div>
<div id="res" class=""></div>
</div>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({
theme: 'forest',
arrowMarkerAbsolute: true,
// themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}',
logLevel: 0,
state: {
defaultRenderer: 'dagre-wrapper',
},
flowchart: {
// defaultRenderer: 'dagre-wrapper',
nodeSpacing: 10,
curve: 'cardinal',
htmlLabels: true,
},
htmlLabels: false,
// gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorFontFamily: 'courier', actorMargin: 50, showSequenceNumbers: false },
// sequenceDiagram: { actorMargin: 300 } // deprecated
// fontFamily: '"times", sans-serif',
// fontFamily: 'courier',
fontSize: 18,
curve: 'basis',
securityLevel: 'strict',
startOnLoad: false,
secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize'],
// themeVariables: {relationLabelColor: 'red'}
});
function callback() {
alert('It worked');
}
let diagram = 'block-beta\n';
diagram += '`A-- "X<img src=x on';
diagram += 'error=xssAttack()>" -->B';
console.log(diagram);
// document.querySelector('#diagram').innerHTML = diagram;
const { svg } = await mermaid.render('diagram', diagram);
document.querySelector('#res').innerHTML = svg;
</script>
</body>
</html>

View File

@@ -15,7 +15,6 @@
import '@cypress/code-coverage/support';
import '@applitools/eyes-cypress/commands';
import '@argos-ci/cypress/support';
// Import commands.js using ES2015 syntax:
import './commands';

View File

@@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es2020",
"lib": ["es2020", "dom"],
"types": ["cypress", "node", "@argos-ci/cypress/dist/support.d.ts"],
"types": ["cypress", "node"],
"allowImportingTsExtensions": true,
"noEmit": true
},

View File

@@ -1,3 +1,4 @@
version: '3.9'
services:
mermaid:
build:
@@ -7,7 +8,7 @@ services:
tty: true
working_dir: /mermaid
mem_limit: '8G'
entrypoint: ./docker-entrypoint.sh
entrypoint: docker-entrypoint.sh
environment:
- NODE_OPTIONS=--max_old_space_size=8192
volumes:
@@ -15,7 +16,6 @@ services:
- root_cache:/root/.cache
- root_local:/root/.local
- root_npm:/root/.npm
- /tmp:/tmp
ports:
- 9000:9000
- 3333:3333

View File

@@ -56,7 +56,7 @@ The following commands must be sufficient enough to start with:
```bash
curl -fsSL https://get.pnpm.io/install.sh | sh -
pnpm env use --global 20
pnpm env use --global 18
```
You may also need to reload `.shrc` or `.bashrc` afterwards.

View File

@@ -19,4 +19,4 @@ The `parseError` function will not be called.
#### Defined in
[mermaidAPI.ts:65](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L65)
[mermaidAPI.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L64)

View File

@@ -18,4 +18,4 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[mermaidAPI.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L72)
[mermaidAPI.ts:71](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L71)

View File

@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
#### Defined in
[mermaidAPI.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L95)
[mermaidAPI.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L94)
---
@@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[mermaidAPI.ts:85](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L85)
[mermaidAPI.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L84)
---
@@ -63,4 +63,4 @@ The svg code for the rendered graph.
#### Defined in
[mermaidAPI.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L81)
[mermaidAPI.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L80)

View File

@@ -10,7 +10,7 @@
### defaultConfig
`Const` **defaultConfig**: `MermaidConfig`
`Const` **defaultConfig**: `MermaidConfigWithDefaults`
#### Defined in
@@ -26,9 +26,9 @@ Pushes in a directive to the configuration
#### Parameters
| Name | Type | Description |
| :---------- | :-------------- | :----------------------- |
| `directive` | `MermaidConfig` | The directive to push in |
| Name | Type | Description |
| :---------- | :--------------------------------------------------- | :----------------------- |
| `directive` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> | The directive to push in |
#### Returns
@@ -36,13 +36,13 @@ Pushes in a directive to the configuration
#### Defined in
[config.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L188)
[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191)
---
### getConfig
**getConfig**(): `MermaidConfig`
**getConfig**(): `MermaidConfigWithDefaults`
## getConfig
@@ -54,19 +54,19 @@ Pushes in a directive to the configuration
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The currentConfig
#### Defined in
[config.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L131)
[config.ts:134](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L134)
---
### getSiteConfig
**getSiteConfig**(): `MermaidConfig`
**getSiteConfig**(): `MermaidConfigWithDefaults`
## getSiteConfig
@@ -78,13 +78,13 @@ The currentConfig
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The siteConfig
#### Defined in
[config.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L96)
[config.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L99)
---
@@ -108,9 +108,9 @@ The siteConfig
#### Parameters
| Name | Type | Default value | Description |
| :------- | :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfig` | `siteConfig` | base set of values, which currentConfig could be **reset** to. Defaults to the current siteConfig (e.g returned by [getSiteConfig](config.md#getsiteconfig)). |
| Name | Type | Default value | Description |
| :------- | :-------------------------- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfigWithDefaults` | `siteConfig` | base set of values, which currentConfig could be **reset** to. Defaults to the current siteConfig (e.g returned by [getSiteConfig](config.md#getsiteconfig)). |
#### Returns
@@ -118,7 +118,7 @@ The siteConfig
#### Defined in
[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218)
[config.ts:221](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L221)
---
@@ -147,7 +147,7 @@ options in-place
#### Defined in
[config.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L146)
[config.ts:149](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L149)
---
@@ -157,9 +157,9 @@ options in-place
#### Parameters
| Name | Type |
| :----- | :-------------- |
| `conf` | `MermaidConfig` |
| Name | Type |
| :----- | :--------------------------------------------------- |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> |
#### Returns
@@ -167,13 +167,13 @@ options in-place
#### Defined in
[config.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L75)
[config.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L78)
---
### setConfig
**setConfig**(`conf`): `MermaidConfig`
**setConfig**(`conf`): `MermaidConfigWithDefaults`
## setConfig
@@ -187,25 +187,25 @@ corresponding siteConfig value.
#### Parameters
| Name | Type | Description |
| :----- | :-------------- | :-------------------------- |
| `conf` | `MermaidConfig` | The potential currentConfig |
| Name | Type | Description |
| :----- | :--------------------------------------------------- | :-------------------------- |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> | The potential currentConfig |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The currentConfig merged with the sanitized conf
#### Defined in
[config.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L113)
[config.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L116)
---
### setSiteConfig
**setSiteConfig**(`conf`): `MermaidConfig`
**setSiteConfig**(`conf`): `MermaidConfigWithDefaults`
## setSiteConfig
@@ -220,36 +220,36 @@ function _Default value: At default, will mirror Global Config_
#### Parameters
| Name | Type | Description |
| :----- | :-------------- | :------------------------------------------ |
| `conf` | `MermaidConfig` | The base currentConfig to use as siteConfig |
| Name | Type | Description |
| :----- | :--------------------------------------------------- | :------------------------------------------ |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> | The base currentConfig to use as siteConfig |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
The new siteConfig
#### Defined in
[config.ts:61](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L61)
[config.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L64)
---
### updateCurrentConfig
**updateCurrentConfig**(`siteCfg`, `_directives`): `MermaidConfig`
**updateCurrentConfig**(`siteCfg`, `_directives`): `MermaidConfigWithDefaults`
#### Parameters
| Name | Type |
| :------------ | :----------------- |
| `siteCfg` | `MermaidConfig` |
| `_directives` | `MermaidConfig`\[] |
| Name | Type |
| :------------ | :------------------------------------------------------ |
| `siteCfg` | `MermaidConfigWithDefaults` |
| `_directives` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>\[] |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
#### Defined in
@@ -259,18 +259,18 @@ The new siteConfig
### updateSiteConfig
**updateSiteConfig**(`conf`): `MermaidConfig`
**updateSiteConfig**(`conf`): `MermaidConfigWithDefaults`
#### Parameters
| Name | Type |
| :----- | :-------------- |
| `conf` | `MermaidConfig` |
| Name | Type |
| :----- | :--------------------------------------------------- |
| `conf` | `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}> |
#### Returns
`MermaidConfig`
`MermaidConfigWithDefaults`
#### Defined in
[config.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L79)
[config.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L82)

View File

@@ -20,7 +20,7 @@
### default
`Const` **default**: `RequiredDeep`<`MermaidConfig`>
`Const` **default**: `RequiredDeep`<`MermaidConfigWithDefaults`>
Default mermaid configuration options.

View File

@@ -26,13 +26,13 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
#### Defined in
[mermaidAPI.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L75)
[mermaidAPI.ts:74](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L74)
## Variables
### mermaidAPI
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md) & { `suppressErrors`: `true` }) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md) | `false`>(`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md)> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }>
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfigWithDefaults` = configApi.defaultConfig; `getConfig`: () => `MermaidConfigWithDefaults` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfigWithDefaults` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>) => `void` ; `parse`: (`text`: `string`, `parseOptions`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md) & { `suppressErrors`: `true` }) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md) | `false`>(`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md)> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>) => `MermaidConfigWithDefaults` = configApi.setConfig; `updateSiteConfig`: (`conf`: `PartialObjectDeep`<`MermaidConfigWithDefaults`, {}>) => `MermaidConfigWithDefaults` = configApi.updateSiteConfig }>
## mermaidAPI configuration defaults
@@ -98,7 +98,7 @@ mermaid.initialize(config);
#### Defined in
[mermaidAPI.ts:634](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L634)
[mermaidAPI.ts:635](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L635)
## Functions
@@ -129,7 +129,7 @@ Return the last node appended
#### Defined in
[mermaidAPI.ts:276](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L276)
[mermaidAPI.ts:277](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L277)
---
@@ -167,10 +167,10 @@ Create the user styles
#### Parameters
| Name | Type | Description |
| :---------- | :--------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfig` | configuration that has style and theme settings to use |
| `classDefs` | `undefined` \| `null` \| `Map`<`string`, `DiagramStyleClassDef`> | the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) |
| Name | Type | Description |
| :---------- | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfigWithDefaults` | configuration that has style and theme settings to use |
| `classDefs` | `undefined` \| `null` \| `Record`<`string`, `DiagramStyleClassDef`> | the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) |
#### Returns
@@ -180,7 +180,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:154](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L154)
[mermaidAPI.ts:153](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L153)
---
@@ -190,12 +190,12 @@ the string with all the user styles
#### Parameters
| Name | Type |
| :---------- | :----------------------------------------------------- |
| `config` | `MermaidConfig` |
| `graphType` | `string` |
| `classDefs` | `undefined` \| `Map`<`string`, `DiagramStyleClassDef`> |
| `svgId` | `string` |
| Name | Type |
| :---------- | :-------------------------------------------------------- |
| `config` | `MermaidConfigWithDefaults` |
| `graphType` | `string` |
| `classDefs` | `undefined` \| `Record`<`string`, `DiagramStyleClassDef`> |
| `svgId` | `string` |
#### Returns
@@ -230,7 +230,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:139](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L139)
[mermaidAPI.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L138)
---
@@ -252,10 +252,11 @@ Put the svgCode into an iFrame. Return the iFrame code
`string`
- the code with the iFrame that now contains the svgCode
TODO replace btoa(). Replace with buf.toString('base64')?
#### Defined in
[mermaidAPI.ts:253](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L253)
[mermaidAPI.ts:254](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L254)
---
@@ -280,4 +281,4 @@ Remove any existing elements from the given document
#### Defined in
[mermaidAPI.ts:326](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L326)
[mermaidAPI.ts:327](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L327)

View File

@@ -6,10 +6,6 @@
# Mermaid Chart
The Future of Diagramming & Visual Collaboration
Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an account at [Mermaid Chart](https://www.mermaidchart.com/app/sign-up).
<br />
<a href="https://www.producthunt.com/posts/mermaid-chart?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=416671&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
@@ -22,26 +18,22 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
- **Editor** - A web based editor for creating and editing Mermaid diagrams.
- **Visual Editor** - The Visual Editor enables users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options.
- **Presentation** - A presentation mode for viewing Mermaid diagrams in a slideshow format.
- **AI Chat** - Use our embedded AI Chat to generate diagrams from natural language descriptions.
- **Collaboration** - A web based collaboration feature for multi-user editing on Mermaid diagrams in real-time (Pro plan).
- **Plugins** - A plugin system for extending the functionality of Mermaid.
Official Mermaid Chart plugins:
Plugins are available for:
- [Mermaid Chart GPT](https://chat.openai.com/g/g-1IRFKwq4G-mermaid-chart)
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart)
- [ChatGPT](https://docs.mermaidchart.com/plugins/chatgpt)
- [JetBrains IDE](https://plugins.jetbrains.com/plugin/23043-mermaid-chart)
- [Microsoft PowerPoint and Word](https://appsource.microsoft.com/en-us/product/office/WA200006214?tab=Overview)
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart)
Visit our [Plugins](https://www.mermaidchart.com/plugins) page for more information.
- **AI diagramming** - A feature for generating Mermaid diagrams from text using AI (Pro plan).
- **Collaboration** - A web based collaboration feature for multi-user editing on Mermaid diagrams in real-time (Pro and Enterprise plans).
- **Comments** - Enhance collaboration by adding comments to diagrams.
- **Presentations** - A presentation mode for viewing Mermaid diagrams in a slideshow format.
- **More** - To learn more, visit our [Product](https://www.mermaidchart.com/product) page.
## Plans
@@ -51,9 +43,11 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
- **Enterprise** - A paid plan for enterprise use that includes all Pro features, and more.
To learn more, visit our [Pricing](https://mermaidchart.com/pricing) page.
## Access
Mermaid Chart is currently offering a 14-day free trial on our Pro and Enterprise tiers. Sign up for a free account at [Mermaid Chart](https://www.mermaidchart.com/app/sign-up).
Sign up for a free account at [Mermaid Chart](https://www.mermaidchart.com/app/sign-up).
Mermaid Chart is currently offering a 14-day free trial of our newly-launched Pro tier. To learn more, visit our [Pricing](https://mermaidchart.com/pricing) page.
## Mermaid JS contributions

View File

@@ -157,7 +157,7 @@ For a list of Mermaid Plugins and Integrations, visit the [Integrations page](..
Mermaid Chart plugins are available for:
- [ChatGPT](https://docs.mermaidchart.com/plugins/mermaid-chart-gpt)
- [ChatGPT](https://docs.mermaidchart.com/plugins/chatgpt)
- [JetBrains IDE](https://docs.mermaidchart.com/plugins/jetbrains-ide)
- [Microsoft PowerPoint](https://docs.mermaidchart.com/plugins/microsoft-powerpoint)
- [Microsoft Word](https://docs.mermaidchart.com/plugins/microsoft-word)

View File

@@ -6,18 +6,6 @@
# Blog
## [How to Choose the Right Documentation Software](https://www.mermaidchart.com/blog/posts/how-to-choose-the-right-documentation-software/)
7 May 2024 · 5 mins
How to Choose the Right Documentation Software. Reliable and efficient documentation software is crucial in the fast-paced world of software development.
## [AI in software diagramming: What trends will define the future?](https://www.mermaidchart.com/blog/posts/ai-in-software-diagramming/)
24 April 2024 · 5 mins
Artificial intelligence (AI) tools are changing the way developers work.
## [Mermaid Chart Unveils Visual Editor for Sequence Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams/)
8 April 2024 · 5 mins

View File

@@ -881,7 +881,7 @@ Examples of tooltip usage below:
```html
<script>
window.callback = function () {
const callback = function () {
alert('A callback was triggered');
};
</script>
@@ -913,7 +913,7 @@ flowchart LR
> **Success** The tooltip functionality and the ability to link to urls are available from version 0.5.2.
?> Due to limitations with how Docsify handles JavaScript callback functions, an alternate working demo for the above code can be viewed at [this jsfiddle](https://jsfiddle.net/yk4h7qou/2/).
?> Due to limitations with how Docsify handles JavaScript callback functions, an alternate working demo for the above code can be viewed at [this jsfiddle](https://jsfiddle.net/Ogglas/2o73vdez/7).
Links are opened in the same browser tab/window by default. It is possible to change this by adding a link target to the click definition (`_self`, `_blank`, `_parent` and `_top` are supported):
@@ -957,7 +957,7 @@ Beginner's tip—a full example using interactive links in a html context:
</pre>
<script>
window.callback = function () {
const callback = function () {
alert('A callback was triggered');
};
const config = {

View File

@@ -206,20 +206,18 @@ Messages can be of two displayed either solid or with a dotted line.
[Actor][Arrow][Actor]:Message text
```
There are ten types of arrows currently supported:
There are six types of arrows currently supported:
| Type | Description |
| -------- | ------------------------------------------------------------------------ |
| `->` | Solid line without arrow |
| `-->` | Dotted line without arrow |
| `->>` | Solid line with arrowhead |
| `-->>` | Dotted line with arrowhead |
| `<<->>` | Solid line with bidirectional arrowheads (v\<MERMAID_RELEASE_VERSION>+) |
| `<<-->>` | Dotted line with bidirectional arrowheads (v\<MERMAID_RELEASE_VERSION>+) |
| `-x` | Solid line with a cross at the end |
| `--x` | Dotted line with a cross at the end. |
| `-)` | Solid line with an open arrow at the end (async) |
| `--)` | Dotted line with a open arrow at the end (async) |
| Type | Description |
| ------ | ------------------------------------------------ |
| `->` | Solid line without arrow |
| `-->` | Dotted line without arrow |
| `->>` | Solid line with arrowhead |
| `-->>` | Dotted line with arrowhead |
| `-x` | Solid line with a cross at the end |
| `--x` | Dotted line with a cross at the end. |
| `-)` | Solid line with an open arrow at the end (async) |
| `--)` | Dotted line with a open arrow at the end (async) |
## Activations
@@ -306,35 +304,17 @@ sequenceDiagram
Note over Alice,John: A typical interaction
```
## Line breaks
Line break can be added to Note and Message:
It is also possible to add a line break (applies to text input in general):
```mermaid-example
sequenceDiagram
Alice->John: Hello John,<br/>how are you?
Alice->John: Hello John, how are you?
Note over Alice,John: A typical interaction<br/>But now in two lines
```
```mermaid
sequenceDiagram
Alice->John: Hello John,<br/>how are you?
Note over Alice,John: A typical interaction<br/>But now in two lines
```
Line breaks in Actor names requires aliases:
```mermaid-example
sequenceDiagram
participant Alice as Alice<br/>Johnson
Alice->John: Hello John,<br/>how are you?
Note over Alice,John: A typical interaction<br/>But now in two lines
```
```mermaid
sequenceDiagram
participant Alice as Alice<br/>Johnson
Alice->John: Hello John,<br/>how are you?
Alice->John: Hello John, how are you?
Note over Alice,John: A typical interaction<br/>But now in two lines
```

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@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a",
"packageManager": "pnpm@8.15.8",
"keywords": [
"diagram",
"markdown",
@@ -25,9 +25,8 @@
"dev:vite": "tsx .vite/server.ts",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
"release": "pnpm build",
"lint": "pnpm biome check && pnpm lint:jison",
"lint:fix": "pnpm biome check --write",
"lint:ci": "cross-env NODE_OPTIONS=--max_old_space_size=8192 eslint --cache --cache-strategy content . && pnpm lint",
"lint": "cross-env NODE_OPTIONS=--max_old_space_size=8192 eslint --cache --cache-strategy content . && pnpm lint:jison && prettier --cache --check .",
"lint:fix": "cross-env NODE_OPTIONS=--max_old_space_size=8192 eslint --cache --cache-strategy content --fix . && prettier --write . && tsx scripts/fixCSpell.ts",
"lint:jison": "tsx ./scripts/jison/lint.mts",
"contributors": "tsx scripts/updateContributors.ts",
"cypress": "cypress run",
@@ -52,13 +51,17 @@
"author": "Knut Sveidqvist",
"license": "MIT",
"standard": {
"ignore": ["**/parser/*.js", "dist/**/*.js", "cypress/**/*.js"],
"globals": ["page"]
"ignore": [
"**/parser/*.js",
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.42.3",
"@argos-ci/cypress": "^2.0.5",
"@biomejs/biome": "1.8.2",
"@cspell/eslint-plugin": "^8.6.0",
"@cypress/code-coverage": "^3.12.30",
"@rollup/plugin-typescript": "^11.1.6",
@@ -123,5 +126,10 @@
},
"nyc": {
"report-dir": "coverage/cypress"
},
"pnpm": {
"patchedDependencies": {
"cytoscape@3.28.1": "patches/cytoscape@3.28.1.patch"
}
}
}

View File

@@ -12,7 +12,12 @@
},
"./*": "./*"
},
"keywords": ["diagram", "markdown", "example", "mermaid"],
"keywords": [
"diagram",
"markdown",
"example",
"mermaid"
],
"scripts": {
"prepublishOnly": "pnpm -w run build"
},
@@ -23,8 +28,14 @@
"author": "Knut Sveidqvist",
"license": "MIT",
"standard": {
"ignore": ["**/parser/*.js", "dist/**/*.js", "cypress/**/*.js"],
"globals": ["page"]
"ignore": [
"**/parser/*.js",
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
},
"dependencies": {
"@braintree/sanitize-url": "^7.0.0",
@@ -36,6 +47,11 @@
"mermaid": "workspace:*",
"rimraf": "^5.0.5"
},
"files": ["dist"],
"sideEffects": ["**/*.css", "**/*.scss"]
"files": [
"dist"
],
"sideEffects": [
"**/*.css",
"**/*.scss"
]
}

View File

@@ -13,7 +13,7 @@ export const draw = (text, id, version) => {
try {
const conf = getConfig();
log.debug('Rendering example diagram\n' + text, 'Conf: ');
const themeColorLimit = getConfig().themeVariables.THEME_COLOR_LIMIT;
const THEME_COLOR_LIMIT = getConfig().themeVariables.THEME_COLOR_LIMIT;
const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sandbox mode
let sandboxElement;
@@ -30,7 +30,7 @@ export const draw = (text, id, version) => {
const g = svg.append('g');
let i;
for (i = 0; i < themeColorLimit; i++) {
for (i = 0; i < THEME_COLOR_LIMIT; i++) {
const section = g.append('g').attr('class', 'section-' + i);
section
.append('rect')

View File

@@ -12,7 +12,13 @@
},
"./*": "./*"
},
"keywords": ["diagram", "markdown", "flowchart", "elk", "mermaid"],
"keywords": [
"diagram",
"markdown",
"flowchart",
"elk",
"mermaid"
],
"scripts": {
"prepublishOnly": "pnpm -w run build"
},
@@ -33,5 +39,7 @@
"mermaid": "workspace:^",
"rimraf": "^5.0.5"
},
"files": ["dist"]
"files": [
"dist"
]
}

View File

@@ -39,26 +39,6 @@ describe('flowchart-elk detector', () => {
).toBe(true);
});
// The error from the issue was reproduced with mindmap, so this is just an example
// what matters is the keyword somewhere inside graph definition
it('should check only the beginning of the line in search of keywords', () => {
expect(
detector('mindmap ["Descendant node in flowchart"]', {
flowchart: {
defaultRenderer: 'elk',
},
})
).toBe(false);
expect(
detector('mindmap ["Descendant node in graph"]', {
flowchart: {
defaultRenderer: 'elk',
},
})
).toBe(false);
});
it('should detect flowchart-elk', () => {
expect(detector('flowchart-elk')).toBe(true);
});

View File

@@ -11,7 +11,7 @@ const detector: DiagramDetector = (txt, config): boolean => {
// If diagram explicitly states flowchart-elk
/^\s*flowchart-elk/.test(txt) ||
// If a flowchart/graph diagram has their default renderer set to elk
(/^\s*(flowchart|graph)/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
(/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
) {
return true;
}

View File

@@ -41,12 +41,12 @@ let nodeDb = {};
export const addVertices = async function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) {
const svg = root.select(`[id="${svgId}"]`);
const nodes = svg.insert('g').attr('class', 'nodes');
const keys = [...vert.keys()];
const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
await Promise.all(
keys.map(async function (id) {
const vertex = vert.get(id);
const vertex = vert[id];
/**
* Variable for storing the classes for the vertex
@@ -447,19 +447,16 @@ export const addEdges = function (edges, diagObj, graph, svg) {
/* eslint-disable no-fallthrough */
switch (edge.type) {
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross';
break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point';
break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle':
@@ -598,7 +595,7 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
*
* @param text
* @param diagObj
* @returns {Map<string, import('../../mermaid/src/diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
* @returns {Record<string, import('../../mermaid/src/diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');

View File

@@ -12,7 +12,12 @@
},
"./*": "./*"
},
"keywords": ["diagram", "markdown", "zenuml", "mermaid"],
"keywords": [
"diagram",
"markdown",
"zenuml",
"mermaid"
],
"scripts": {
"clean": "rimraf dist",
"prepublishOnly": "pnpm -w run build"
@@ -37,5 +42,7 @@
"peerDependencies": {
"mermaid": "workspace:>=10.0.0"
},
"files": ["dist"]
"files": [
"dist"
]
}

View File

@@ -1,4 +1,4 @@
import type { MermaidConfig } from 'mermaid';
import type { MermaidConfigWithDefaults } from 'mermaid';
const warning = (s: string) => {
// Todo remove debug code
@@ -27,7 +27,7 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
};
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
export let getConfig: () => MermaidConfig;
export let getConfig: () => MermaidConfigWithDefaults;
export let sanitizeText: (str: string) => string;
// eslint-disable @typescript-eslint/no-explicit-any
export let setupGraphViewbox: (

View File

@@ -58,13 +58,19 @@
"author": "Knut Sveidqvist",
"license": "MIT",
"standard": {
"ignore": ["**/parser/*.js", "dist/**/*.js", "cypress/**/*.js"],
"globals": ["page"]
"ignore": [
"**/parser/*.js",
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
},
"dependencies": {
"@braintree/sanitize-url": "^7.0.1",
"@mermaid-js/parser": "workspace:^",
"cytoscape": "^3.29.2",
"cytoscape": "^3.28.1",
"cytoscape-cose-bilkent": "^4.1.0",
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
@@ -82,7 +88,7 @@
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.0",
"@types/cytoscape": "^3.21.4",
"@types/cytoscape": "^3.19.16",
"@types/d3": "^7.4.3",
"@types/d3-sankey": "^0.12.4",
"@types/d3-scale": "^4.0.8",
@@ -97,6 +103,8 @@
"@types/prettier": "^3.0.0",
"@types/stylis": "^4.2.5",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"ajv": "^8.12.0",
"chokidar": "^3.6.0",
"concurrently": "^8.2.2",
@@ -106,7 +114,7 @@
"jison": "^0.4.18",
"js-base64": "^3.7.7",
"jsdom": "^24.0.0",
"json-schema-to-typescript": "^13.1.2",
"json-schema-to-typescript": "^14.0.4",
"micromatch": "^4.0.5",
"path-browserify": "^1.0.1",
"prettier": "^3.2.5",
@@ -124,7 +132,10 @@
"vitepress": "^1.0.1",
"vitepress-plugin-search": "1.0.4-alpha.22"
},
"files": ["dist/", "README.md"],
"files": [
"dist/",
"README.md"
],
"sideEffects": false,
"publishConfig": {
"access": "public"

View File

@@ -18,14 +18,14 @@ import { execFile } from 'node:child_process';
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { promisify } from 'node:util';
import { prettierConfig } from './prettier.js';
import prettier from 'prettier';
// Workaround for wrong AJV types, see
// https://github.com/ajv-validator/ajv/issues/2132#issuecomment-1290409907
const Ajv2019 = _Ajv2019 as unknown as typeof _Ajv2019.default;
// !!! -- The config.type.js file is created by this script -- !!!
import type { MermaidConfig } from '../src/config.type.js';
import type { MermaidConfigWithDefaults } from '../src/config.type.js';
// options for running the main command
const verifyOnly = process.argv.includes('--verify');
@@ -33,7 +33,7 @@ const verifyOnly = process.argv.includes('--verify');
const git = process.argv.includes('--git');
/**
* Loads the MermaidConfig JSON schema YAML file.
* Loads the MermaidConfigWithDefaults JSON schema YAML file.
*
* @returns The loaded JSON Schema, use {@link validateSchema} to confirm it is a valid JSON Schema.
*/
@@ -55,7 +55,9 @@ async function loadJsonSchemaFromYaml() {
* @param jsonSchema - The value to validate as JSON Schema 2019-09
* @throws {Error} if the given object is invalid.
*/
function validateSchema(jsonSchema: unknown): asserts jsonSchema is JSONSchemaType<MermaidConfig> {
function validateSchema(
jsonSchema: unknown
): asserts jsonSchema is JSONSchemaType<MermaidConfigWithDefaults> {
if (typeof jsonSchema !== 'object') {
throw new Error(`jsonSchema param is not an object: actual type is ${typeof jsonSchema}`);
}
@@ -85,7 +87,7 @@ function validateSchema(jsonSchema: unknown): asserts jsonSchema is JSONSchemaTy
*
* @param mermaidConfigSchema - The input JSON Schema.
*/
async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidConfigWithDefaults>) {
/**
* Replace all usages of `allOf` with `extends`.
*
@@ -107,41 +109,23 @@ async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidCon
return schema;
}
/**
* For backwards compatibility with older Mermaid Typescript defs,
* we need to make all value optional instead of required.
*
* This is because the `MermaidConfig` type is used as an input, and everything is optional,
* since all the required values have default values.s
*
* In the future, we should make make the input to Mermaid `Partial<MermaidConfig>`.
*
* @todo TODO: Remove this function when Mermaid releases a new breaking change.
* @param schema - The input schema.
* @returns The schema with all required values removed.
*/
function removeRequired(schema: JSONSchemaType<Record<string, any>>) {
return { ...schema, required: [] };
}
assert.ok(mermaidConfigSchema.$defs);
const modifiedSchema = {
...removeRequired(mermaidConfigSchema),
...mermaidConfigSchema,
$defs: Object.fromEntries(
Object.entries(mermaidConfigSchema.$defs).map(([key, subSchema]) => {
return [key, removeRequired(replaceAllOfWithExtends(subSchema))];
return [key, replaceAllOfWithExtends(subSchema as JSONSchemaType<Record<string, any>>)];
})
),
};
const typescriptFile = await compile(
modifiedSchema as JSONSchema, // json-schema-to-typescript only allows JSON Schema 4 as input type
'MermaidConfig',
modifiedSchema as unknown as JSONSchema, // json-schema-to-typescript only allows JSON Schema 4 as input type
'MermaidConfigWithDefaults',
{
additionalProperties: false, // in JSON Schema 2019-09, these are called `unevaluatedProperties`
unreachableDefinitions: true, // definition for FontConfig is unreachable
style: prettierConfig,
style: (await prettier.resolveConfig('.')) ?? {},
}
);
@@ -191,6 +175,7 @@ async function main() {
}
const configJsonSchema = await loadJsonSchemaFromYaml();
// TODO: Add code to mark objects with default values as required
removeProp(configJsonSchema, 'default');

View File

@@ -52,7 +52,6 @@ import mm from 'micromatch';
// @ts-ignore No typescript declaration file
import flatmap from 'unist-util-flatmap';
import { visit } from 'unist-util-visit';
import { prettierConfig } from './prettier.js';
export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8'))
.version as string;
@@ -86,6 +85,7 @@ const LOGMSG_COPIED = `, and copied to ${FINAL_DOCS_DIR}`;
const WARN_DOCSDIR_DOESNT_MATCH = `Changed files were transformed in ${SOURCE_DOCS_DIR} but do not match the files in ${FINAL_DOCS_DIR}. Please run 'pnpm --filter mermaid run docs:build' after making changes to ${SOURCE_DOCS_DIR} to update the ${FINAL_DOCS_DIR} directory with the transformed files.`;
const prettierConfig = (await prettier.resolveConfig('.')) ?? {};
// From https://github.com/vuejs/vitepress/blob/428eec3750d6b5648a77ac52d88128df0554d4d1/src/node/markdownToVue.ts#L20-L21
const includesRE = /<!--\s*@include:\s*(.*?)\s*-->/g;
const includedFiles: Set<string> = new Set();
@@ -367,26 +367,26 @@ async function transformJsonSchema(file: string) {
});
/** Location of the `schema.yaml` files */
const schemaInputDir = 'src/schemas/';
const SCHEMA_INPUT_DIR = 'src/schemas/';
/**
* Location to store the generated `schema.json` file for the website
*
* Because Vitepress doesn't handle bundling `.json` files properly, we need
* to instead place it into a `public/` subdirectory.
*/
const schemaOutputDir = 'src/docs/public/schemas/';
const vitepressPublicDir = 'src/docs/public';
const SCHEMA_OUTPUT_DIR = 'src/docs/public/schemas/';
const VITEPRESS_PUBLIC_DIR = 'src/docs/public';
/**
* Location to store the generated Schema Markdown docs.
* Links to JSON Schemas should automatically be rewritten to point to
* `SCHEMA_OUTPUT_DIR`.
*/
const schemaMarkdownOutputDir = join('src', 'docs', 'config', 'schema-docs');
const SCHEMA_MARKDOWN_OUTPUT_DIR = join('src', 'docs', 'config', 'schema-docs');
// write .schema.json files
const jsonFileName = file
.replace('.schema.yaml', '.schema.json')
.replace(schemaInputDir, schemaOutputDir);
.replace(SCHEMA_INPUT_DIR, SCHEMA_OUTPUT_DIR);
copyTransformedContents(jsonFileName, !verifyOnly, JSON.stringify(jsonSchema, undefined, 2));
const schemas = traverseSchemas([schemaLoader()(jsonFileName, jsonSchema)]);
@@ -414,7 +414,7 @@ async function transformJsonSchema(file: string) {
* @returns New absolute Vitepress path to schema
*/
rewritelinks: (origin: string) => {
return `/${relative(vitepressPublicDir, origin)}`;
return `/${relative(VITEPRESS_PUBLIC_DIR, origin)}`;
},
})(schemas);
@@ -458,7 +458,7 @@ async function transformJsonSchema(file: string) {
parser: 'markdown',
...prettierConfig,
});
const newFileName = join(schemaMarkdownOutputDir, `${name}.md`);
const newFileName = join(SCHEMA_MARKDOWN_OUTPUT_DIR, `${name}.md`);
copyTransformedContents(newFileName, !verifyOnly, formatted);
}
}

View File

@@ -24,11 +24,11 @@ describe('docs.mts', () => {
describe('is a code block', () => {
const beforeCodeLine = 'test\n';
const diagramText = 'graph\n A --> B\n';
const diagram_text = 'graph\n A --> B\n';
describe('language = "mermaid-nocode"', () => {
const langKeyword = 'mermaid-nocode';
const contents = beforeCodeLine + '```' + langKeyword + '\n' + diagramText + '\n```\n';
const lang_keyword = 'mermaid-nocode';
const contents = beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
it('changes the language to "mermaid"', async () => {
const result = (
@@ -37,16 +37,17 @@ describe('docs.mts', () => {
.process(contents)
).toString();
expect(result).toEqual(
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagramText + '\n```\n'
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagram_text + '\n```\n'
);
});
});
describe('language = "mermaid" | "mmd" | "mermaid-example"', () => {
const mermaidKeywords = ['mermaid', 'mmd', 'mermaid-example'];
const mermaid_keywords = ['mermaid', 'mmd', 'mermaid-example'];
mermaidKeywords.forEach((langKeyword) => {
const contents = beforeCodeLine + '```' + langKeyword + '\n' + diagramText + '\n```\n';
mermaid_keywords.forEach((lang_keyword) => {
const contents =
beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', async () => {
const result = (
@@ -58,10 +59,10 @@ describe('docs.mts', () => {
beforeCodeLine +
'\n' +
'```mermaid-example\n' +
diagramText +
diagram_text +
'\n```\n' +
'\n```mermaid\n' +
diagramText +
diagram_text +
'\n```\n'
);
});
@@ -69,9 +70,9 @@ describe('docs.mts', () => {
});
it('calls transformToBlockQuote with the node information', async () => {
const langKeyword = 'note';
const lang_keyword = 'note';
const contents =
beforeCodeLine + '```' + langKeyword + '\n' + 'This is the text\n' + '```\n';
beforeCodeLine + '```' + lang_keyword + '\n' + 'This is the text\n' + '```\n';
const result = (
await remarkBuilder().use(transformMarkdownAst, { originalFilename }).process(contents)

View File

@@ -1,8 +0,0 @@
export const prettierConfig = {
endOfLine: 'auto',
printWidth: 100,
singleQuote: true,
useTabs: false,
tabWidth: 2,
trailingComma: 'es5',
} as const;

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { expect, it } from 'vitest';
import * as configApi from './config.js';
import type { MermaidConfig } from './config.type.js';
import type { MermaidConfig, MermaidConfigWithDefaults } from './config.type.js';
describe('when working with site config', () => {
beforeEach(() => {
@@ -8,32 +9,32 @@ describe('when working with site config', () => {
configApi.setSiteConfig({});
});
it('should set site config and config properly', () => {
const config0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config0);
const config1 = configApi.getSiteConfig();
const config2 = configApi.getConfig();
expect(config1.fontFamily).toEqual(config0.fontFamily);
expect(config1.fontSize).toEqual(config0.fontSize);
expect(config1).toEqual(config2);
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0);
const config_1 = configApi.getSiteConfig();
const config_2 = configApi.getConfig();
expect(config_1.fontFamily).toEqual(config_0.fontFamily);
expect(config_1.fontSize).toEqual(config_0.fontSize);
expect(config_1).toEqual(config_2);
});
it('should respect secure keys when applying directives', () => {
const config0: MermaidConfig = {
const config_0 = {
fontFamily: 'foo-font',
securityLevel: 'strict', // can't be changed
fontSize: 12345, // can't be changed
secure: [...configApi.defaultConfig.secure!, 'fontSize'],
};
configApi.setSiteConfig(config0);
} as MermaidConfigWithDefaults;
configApi.setSiteConfig(config_0);
const directive: MermaidConfig = {
fontFamily: 'baf',
// fontSize and securityLevel shouldn't be changed
fontSize: 54321,
securityLevel: 'loose',
};
const cfg: MermaidConfig = configApi.updateCurrentConfig(config0, [directive]);
const cfg: MermaidConfigWithDefaults = configApi.updateCurrentConfig(config_0, [directive]);
expect(cfg.fontFamily).toEqual(directive.fontFamily);
expect(cfg.fontSize).toBe(config0.fontSize);
expect(cfg.securityLevel).toBe(config0.securityLevel);
expect(cfg.fontSize).toBe(config_0.fontSize);
expect(cfg.securityLevel).toBe(config_0.securityLevel);
});
it('should allow setting partial options', () => {
const defaultConfig = configApi.getConfig();
@@ -52,30 +53,30 @@ describe('when working with site config', () => {
);
});
it('should set reset config properly', () => {
const config0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config0);
const config1 = { fontFamily: 'baf' };
configApi.setConfig(config1);
const config2 = configApi.getConfig();
expect(config2.fontFamily).toEqual(config1.fontFamily);
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0);
const config_1 = { fontFamily: 'baf' };
configApi.setConfig(config_1);
const config_2 = configApi.getConfig();
expect(config_2.fontFamily).toEqual(config_1.fontFamily);
configApi.reset();
const config3 = configApi.getConfig();
expect(config3.fontFamily).toEqual(config0.fontFamily);
const config4 = configApi.getSiteConfig();
expect(config4.fontFamily).toEqual(config0.fontFamily);
const config_3 = configApi.getConfig();
expect(config_3.fontFamily).toEqual(config_0.fontFamily);
const config_4 = configApi.getSiteConfig();
expect(config_4.fontFamily).toEqual(config_0.fontFamily);
});
it('should set global reset config properly', () => {
const config0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config0);
const config1 = configApi.getSiteConfig();
expect(config1.fontFamily).toEqual(config0.fontFamily);
const config2 = configApi.getConfig();
expect(config2.fontFamily).toEqual(config0.fontFamily);
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0);
const config_1 = configApi.getSiteConfig();
expect(config_1.fontFamily).toEqual(config_0.fontFamily);
const config_2 = configApi.getConfig();
expect(config_2.fontFamily).toEqual(config_0.fontFamily);
configApi.setConfig({ altFontFamily: 'bar-font' });
const config3 = configApi.getConfig();
expect(config3.altFontFamily).toEqual('bar-font');
const config_3 = configApi.getConfig();
expect(config_3.altFontFamily).toEqual('bar-font');
configApi.reset();
const config4 = configApi.getConfig();
expect(config4.altFontFamily).toBeUndefined();
const config_4 = configApi.getConfig();
expect(config_4.altFontFamily).toBeUndefined();
});
});

View File

@@ -1,20 +1,23 @@
import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfigWithDefaults, MermaidConfig } from './config.type.js';
import config from './defaultConfig.js';
import { log } from './logger.js';
import theme from './themes/index.js';
import config from './defaultConfig.js';
import type { MermaidConfig } from './config.type.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const defaultConfig: MermaidConfig = Object.freeze(config);
export const defaultConfig: MermaidConfigWithDefaults = Object.freeze(config);
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let siteConfig: MermaidConfigWithDefaults = assignWithDepth({}, defaultConfig);
let configFromInitialize: MermaidConfig;
let directives: MermaidConfig[] = [];
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let currentConfig: MermaidConfigWithDefaults = assignWithDepth({}, defaultConfig);
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => {
export const updateCurrentConfig = (
siteCfg: MermaidConfigWithDefaults,
_directives: MermaidConfig[]
) => {
// start with config being the siteConfig
let cfg: MermaidConfig = assignWithDepth({}, siteCfg);
let cfg: MermaidConfigWithDefaults = assignWithDepth({}, siteCfg);
// let sCfg = assignWithDepth(defaultConfig, siteConfigDelta);
// Join directives
@@ -58,7 +61,7 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: Mermaid
* @param conf - The base currentConfig to use as siteConfig
* @returns The new siteConfig
*/
export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => {
export const setSiteConfig = (conf: MermaidConfig): MermaidConfigWithDefaults => {
siteConfig = assignWithDepth({}, defaultConfig);
siteConfig = assignWithDepth(siteConfig, conf);
@@ -76,7 +79,7 @@ export const saveConfigFromInitialize = (conf: MermaidConfig): void => {
configFromInitialize = assignWithDepth({}, conf);
};
export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => {
export const updateSiteConfig = (conf: MermaidConfig): MermaidConfigWithDefaults => {
siteConfig = assignWithDepth(siteConfig, conf);
updateCurrentConfig(siteConfig, directives);
@@ -93,7 +96,7 @@ export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => {
*
* @returns The siteConfig
*/
export const getSiteConfig = (): MermaidConfig => {
export const getSiteConfig = (): MermaidConfigWithDefaults => {
return assignWithDepth({}, siteConfig);
};
/**
@@ -110,7 +113,7 @@ export const getSiteConfig = (): MermaidConfig => {
* @param conf - The potential currentConfig
* @returns The currentConfig merged with the sanitized conf
*/
export const setConfig = (conf: MermaidConfig): MermaidConfig => {
export const setConfig = (conf: MermaidConfig): MermaidConfigWithDefaults => {
checkConfig(conf);
assignWithDepth(currentConfig, conf);
@@ -128,7 +131,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => {
*
* @returns The currentConfig
*/
export const getConfig = (): MermaidConfig => {
export const getConfig = (): MermaidConfigWithDefaults => {
return assignWithDepth({}, currentConfig);
};
/**

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,13 @@ export default intersectPolygon;
* @param point
*/
function intersectPolygon(node, polyPoints, point) {
let x1 = node.x;
let y1 = node.y;
var x1 = node.x;
var y1 = node.y;
let intersections = [];
var intersections = [];
let minX = Number.POSITIVE_INFINITY;
let minY = Number.POSITIVE_INFINITY;
var minX = Number.POSITIVE_INFINITY;
var minY = Number.POSITIVE_INFINITY;
if (typeof polyPoints.forEach === 'function') {
polyPoints.forEach(function (entry) {
minX = Math.min(minX, entry.x);
@@ -30,13 +30,13 @@ function intersectPolygon(node, polyPoints, point) {
minY = Math.min(minY, polyPoints.y);
}
let left = x1 - node.width / 2 - minX;
let top = y1 - node.height / 2 - minY;
var left = x1 - node.width / 2 - minX;
var top = y1 - node.height / 2 - minY;
for (let i = 0; i < polyPoints.length; i++) {
let p1 = polyPoints[i];
let p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
let intersect = intersectLine(
for (var i = 0; i < polyPoints.length; i++) {
var p1 = polyPoints[i];
var p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];
var intersect = intersectLine(
node,
point,
{ x: left + p1.x, y: top + p1.y },
@@ -55,13 +55,13 @@ function intersectPolygon(node, polyPoints, point) {
if (intersections.length > 1) {
// More intersections, find the one nearest to edge end point
intersections.sort(function (p, q) {
let pdx = p.x - point.x;
let pdy = p.y - point.y;
let distp = Math.sqrt(pdx * pdx + pdy * pdy);
var pdx = p.x - point.x;
var pdy = p.y - point.y;
var distp = Math.sqrt(pdx * pdx + pdy * pdy);
let qdx = q.x - point.x;
let qdy = q.y - point.y;
let distq = Math.sqrt(qdx * qdx + qdy * qdy);
var qdx = q.x - point.x;
var qdy = q.y - point.y;
var distq = Math.sqrt(qdx * qdx + qdy * qdy);
return distp < distq ? -1 : distp === distq ? 0 : 1;
});

View File

@@ -119,7 +119,7 @@ const hexagon = async (parent, node) => {
return shapeSvg;
};
const blockArrow = async (parent, node) => {
const block_arrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const f = 2;
@@ -140,7 +140,7 @@ const blockArrow = async (parent, node) => {
return shapeSvg;
};
const rectLeftInvArrow = async (parent, node) => {
const rect_left_inv_arrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -171,7 +171,7 @@ const rectLeftInvArrow = async (parent, node) => {
return shapeSvg;
};
const leanRight = async (parent, node) => {
const lean_right = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(parent, node, getClassesFromNode(node), true);
const w = bbox.width + node.padding;
@@ -194,7 +194,7 @@ const leanRight = async (parent, node) => {
return shapeSvg;
};
const leanLeft = async (parent, node) => {
const lean_left = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -250,7 +250,7 @@ const trapezoid = async (parent, node) => {
return shapeSvg;
};
const invTrapezoid = async (parent, node) => {
const inv_trapezoid = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -278,7 +278,7 @@ const invTrapezoid = async (parent, node) => {
return shapeSvg;
};
const rectRightInvArrow = async (parent, node) => {
const rect_right_inv_arrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -875,7 +875,7 @@ const end = (parent, node) => {
return shapeSvg;
};
const classBox = (parent, node) => {
const class_box = (parent, node) => {
const halfPadding = node.padding / 2;
const rowPadding = 4;
const lineHeight = 8;
@@ -1111,13 +1111,13 @@ const shapes = {
doublecircle,
stadium,
hexagon,
block_arrow: blockArrow,
rect_left_inv_arrow: rectLeftInvArrow,
lean_right: leanRight,
lean_left: leanLeft,
block_arrow,
rect_left_inv_arrow,
lean_right,
lean_left,
trapezoid,
inv_trapezoid: invTrapezoid,
rect_right_inv_arrow: rectRightInvArrow,
inv_trapezoid,
rect_right_inv_arrow,
cylinder,
start,
end,
@@ -1125,7 +1125,7 @@ const shapes = {
subroutine,
fork: forkJoin,
join: forkJoin,
class_box: classBox,
class_box,
};
let nodeElems = {};

View File

@@ -1,8 +1,5 @@
import type { RequiredDeep } from 'type-fest';
import type { MermaidConfigWithDefaults } from './config.type.js';
import theme from './themes/index.js';
import type { MermaidConfig } from './config.type.js';
// Uses our custom Vite jsonSchemaPlugin to load only the default values from
// our JSON Schema
// @ts-expect-error This file is automatically generated via a custom Vite plugin
@@ -15,7 +12,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: RequiredDeep<MermaidConfig> = {
const config: MermaidConfigWithDefaults = {
...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?

View File

@@ -1,13 +1,13 @@
import type { MermaidConfig } from '../config.type.js';
import type { MermaidConfigWithDefaults } from '../config.type.js';
import { UnknownDiagramError } from '../errors.js';
import { log } from '../logger.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import type {
DetectorRecord,
DiagramDetector,
DiagramLoader,
ExternalDiagramDefinition,
} from './types.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import { UnknownDiagramError } from '../errors.js';
export const detectors: Record<string, DetectorRecord> = {};
@@ -33,7 +33,7 @@ export const detectors: Record<string, DetectorRecord> = {};
* @param config - The mermaid config.
* @returns A graph definition key
*/
export const detectType = function (text: string, config?: MermaidConfig): string {
export const detectType = function (text: string, config?: MermaidConfigWithDefaults): string {
text = text
.replace(frontMatterRegex, '')
.replace(directiveRegex, '')

View File

@@ -1,4 +1,5 @@
import { it, describe, expect } from 'vitest';
import { describe, expect, it, beforeAll } from 'vitest';
import type { MermaidConfigWithDefaults } from '../config.type.js';
import { detectType } from './detectType.js';
import { addDiagrams } from './diagram-orchestration.js';
@@ -46,30 +47,40 @@ describe('diagram-orchestration', () => {
// graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B')).toBe('flowchart');
// graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })).toBe(
'flowchart'
);
expect(
detectType('graph TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-d3' },
} as MermaidConfigWithDefaults)
).toBe('flowchart');
// flowchart & dagre-d3 => error
expect(() =>
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })
detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-d3' },
} as MermaidConfigWithDefaults)
).toThrowErrorMatchingInlineSnapshot(
`[UnknownDiagramError: No diagram type detected matching given configuration for text: flowchart TD; A-->B]`
);
// graph & dagre-wrapper => flowchart-v2
expect(
detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } })
detectType('graph TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-wrapper' },
} as MermaidConfigWithDefaults)
).toBe('flowchart-v2');
// flowchart ==> flowchart-v2
expect(detectType('flowchart TD; A-->B')).toBe('flowchart-v2');
// flowchart && dagre-wrapper ==> flowchart-v2
expect(
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } })
detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-wrapper' },
} as MermaidConfigWithDefaults)
).toBe('flowchart-v2');
// flowchart && elk ==> flowchart-elk
expect(detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'elk' } })).toBe(
'flowchart-elk'
);
expect(
detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'elk' },
} as MermaidConfigWithDefaults)
).toBe('flowchart-elk');
});
it('should not detect flowchart if pie contains flowchart', () => {

View File

@@ -70,7 +70,7 @@ export interface DiagramRenderer {
getClasses?: (
text: string,
diagram: Pick<DiagramDefinition, 'db'>
) => Map<string, DiagramStyleClassDef>;
) => Record<string, DiagramStyleClassDef>;
}
export interface DiagramDefinition {

View File

@@ -70,9 +70,8 @@ describe('diagram detection', () => {
--------------^
Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF']
`);
await expect(
Diagram.fromText('sequenceDiagram; A-->B')
).rejects.toThrowErrorMatchingInlineSnapshot(`
await expect(Diagram.fromText('sequenceDiagram; A-->B')).rejects
.toThrowErrorMatchingInlineSnapshot(`
[Error: Parse error on line 1:
...quenceDiagram; A-->B
-----------------------^

View File

@@ -1,26 +1,21 @@
import clone from 'lodash-es/clone.js';
import * as configApi from '../../config.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
import common from '../common/common.js';
import { clear as commonClear } from '../common/commonDb.js';
import type { Block, ClassDef } from './blockTypes.js';
// Initialize the node database for simple lookups
let blockDatabase: Map<string, Block> = new Map();
let blockDatabase: Record<string, Block> = {};
let edgeList: Block[] = [];
let edgeCount: Map<string, number> = new Map();
let edgeCount: Record<string, number> = {};
const COLOR_KEYWORD = 'color';
const FILL_KEYWORD = 'fill';
const BG_FILL = 'bgFill';
const STYLECLASS_SEP = ',';
const config = getConfig();
let classes: Map<string, ClassDef> = new Map();
const sanitizeText = (txt: string) => common.sanitizeText(txt, config);
let classes = {} as Record<string, ClassDef>;
/**
* Called when the parser comes across a (style) class definition
@@ -31,11 +26,10 @@ const sanitizeText = (txt: string) => common.sanitizeText(txt, config);
*/
export const addStyleClass = function (id: string, styleAttributes = '') {
// create a new style class object with this id
let foundClass = classes.get(id);
if (!foundClass) {
foundClass = { id: id, styles: [], textStyles: [] };
classes.set(id, foundClass); // This is a classDef
if (classes[id] === undefined) {
classes[id] = { id: id, styles: [], textStyles: [] }; // This is a classDef
}
const foundClass = classes[id];
if (styleAttributes !== undefined && styleAttributes !== null) {
styleAttributes.split(STYLECLASS_SEP).forEach((attrib) => {
// remove any trailing ;
@@ -60,7 +54,7 @@ export const addStyleClass = function (id: string, styleAttributes = '') {
* @param styles - the string with 1 or more style attributes (each separated by a comma)
*/
export const addStyle2Node = function (id: string, styles = '') {
const foundBlock = blockDatabase.get(id)!;
const foundBlock = blockDatabase[id];
if (styles !== undefined && styles !== null) {
foundBlock.styles = styles.split(STYLECLASS_SEP);
}
@@ -76,11 +70,11 @@ export const addStyle2Node = function (id: string, styles = '') {
*/
export const setCssClass = function (itemIds: string, cssClassName: string) {
itemIds.split(',').forEach(function (id: string) {
let foundBlock = blockDatabase.get(id);
let foundBlock = blockDatabase[id];
if (foundBlock === undefined) {
const trimmedId = id.trim();
foundBlock = { id: trimmedId, type: 'na', children: [] } as Block;
blockDatabase.set(trimmedId, foundBlock);
blockDatabase[trimmedId] = { id: trimmedId, type: 'na', children: [] } as Block;
foundBlock = blockDatabase[trimmedId];
}
if (!foundBlock.classes) {
foundBlock.classes = [];
@@ -93,9 +87,6 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block):
const blockList = _blockList.flat();
const children = [];
for (const block of blockList) {
if (block.label) {
block.label = sanitizeText(block.label);
}
if (block.type === 'classDef') {
addStyleClass(block.id, block.css);
continue;
@@ -113,9 +104,12 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block):
if (block.type === 'column-setting') {
parent.columns = block.columns || -1;
} else if (block.type === 'edge') {
const count = (edgeCount.get(block.id) ?? 0) + 1;
edgeCount.set(block.id, count);
block.id = count + '-' + block.id;
if (edgeCount[block.id]) {
edgeCount[block.id]++;
} else {
edgeCount[block.id] = 1;
}
block.id = edgeCount[block.id] + '-' + block.id;
edgeList.push(block);
} else {
if (!block.label) {
@@ -126,17 +120,16 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block):
block.label = block.id;
}
}
const existingBlock = blockDatabase.get(block.id);
if (existingBlock === undefined) {
blockDatabase.set(block.id, block);
const newBlock = !blockDatabase[block.id];
if (newBlock) {
blockDatabase[block.id] = block;
} else {
// Add newer relevant data to aggregated node
if (block.type !== 'na') {
existingBlock.type = block.type;
blockDatabase[block.id].type = block.type;
}
if (block.label !== block.id) {
existingBlock.label = block.label;
blockDatabase[block.id].label = block.label;
}
}
@@ -149,10 +142,10 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block):
for (let j = 0; j < w; j++) {
const newBlock = clone(block);
newBlock.id = newBlock.id + '-' + j;
blockDatabase.set(newBlock.id, newBlock);
blockDatabase[newBlock.id] = newBlock;
children.push(newBlock);
}
} else if (existingBlock === undefined) {
} else if (newBlock) {
children.push(block);
}
}
@@ -167,12 +160,12 @@ const clear = (): void => {
log.debug('Clear called');
commonClear();
rootBlock = { id: 'root', type: 'composite', children: [], columns: -1 } as Block;
blockDatabase = new Map([['root', rootBlock]]);
blockDatabase = { root: rootBlock };
blocks = [] as Block[];
classes = new Map();
classes = {} as Record<string, ClassDef>;
edgeList = [];
edgeCount = new Map();
edgeCount = {};
};
export function typeStr2Type(typeStr: string) {
@@ -248,7 +241,7 @@ const setHierarchy = (block: Block[]): void => {
};
const getColumns = (blockId: string): number => {
const block = blockDatabase.get(blockId);
const block = blockDatabase[blockId];
if (!block) {
return -1;
}
@@ -266,7 +259,7 @@ const getColumns = (blockId: string): number => {
* @returns
*/
const getBlocksFlat = () => {
return [...blockDatabase.values()];
return [...Object.values(blockDatabase)];
};
/**
* Returns the hierarchy of blocks
@@ -280,11 +273,11 @@ const getEdges = () => {
return edgeList;
};
const getBlock = (id: string) => {
return blockDatabase.get(id);
return blockDatabase[id];
};
const setBlock = (block: Block) => {
blockDatabase.set(block.id, block);
blockDatabase[block.id] = block;
};
const getLogger = () => console;

View File

@@ -5,7 +5,7 @@ import {
} from 'd3';
import type { Diagram } from '../../Diagram.js';
import * as configApi from '../../config.js';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
import insertMarkers from '../../dagre-wrapper/markers.js';
import { log } from '../../logger.js';
import { configureSvgSize } from '../../setupGraphViewbox.js';
@@ -75,7 +75,7 @@ export const draw = async function (
const magicFactor = Math.max(1, Math.round(0.125 * (bounds2.width / bounds2.height)));
const height = bounds2.height + magicFactor + 10;
const width = bounds2.width + 10;
const { useMaxWidth } = conf as Exclude<MermaidConfig['block'], undefined>;
const { useMaxWidth } = conf as Exclude<MermaidConfigWithDefaults['block'], undefined>;
configureSvgSize(svg, height, width, !!useMaxWidth);
log.debug('Here Bounds', bounds, bounds2);
svg.attr(

View File

@@ -388,7 +388,7 @@ describe('Block diagram', function () {
const mc = blocks[0];
expect(mc.classes).toContain('black');
const classes = db.getClasses();
const black = classes.get('black')!;
const black = classes.black;
expect(black.id).toBe('black');
expect(black.styles[0]).toEqual('color:#ffffff');
});
@@ -406,21 +406,4 @@ columns 1
expect(B.styles).toContain('fill:#f9F');
});
});
describe('prototype properties', function () {
function validateProperty(prop: string) {
expect(() => block.parse(`block-beta\n${prop}`)).not.toThrow();
expect(() =>
block.parse(`block-beta\nA; classDef ${prop} color:#ffffff,fill:#000000; class A ${prop}`)
).not.toThrow();
}
it('should work with a __proto__ property', function () {
validateProperty('__proto__');
});
it('should work with a constructor property', function () {
validateProperty('constructor');
});
});
});

View File

@@ -1,17 +1,16 @@
// @ts-ignore: JISON doesn't support types
import parser from './parser/c4Diagram.jison';
import type { DiagramDefinition } from '../../diagram-api/types.js';
import db from './c4Db.js';
import renderer from './c4Renderer.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/c4Diagram.jison';
import styles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
parser,
db,
renderer,
styles,
init: ({ c4, wrap }: MermaidConfig) => {
init: ({ c4, wrap }) => {
renderer.setConf(c4);
db.setWrap(wrap);
},

View File

@@ -256,7 +256,7 @@ boundaryStartStatement
boundaryStart
: ENTERPRISE_BOUNDARY attributes {$2.splice(2, 0, 'ENTERPRISE'); yy.addPersonOrSystemBoundary(...$2); $$=$2;}
| SYSTEM_BOUNDARY attributes {$2.splice(2, 0, 'SYSTEM'); yy.addPersonOrSystemBoundary(...$2); $$=$2;}
| SYSTEM_BOUNDARY attributes {$2.splice(2, 0, 'ENTERPRISE'); yy.addPersonOrSystemBoundary(...$2); $$=$2;}
| BOUNDARY attributes {yy.addPersonOrSystemBoundary(...$2); $$=$2;}
| CONTAINER_BOUNDARY attributes {$2.splice(2, 0, 'CONTAINER'); yy.addContainerBoundary(...$2); $$=$2;}
| NODE attributes {yy.addDeploymentNode('node', ...$2); $$=$2;}

View File

@@ -26,10 +26,10 @@ import type {
const MERMAID_DOM_ID_PREFIX = 'classId-';
let relations: ClassRelation[] = [];
let classes: Map<string, ClassNode> = new Map();
let classes: ClassMap = {};
let notes: ClassNote[] = [];
let classCounter = 0;
let namespaces: Map<string, NamespaceNode> = new Map();
let namespaces: NamespaceMap = {};
let namespaceCounter = 0;
let functions: any[] = [];
@@ -57,7 +57,7 @@ export const setClassLabel = function (_id: string, label: string) {
}
const { className } = splitClassNameAndType(id);
classes.get(className)!.label = label;
classes[className].label = label;
};
/**
@@ -70,13 +70,13 @@ export const addClass = function (_id: string) {
const id = common.sanitizeText(_id, getConfig());
const { className, type } = splitClassNameAndType(id);
// Only add class if not exists
if (classes.has(className)) {
if (Object.hasOwn(classes, className)) {
return;
}
// alert('Adding class: ' + className);
const name = common.sanitizeText(className, getConfig());
// alert('Adding class after: ' + name);
classes.set(name, {
classes[name] = {
id: name,
type: type,
label: name,
@@ -86,7 +86,7 @@ export const addClass = function (_id: string) {
annotations: [],
styles: [],
domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter,
} as ClassNode);
} as ClassNode;
classCounter++;
};
@@ -99,25 +99,25 @@ export const addClass = function (_id: string) {
*/
export const lookUpDomId = function (_id: string): string {
const id = common.sanitizeText(_id, getConfig());
if (classes.has(id)) {
return classes.get(id)!.domId;
if (id in classes) {
return classes[id].domId;
}
throw new Error('Class not found: ' + id);
};
export const clear = function () {
relations = [];
classes = new Map();
classes = {};
notes = [];
functions = [];
functions.push(setupToolTips);
namespaces = new Map();
namespaces = {};
namespaceCounter = 0;
commonClear();
};
export const getClass = function (id: string): ClassNode {
return classes.get(id)!;
return classes[id];
};
export const getClasses = function (): ClassMap {
@@ -157,7 +157,7 @@ export const addRelation = function (relation: ClassRelation) {
*/
export const addAnnotation = function (className: string, annotation: string) {
const validatedClassName = splitClassNameAndType(className).className;
classes.get(validatedClassName)!.annotations.push(annotation);
classes[validatedClassName].annotations.push(annotation);
};
/**
@@ -173,7 +173,7 @@ export const addMember = function (className: string, member: string) {
addClass(className);
const validatedClassName = splitClassNameAndType(className).className;
const theClass = classes.get(validatedClassName)!;
const theClass = classes[validatedClassName];
if (typeof member === 'string') {
// Member can contain white spaces, we trim them out
@@ -226,9 +226,8 @@ export const setCssClass = function (ids: string, className: string) {
if (_id[0].match(/\d/)) {
id = MERMAID_DOM_ID_PREFIX + id;
}
const classNode = classes.get(id);
if (classNode) {
classNode.cssClasses.push(className);
if (classes[id] !== undefined) {
classes[id].cssClasses.push(className);
}
});
};
@@ -242,17 +241,17 @@ export const setCssClass = function (ids: string, className: string) {
const setTooltip = function (ids: string, tooltip?: string) {
ids.split(',').forEach(function (id) {
if (tooltip !== undefined) {
classes.get(id)!.tooltip = sanitizeText(tooltip);
classes[id].tooltip = sanitizeText(tooltip);
}
});
};
export const getTooltip = function (id: string, namespace?: string) {
if (namespace && namespaces.has(namespace)) {
return namespaces.get(namespace)!.classes.get(id)!.tooltip;
if (namespace) {
return namespaces[namespace].classes[id].tooltip;
}
return classes.get(id)!.tooltip;
return classes[id].tooltip;
};
/**
@@ -269,15 +268,14 @@ export const setLink = function (ids: string, linkStr: string, target: string) {
if (_id[0].match(/\d/)) {
id = MERMAID_DOM_ID_PREFIX + id;
}
const theClass = classes.get(id);
if (theClass) {
theClass.link = utils.formatUrl(linkStr, config);
if (classes[id] !== undefined) {
classes[id].link = utils.formatUrl(linkStr, config);
if (config.securityLevel === 'sandbox') {
theClass.linkTarget = '_top';
classes[id].linkTarget = '_top';
} else if (typeof target === 'string') {
theClass.linkTarget = sanitizeText(target);
classes[id].linkTarget = sanitizeText(target);
} else {
theClass.linkTarget = '_blank';
classes[id].linkTarget = '_blank';
}
}
});
@@ -294,7 +292,7 @@ export const setLink = function (ids: string, linkStr: string, target: string) {
export const setClickEvent = function (ids: string, functionName: string, functionArgs: string) {
ids.split(',').forEach(function (id) {
setClickFunc(id, functionName, functionArgs);
classes.get(id)!.haveCallback = true;
classes[id].haveCallback = true;
});
setCssClass(ids, 'clickable');
};
@@ -310,7 +308,7 @@ const setClickFunc = function (_domId: string, functionName: string, functionArg
}
const id = domId;
if (classes.has(id)) {
if (classes[id] !== undefined) {
const elemId = lookUpDomId(id);
let argList: string[] = [];
if (typeof functionArgs === 'string') {
@@ -419,22 +417,22 @@ const setDirection = (dir: string) => {
* @public
*/
export const addNamespace = function (id: string) {
if (namespaces.has(id)) {
if (namespaces[id] !== undefined) {
return;
}
namespaces.set(id, {
namespaces[id] = {
id: id,
classes: new Map(),
classes: {},
children: {},
domId: MERMAID_DOM_ID_PREFIX + id + '-' + namespaceCounter,
} as NamespaceNode);
} as NamespaceNode;
namespaceCounter++;
};
const getNamespace = function (name: string): NamespaceNode {
return namespaces.get(name)!;
return namespaces[name];
};
const getNamespaces = function (): NamespaceMap {
@@ -449,18 +447,18 @@ const getNamespaces = function (): NamespaceMap {
* @public
*/
export const addClassesToNamespace = function (id: string, classNames: string[]) {
if (!namespaces.has(id)) {
if (namespaces[id] === undefined) {
return;
}
for (const name of classNames) {
const { className } = splitClassNameAndType(name);
classes.get(className)!.parent = id;
namespaces.get(id)!.classes.set(className, classes.get(className)!);
classes[className].parent = id;
namespaces[id].classes[className] = classes[className];
}
};
export const setCssStyle = function (id: string, styles: string[]) {
const thisClass = classes.get(id);
const thisClass = classes[id];
if (!styles || !thisClass) {
return;
}

View File

@@ -2,7 +2,6 @@
import { parser } from './parser/classDiagram.jison';
import classDb from './classDb.js';
import { vi, describe, it, expect } from 'vitest';
import type { ClassMap, NamespaceNode } from './classTypes.js';
const spyOn = vi.spyOn;
const staticCssStyle = 'text-decoration:underline;';
@@ -393,8 +392,8 @@ class C13["With Città foreign language"]
Student "1" --o "1" IdCard : carries
Student "1" --o "1" Bike : rides`);
expect(classDb.getClasses().size).toBe(3);
expect(classDb.getClasses().get('Student')).toMatchInlineSnapshot(`
expect(Object.keys(classDb.getClasses()).length).toBe(3);
expect(classDb.getClasses().Student).toMatchInlineSnapshot(`
{
"annotations": [],
"cssClasses": [],
@@ -1540,12 +1539,12 @@ class Class2
}`;
parser.parse(str);
const testNamespace: NamespaceNode = parser.yy.getNamespace('Namespace1');
const testClasses: ClassMap = parser.yy.getClasses();
expect(testNamespace.classes.size).toBe(2);
const testNamespace = parser.yy.getNamespace('Namespace1');
const testClasses = parser.yy.getClasses();
expect(Object.keys(testNamespace.classes).length).toBe(2);
expect(Object.keys(testNamespace.children).length).toBe(0);
expect(testNamespace.classes.get('Class1')?.id).toBe('Class1');
expect(testClasses.size).toBe(2);
expect(testNamespace.classes['Class1'].id).toBe('Class1');
expect(Object.keys(testClasses).length).toBe(2);
});
it('should add relations between classes of different namespaces', function () {
@@ -1574,25 +1573,25 @@ class Class2
const testNamespaceB = parser.yy.getNamespace('B');
const testClasses = parser.yy.getClasses();
const testRelations = parser.yy.getRelations();
expect(testNamespaceA.classes.size).toBe(2);
expect(testNamespaceA.classes.get('A1').members[0].getDisplayDetails().displayText).toBe(
expect(Object.keys(testNamespaceA.classes).length).toBe(2);
expect(testNamespaceA.classes['A1'].members[0].getDisplayDetails().displayText).toBe(
'+foo : string'
);
expect(testNamespaceA.classes.get('A2').members[0].getDisplayDetails().displayText).toBe(
expect(testNamespaceA.classes['A2'].members[0].getDisplayDetails().displayText).toBe(
'+bar : int'
);
expect(testNamespaceB.classes.size).toBe(2);
expect(testNamespaceB.classes.get('B1').members[0].getDisplayDetails().displayText).toBe(
expect(Object.keys(testNamespaceB.classes).length).toBe(2);
expect(testNamespaceB.classes['B1'].members[0].getDisplayDetails().displayText).toBe(
'+foo : bool'
);
expect(testNamespaceB.classes.get('B2').members[0].getDisplayDetails().displayText).toBe(
expect(testNamespaceB.classes['B2'].members[0].getDisplayDetails().displayText).toBe(
'+bar : float'
);
expect(testClasses.size).toBe(4);
expect(testClasses.get('A1').parent).toBe('A');
expect(testClasses.get('A2').parent).toBe('A');
expect(testClasses.get('B1').parent).toBe('B');
expect(testClasses.get('B2').parent).toBe('B');
expect(Object.keys(testClasses).length).toBe(4);
expect(testClasses['A1'].parent).toBe('A');
expect(testClasses['A2'].parent).toBe('A');
expect(testClasses['B1'].parent).toBe('B');
expect(testClasses['B2'].parent).toBe('B');
expect(testRelations[0].id1).toBe('A1');
expect(testRelations[0].id2).toBe('B1');
expect(testRelations[1].id1).toBe('A2');

View File

@@ -44,11 +44,14 @@ export const addNamespaces = function (
_id: string,
diagObj: any
) {
log.info('keys:', [...namespaces.keys()]);
const keys = Object.keys(namespaces);
log.info('keys:', keys);
log.info(namespaces);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
namespaces.forEach(function (vertex) {
keys.forEach(function (id) {
const vertex = namespaces[id];
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
const shape = 'rect';
@@ -86,13 +89,16 @@ export const addClasses = function (
diagObj: any,
parent?: string
) {
log.info('keys:', [...classes.keys()]);
const keys = Object.keys(classes);
log.info('keys:', keys);
log.info(classes);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
[...classes.values()]
.filter((vertex) => vertex.parent === parent)
.forEach(function (vertex) {
keys
.filter((id) => classes[id].parent == parent)
.forEach(function (id) {
const vertex = classes[id];
/**
* Variable for storing the classes for the vertex
*/
@@ -181,7 +187,7 @@ export const addNotes = function (
g.setNode(vertex.id, node);
log.info('setNode', node);
if (!vertex.class || !classes.has(vertex.class)) {
if (!vertex.class || !(vertex.class in classes)) {
return;
}
const edgeId = startEdgeId + i;
@@ -343,7 +349,7 @@ export const draw = async function (text: string, id: string, _version: string,
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement!.nodes()[0]!.contentDocument.body)
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const svg = root.select(`[id="${id}"]`);
@@ -363,8 +369,7 @@ export const draw = async function (text: string, id: string, _version: string,
// Add label rects for non html labels
if (!conf?.htmlLabels) {
const doc =
securityLevel === 'sandbox' ? sandboxElement!.nodes()[0]!.contentDocument : document;
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
for (const label of labels) {
// Get dimensions of label

View File

@@ -175,10 +175,10 @@ export const draw = function (text, id, _version, diagObj) {
});
const classes = diagObj.db.getClasses();
const keys = [...classes.keys()];
const keys = Object.keys(classes);
for (const key of keys) {
const classDef = classes.get(key);
const classDef = classes[key];
const node = svgDraw.drawClass(diagram, classDef, conf, diagObj);
idCache[node.id] = node;
@@ -216,7 +216,7 @@ export const draw = function (text, id, _version, diagObj) {
// metadata about the node. In this case we're going to add labels to each of
// our nodes.
g.setNode(node.id, node);
if (note.class && classes.has(note.class)) {
if (note.class && note.class in classes) {
g.setEdge(
note.id,
getGraphId(note.class),

View File

@@ -161,5 +161,5 @@ export interface NamespaceNode {
children: NamespaceMap;
}
export type ClassMap = Map<string, ClassNode>;
export type NamespaceMap = Map<string, NamespaceNode>;
export type ClassMap = Record<string, ClassNode>;
export type NamespaceMap = Record<string, NamespaceNode>;

View File

@@ -1,16 +0,0 @@
import { parser } from './classDiagram.jison';
import classDb from '../classDb.js';
describe('class diagram', function () {
beforeEach(function () {
parser.yy = classDb;
parser.yy.clear();
});
describe('prototype properties', function () {
it.each(['__proto__', 'constructor'])('should work with a %s property', function (prop) {
expect(() => parser.parse(`classDiagram\nclass ${prop}`)).not.toThrow();
expect(() => parser.parse(`classDiagram\nnamespace ${prop} {\n\tclass A\n}`)).not.toThrow();
});
});
});

View File

@@ -78,28 +78,28 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
x = labelPosition.x;
y = labelPosition.y;
let p1CardX, p1CardY;
let p2CardX, p2CardY;
let p1_card_x, p1_card_y;
let p2_card_x, p2_card_y;
if (l % 2 !== 0 && l > 1) {
let cardinality1Point = utils.calcCardinalityPosition(
let cardinality_1_point = utils.calcCardinalityPosition(
relation.relation.type1 !== 'none',
path.points,
path.points[0]
);
let cardinality2Point = utils.calcCardinalityPosition(
let cardinality_2_point = utils.calcCardinalityPosition(
relation.relation.type2 !== 'none',
path.points,
path.points[l - 1]
);
log.debug('cardinality_1_point ' + JSON.stringify(cardinality1Point));
log.debug('cardinality_2_point ' + JSON.stringify(cardinality2Point));
log.debug('cardinality_1_point ' + JSON.stringify(cardinality_1_point));
log.debug('cardinality_2_point ' + JSON.stringify(cardinality_2_point));
p1CardX = cardinality1Point.x;
p1CardY = cardinality1Point.y;
p2CardX = cardinality2Point.x;
p2CardY = cardinality2Point.y;
p1_card_x = cardinality_1_point.x;
p1_card_y = cardinality_1_point.y;
p2_card_x = cardinality_2_point.x;
p2_card_y = cardinality_2_point.y;
}
if (relation.title !== undefined) {
@@ -129,8 +129,8 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
const g = elem.append('g').attr('class', 'cardinality');
g.append('text')
.attr('class', 'type1')
.attr('x', p1CardX)
.attr('y', p1CardY)
.attr('x', p1_card_x)
.attr('y', p1_card_y)
.attr('fill', 'black')
.attr('font-size', '6')
.text(relation.relationTitle1);
@@ -139,8 +139,8 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
const g = elem.append('g').attr('class', 'cardinality');
g.append('text')
.attr('class', 'type2')
.attr('x', p2CardX)
.attr('y', p2CardY)
.attr('x', p2_card_x)
.attr('y', p2_card_y)
.attr('fill', 'black')
.attr('font-size', '6')
.text(relation.relationTitle2);

View File

@@ -1,4 +1,6 @@
import { sanitizeText, removeScript, parseGenericTypes, countOccurrence } from './common.js';
import { describe, expect, it } from 'vitest';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
import { countOccurrence, parseGenericTypes, removeScript, sanitizeText } from './common.js';
describe('when securityLevel is antiscript, all script must be removed', () => {
/**
@@ -67,7 +69,7 @@ describe('Sanitize text', () => {
const result = sanitizeText(maliciousStr, {
securityLevel: 'strict',
flowchart: { htmlLabels: true },
});
} as MermaidConfigWithDefaults);
expect(result).not.toContain('javascript:alert(1)');
});
});

View File

@@ -1,5 +1,5 @@
import DOMPurify from 'dompurify';
import type { MermaidConfig } from '../../config.type.js';
import type { MermaidConfigWithDefaults } from '../../config.type.js';
// Remove and ignore br:s
export const lineBreakRegex = /<br\s*\/?>/gi;
@@ -30,18 +30,18 @@ const setupDompurifyHooksIfNotSetup = (() => {
})();
function setupDompurifyHooks() {
const temporaryAttribute = 'data-temp-href-target';
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target';
DOMPurify.addHook('beforeSanitizeAttributes', (node: Element) => {
if (node.tagName === 'A' && node.hasAttribute('target')) {
node.setAttribute(temporaryAttribute, node.getAttribute('target') || '');
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') || '');
}
});
DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => {
if (node.tagName === 'A' && node.hasAttribute(temporaryAttribute)) {
node.setAttribute('target', node.getAttribute(temporaryAttribute) || '');
node.removeAttribute(temporaryAttribute);
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) || '');
node.removeAttribute(TEMPORARY_ATTRIBUTE);
if (node.getAttribute('target') === '_blank') {
node.setAttribute('rel', 'noopener');
}
@@ -63,7 +63,7 @@ export const removeScript = (txt: string): string => {
return sanitizedText;
};
const sanitizeMore = (text: string, config: MermaidConfig) => {
const sanitizeMore = (text: string, config: MermaidConfigWithDefaults) => {
if (config.flowchart?.htmlLabels !== false) {
const level = config.securityLevel;
if (level === 'antiscript' || level === 'strict') {
@@ -78,7 +78,7 @@ const sanitizeMore = (text: string, config: MermaidConfig) => {
return text;
};
export const sanitizeText = (text: string, config: MermaidConfig): string => {
export const sanitizeText = (text: string, config: MermaidConfigWithDefaults): string => {
if (!text) {
return text;
}
@@ -94,7 +94,7 @@ export const sanitizeText = (text: string, config: MermaidConfig): string => {
export const sanitizeTextOrArray = (
a: string | string[] | string[][],
config: MermaidConfig
config: MermaidConfigWithDefaults
): string | string[] => {
if (typeof a === 'string') {
return sanitizeText(a, config);
@@ -310,7 +310,10 @@ export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.leng
* @param config - Configuration for Mermaid
* @returns Object containing \{width, height\}
*/
export const calculateMathMLDimensions = async (text: string, config: MermaidConfig) => {
export const calculateMathMLDimensions = async (
text: string,
config: MermaidConfigWithDefaults
) => {
text = await renderKatex(text, config);
const divElem = document.createElement('div');
divElem.innerHTML = text;
@@ -332,7 +335,10 @@ export const calculateMathMLDimensions = async (text: string, config: MermaidCon
* @param config - Configuration for Mermaid
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
*/
export const renderKatex = async (text: string, config: MermaidConfig): Promise<string> => {
export const renderKatex = async (
text: string,
config: MermaidConfigWithDefaults
): Promise<string> => {
if (!hasKatex(text)) {
return text;
}

View File

@@ -11,7 +11,7 @@ import {
getDiagramTitle,
} from '../common/commonDb.js';
let entities = new Map();
let entities = {};
let relationships = [];
const Cardinality = {
@@ -28,15 +28,15 @@ const Identification = {
};
const addEntity = function (name, alias = undefined) {
if (!entities.has(name)) {
entities.set(name, { attributes: [], alias: alias });
if (entities[name] === undefined) {
entities[name] = { attributes: [], alias: alias };
log.info('Added new entity :', name);
} else if (entities.has(name) && !entities.get(name).alias && alias) {
entities.get(name).alias = alias;
} else if (entities[name] && !entities[name].alias && alias) {
entities[name].alias = alias;
log.info(`Add alias '${alias}' to entity '${name}'`);
}
return entities.get(name);
return entities[name];
};
const getEntities = () => entities;
@@ -75,7 +75,7 @@ const addRelationship = function (entA, rolA, entB, rSpec) {
const getRelationships = () => relationships;
const clear = function () {
entities = new Map();
entities = {};
relationships = [];
commonClear();
};

View File

@@ -296,12 +296,12 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
* Use D3 to construct the svg elements for the entities
*
* @param svgNode The svg node that contains the diagram
* @param {Map<string, object>} entities The entities to be drawn
* @param entities The entities to be drawn
* @param graph The graph that contains the vertex and edge definitions post-layout
* @returns {object} The first entity that was inserted
*/
const drawEntities = function (svgNode, entities, graph) {
const keys = [...entities.keys()];
const keys = Object.keys(entities);
let firstOne;
keys.forEach(function (entityName) {
@@ -326,12 +326,12 @@ const drawEntities = function (svgNode, entities, graph) {
.style('text-anchor', 'middle')
.style('font-family', getConfig().fontFamily)
.style('font-size', conf.fontSize + 'px')
.text(entities.get(entityName).alias ?? entityName);
.text(entities[entityName].alias ?? entityName);
const { width: entityWidth, height: entityHeight } = drawAttributes(
groupNode,
textNode,
entities.get(entityName).attributes
entities[entityName].attributes
);
// Draw the rectangle - insert it before the text so that the text is not obscured

View File

@@ -17,7 +17,7 @@ describe('when parsing ER diagram it...', function () {
const line2 = 'MAINLAND';
erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`);
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(erDb.getRelationships().length).toBe(0);
});
@@ -27,7 +27,7 @@ describe('when parsing ER diagram it...', function () {
expect(() => {
erDiagram.parser.parse(`erDiagram\n ${name}\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(false);
expect(entities.hasOwnProperty(name)).toBe(false);
}).toThrow();
});
describe('has non A-Za-z0-9_- chars', function () {
@@ -47,7 +47,7 @@ describe('when parsing ER diagram it...', function () {
expect(() => {
erDiagram.parser.parse(`erDiagram\n ${name}\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(false);
expect(entities.hasOwnProperty(name)).toBe(false);
}).toThrow();
});
@@ -55,21 +55,21 @@ describe('when parsing ER diagram it...', function () {
const name = singleOccurrence;
erDiagram.parser.parse(`erDiagram\n "${name}"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(true);
expect(entities.hasOwnProperty(name)).toBe(true);
});
it(`"${repeatedOccurrence}" repeated occurrence`, function () {
const name = repeatedOccurrence;
erDiagram.parser.parse(`erDiagram\n "${name}"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(true);
expect(entities.hasOwnProperty(name)).toBe(true);
});
it(`"${singleOccurrence}" ends with`, function () {
const name = endsWith;
erDiagram.parser.parse(`erDiagram\n "${name}"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(true);
expect(entities.hasOwnProperty(name)).toBe(true);
});
it(`"${cannontStartWith}" cannot start with the character`, function () {
@@ -77,7 +77,7 @@ describe('when parsing ER diagram it...', function () {
expect(() => {
erDiagram.parser.parse(`erDiagram\n "${name}"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(false);
expect(entities.hasOwnProperty(name)).toBe(false);
}).toThrow();
});
});
@@ -88,7 +88,7 @@ describe('when parsing ER diagram it...', function () {
const name = 'a' + allCombined;
erDiagram.parser.parse(`erDiagram\n "${name}"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(true);
expect(entities.hasOwnProperty(name)).toBe(true);
});
});
@@ -96,14 +96,14 @@ describe('when parsing ER diagram it...', function () {
expect(() => {
erDiagram.parser.parse(`erDiagram\n "Blo%rf"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(false);
expect(entities.hasOwnProperty(name)).toBe(false);
}).toThrow();
});
it('cannot contain \\ because it could start and escape code', function () {
expect(() => {
erDiagram.parser.parse(`erDiagram\n "Blo\\rf"\n`);
const entities = erDb.getEntities();
expect(entities.has(name)).toBe(false);
expect(entities.hasOwnProperty(name)).toBe(false);
}).toThrow();
});
@@ -114,7 +114,7 @@ describe('when parsing ER diagram it...', function () {
expect(() => {
erDiagram.parser.parse(`erDiagram\n "${badName}"\n`);
const entities = erDb.getEntities();
expect(entities.has(badName)).toBe(false);
expect(entities.hasOwnProperty(badName)).toBe(false);
}).toThrow();
});
});
@@ -124,14 +124,14 @@ describe('when parsing ER diagram it...', function () {
const beyondEnglishName = 'DUCK-àáâäæãåā';
erDiagram.parser.parse(`erDiagram\n${beyondEnglishName}\n`);
const entities = erDb.getEntities();
expect(entities.has(beyondEnglishName)).toBe(true);
expect(entities.hasOwnProperty(beyondEnglishName)).toBe(true);
});
it('can contain - _ without needing ""', function () {
const hyphensUnderscore = 'DUCK-BILLED_PLATYPUS';
erDiagram.parser.parse(`erDiagram\n${hyphensUnderscore}\n`);
const entities = erDb.getEntities();
expect(entities.has(hyphensUnderscore)).toBe(true);
expect(entities.hasOwnProperty(hyphensUnderscore)).toBe(true);
});
it('can have an alias', function () {
@@ -139,8 +139,8 @@ describe('when parsing ER diagram it...', function () {
const alias = 'bar';
erDiagram.parser.parse(`erDiagram\n${entity}["${alias}"]\n`);
const entities = erDb.getEntities();
expect(entities.has(entity)).toBe(true);
expect(entities.get(entity).alias).toBe(alias);
expect(entities.hasOwnProperty(entity)).toBe(true);
expect(entities[entity].alias).toBe(alias);
});
it('can have an alias even if the relationship is defined before class', function () {
@@ -151,10 +151,10 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\n${firstEntity} ||--o| ${secondEntity} : rel\nclass ${firstEntity}["${alias}"]\n`
);
const entities = erDb.getEntities();
expect(entities.has(firstEntity)).toBe(true);
expect(entities.has(secondEntity)).toBe(true);
expect(entities.get(firstEntity).alias).toBe(alias);
expect(entities.get(secondEntity).alias).toBeUndefined();
expect(entities.hasOwnProperty(firstEntity)).toBe(true);
expect(entities.hasOwnProperty(secondEntity)).toBe(true);
expect(entities[firstEntity].alias).toBe(alias);
expect(entities[secondEntity].alias).toBeUndefined();
});
it('can have an alias even if the relationship is defined after class', function () {
@@ -165,17 +165,17 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\nclass ${firstEntity}["${alias}"]\n${firstEntity} ||--o| ${secondEntity} : rel\n`
);
const entities = erDb.getEntities();
expect(entities.has(firstEntity)).toBe(true);
expect(entities.has(secondEntity)).toBe(true);
expect(entities.get(firstEntity).alias).toBe(alias);
expect(entities.get(secondEntity).alias).toBeUndefined();
expect(entities.hasOwnProperty(firstEntity)).toBe(true);
expect(entities.hasOwnProperty(secondEntity)).toBe(true);
expect(entities[firstEntity].alias).toBe(alias);
expect(entities[secondEntity].alias).toBeUndefined();
});
it('can start with an underscore', function () {
const entity = '_foo';
erDiagram.parser.parse(`erDiagram\n${entity}\n`);
const entities = erDb.getEntities();
expect(entities.has(entity)).toBe(true);
expect(entities.hasOwnProperty(entity)).toBe(true);
});
});
@@ -191,11 +191,11 @@ describe('when parsing ER diagram it...', function () {
);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(3);
expect(entities.get(entity).attributes[0].attributeName).toBe('myBookTitle');
expect(entities.get(entity).attributes[1].attributeName).toBe('MYBOOKSUBTITLE_1');
expect(entities.get(entity).attributes[2].attributeName).toBe('author-ref[name](1)');
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(3);
expect(entities[entity].attributes[0].attributeName).toBe('myBookTitle');
expect(entities[entity].attributes[1].attributeName).toBe('MYBOOKSUBTITLE_1');
expect(entities[entity].attributes[2].attributeName).toBe('author-ref[name](1)');
});
it('should allow asterisk at the start of attribute name', function () {
@@ -204,8 +204,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity}{\n${attribute}}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow asterisks at the start of attribute declared with type and name', () => {
@@ -214,8 +214,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should not allow leading numbers, dashes or brackets', function () {
@@ -236,8 +236,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with a single attribute to be defined with a key', function () {
@@ -246,8 +246,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with a single attribute to be defined with a comment', function () {
@@ -256,9 +256,9 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(entities.get(entity).attributes[0].attributeComment).toBe('comment');
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
expect(entities[entity].attributes[0].attributeComment).toBe('comment');
});
it('should allow an entity with a single attribute to be defined with a key and a comment', function () {
@@ -267,8 +267,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow an entity with attribute starting with fk, pk or uk and a comment', function () {
@@ -282,7 +282,7 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\n${entity} {\n${attribute1} \n\n${attribute2}\n${attribute3}\n${attribute4}\n}`
);
const entities = erDb.getEntities();
expect(entities.get(entity).attributes.length).toBe(4);
expect(entities[entity].attributes.length).toBe(4);
});
it('should allow an entity with attributes that have many constraints and comments', function () {
@@ -297,14 +297,14 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n${attribute4}\n${attribute5}\n}`
);
const entities = erDb.getEntities();
expect(entities.get(entity).attributes[0].attributeKeyTypeList).toEqual(['PK', 'FK']);
expect(entities.get(entity).attributes[0].attributeComment).toBe('comment1');
expect(entities.get(entity).attributes[1].attributeKeyTypeList).toEqual(['PK', 'UK', 'FK']);
expect(entities.get(entity).attributes[2].attributeKeyTypeList).toEqual(['PK', 'UK']);
expect(entities.get(entity).attributes[2].attributeComment).toBe('comment3');
expect(entities.get(entity).attributes[3].attributeKeyTypeList).toBeUndefined();
expect(entities.get(entity).attributes[4].attributeKeyTypeList).toBeUndefined();
expect(entities.get(entity).attributes[4].attributeComment).toBe('comment5');
expect(entities[entity].attributes[0].attributeKeyTypeList).toEqual(['PK', 'FK']);
expect(entities[entity].attributes[0].attributeComment).toBe('comment1');
expect(entities[entity].attributes[1].attributeKeyTypeList).toEqual(['PK', 'UK', 'FK']);
expect(entities[entity].attributes[2].attributeKeyTypeList).toEqual(['PK', 'UK']);
expect(entities[entity].attributes[2].attributeComment).toBe('comment3');
expect(entities[entity].attributes[3].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeComment).toBe('comment5');
});
it('should allow an entity with attribute that has a generic type', function () {
@@ -317,8 +317,8 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n}`
);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(3);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(3);
});
it('should allow an entity with attribute that is an array', function () {
@@ -328,8 +328,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(2);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(2);
});
it('should allow an entity with attribute that is a limited length string', function () {
@@ -339,10 +339,10 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(2);
expect(entities.get(entity).attributes[0].attributeType).toBe('character(10)');
expect(entities.get(entity).attributes[1].attributeType).toBe('varchar(5)');
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(2);
expect(entities[entity].attributes[0].attributeType).toBe('character(10)');
expect(entities[entity].attributes[1].attributeType).toBe('varchar(5)');
});
it('should allow an entity with multiple attributes to be defined', function () {
@@ -355,7 +355,7 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n}`
);
const entities = erDb.getEntities();
expect(entities.get(entity).attributes.length).toBe(3);
expect(entities[entity].attributes.length).toBe(3);
});
it('should allow attribute definitions to be split into multiple blocks', function () {
@@ -368,7 +368,7 @@ describe('when parsing ER diagram it...', function () {
`erDiagram\n${entity} {\n${attribute1}\n}\n${entity} {\n${attribute2}\n${attribute3}\n}`
);
const entities = erDb.getEntities();
expect(entities.get(entity).attributes.length).toBe(3);
expect(entities[entity].attributes.length).toBe(3);
});
it('should allow an empty attribute block', function () {
@@ -376,8 +376,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {}`);
const entities = erDb.getEntities();
expect(entities.has('BOOK')).toBe(true);
expect(entities.get(entity).attributes.length).toBe(0);
expect(entities.hasOwnProperty('BOOK')).toBe(true);
expect(entities[entity].attributes.length).toBe(0);
});
it('should allow an attribute block to start immediately after the entity name', function () {
@@ -385,8 +385,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity}{}`);
const entities = erDb.getEntities();
expect(entities.has('BOOK')).toBe(true);
expect(entities.get(entity).attributes.length).toBe(0);
expect(entities.hasOwnProperty('BOOK')).toBe(true);
expect(entities[entity].attributes.length).toBe(0);
});
it('should allow an attribute block to be separated from the entity name by spaces', function () {
@@ -394,8 +394,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {}`);
const entities = erDb.getEntities();
expect(entities.has('BOOK')).toBe(true);
expect(entities.get(entity).attributes.length).toBe(0);
expect(entities.hasOwnProperty('BOOK')).toBe(true);
expect(entities[entity].attributes.length).toBe(0);
});
it('should allow whitespace before and after attribute definitions', function () {
@@ -404,8 +404,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity} {\n \n\n ${attribute}\n\n \n}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should allow no whitespace before and after attribute definitions', function () {
@@ -414,8 +414,8 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${entity}{${attribute}}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(1);
expect(entities.get(entity).attributes.length).toBe(1);
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(1);
});
it('should associate two entities correctly', function () {
@@ -423,8 +423,8 @@ describe('when parsing ER diagram it...', function () {
const entities = erDb.getEntities();
const relationships = erDb.getRelationships();
expect(entities.has('CAR')).toBe(true);
expect(entities.has('DRIVER')).toBe(true);
expect(entities.hasOwnProperty('CAR')).toBe(true);
expect(entities.hasOwnProperty('DRIVER')).toBe(true);
expect(relationships.length).toBe(1);
expect(relationships[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(relationships[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
@@ -437,7 +437,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse(`erDiagram\n${line1}\n${line2}`);
const entities = erDb.getEntities();
expect(entities.size).toBe(3);
expect(Object.keys(entities).length).toBe(3);
});
it('should create the role specified', function () {
@@ -451,7 +451,7 @@ describe('when parsing ER diagram it...', function () {
it('should allow recursive relationships', function () {
erDiagram.parser.parse('erDiagram\nNODE ||--o{ NODE : "leads to"');
expect(erDb.getEntities().size).toBe(1);
expect(Object.keys(erDb.getEntities()).length).toBe(1);
});
describe('accessible title and description', () => {
@@ -491,7 +491,7 @@ describe('when parsing ER diagram it...', function () {
const entities = erDb.getEntities();
const rels = erDb.getRelationships();
expect(entities.size).toBe(2);
expect(Object.keys(entities).length).toBe(2);
expect(rels.length).toBe(2);
});
@@ -507,7 +507,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA ||--|{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONE_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
@@ -517,7 +517,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA ||..o{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
@@ -527,7 +527,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA |o..o{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
@@ -537,7 +537,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA |o--|{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONE_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
@@ -547,7 +547,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }|--|| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONLY_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
@@ -557,7 +557,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }o--|| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONLY_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -567,7 +567,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }o..o| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -577,7 +577,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }|..o| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
@@ -587,7 +587,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA |o..|| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONLY_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
@@ -597,7 +597,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA ||..|| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONLY_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
@@ -607,7 +607,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA ||--o| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
@@ -617,7 +617,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA |o..o| B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
@@ -627,7 +627,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }o--o{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -637,7 +637,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }|..|{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONE_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
@@ -647,7 +647,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }o--|{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONE_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -657,7 +657,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA }|..o{ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
@@ -667,7 +667,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA one or zero to many B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_ONE);
@@ -677,7 +677,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA one or many optionally to zero or one B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONE_OR_MORE);
@@ -687,7 +687,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA zero or more to zero or many B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -697,7 +697,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA many(0) to many(1) B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONE_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -707,7 +707,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA many optionally to one B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONLY_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -717,7 +717,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA only one optionally to 1+ B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONE_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ONLY_ONE);
@@ -727,7 +727,7 @@ describe('when parsing ER diagram it...', function () {
erDiagram.parser.parse('erDiagram\nA 0+ optionally to 1 B : has');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ONLY_ONE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
@@ -786,21 +786,10 @@ describe('when parsing ER diagram it...', function () {
it('should represent parent-child relationship correctly', function () {
erDiagram.parser.parse('erDiagram\nPROJECT u--o{ TEAM_MEMBER : "parent"');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(Object.keys(erDb.getEntities()).length).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.MD_PARENT);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
});
});
describe('prototype properties', function () {
it.each(['__proto__', 'constructor', 'prototype'])(
'should work with a %s property',
function (prop) {
expect(() =>
erDiagram.parser.parse(`erDiagram\n${prop} ||--|{ ORDER : place`)
).not.toThrow();
}
);
});
});

View File

@@ -53,7 +53,7 @@ function hexagon(parent, bbox, node) {
* @param bbox
* @param node
*/
function rectLeftInvArrow(parent, bbox, node) {
function rect_left_inv_arrow(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -75,7 +75,7 @@ function rectLeftInvArrow(parent, bbox, node) {
* @param bbox
* @param node
*/
function leanRight(parent, bbox, node) {
function lean_right(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -96,7 +96,7 @@ function leanRight(parent, bbox, node) {
* @param bbox
* @param node
*/
function leanLeft(parent, bbox, node) {
function lean_left(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -138,7 +138,7 @@ function trapezoid(parent, bbox, node) {
* @param bbox
* @param node
*/
function invTrapezoid(parent, bbox, node) {
function inv_trapezoid(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -159,7 +159,7 @@ function invTrapezoid(parent, bbox, node) {
* @param bbox
* @param node
*/
function rectRightInvArrow(parent, bbox, node) {
function rect_right_inv_arrow(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -308,22 +308,22 @@ export function addToRender(render) {
render.shapes().cylinder = cylinder;
// Add custom shape for box with inverted arrow on left side
render.shapes().rect_left_inv_arrow = rectLeftInvArrow;
render.shapes().rect_left_inv_arrow = rect_left_inv_arrow;
// Add custom shape for box with inverted arrow on left side
render.shapes().lean_right = leanRight;
render.shapes().lean_right = lean_right;
// Add custom shape for box with inverted arrow on left side
render.shapes().lean_left = leanLeft;
render.shapes().lean_left = lean_left;
// Add custom shape for box with inverted arrow on left side
render.shapes().trapezoid = trapezoid;
// Add custom shape for box with inverted arrow on left side
render.shapes().inv_trapezoid = invTrapezoid;
render.shapes().inv_trapezoid = inv_trapezoid;
// Add custom shape for box with inverted arrow on right side
render.shapes().rect_right_inv_arrow = rectRightInvArrow;
render.shapes().rect_right_inv_arrow = rect_right_inv_arrow;
}
/** @param addShape */
@@ -335,22 +335,22 @@ export function addToRenderV2(addShape) {
addShape({ cylinder });
// Add custom shape for box with inverted arrow on left side
addShape({ rect_left_inv_arrow: rectLeftInvArrow });
addShape({ rect_left_inv_arrow });
// Add custom shape for box with inverted arrow on left side
addShape({ lean_right: leanRight });
addShape({ lean_right });
// Add custom shape for box with inverted arrow on left side
addShape({ lean_left: leanLeft });
addShape({ lean_left });
// Add custom shape for box with inverted arrow on left side
addShape({ trapezoid });
// Add custom shape for box with inverted arrow on left side
addShape({ inv_trapezoid: invTrapezoid });
addShape({ inv_trapezoid });
// Add custom shape for box with inverted arrow on right side
addShape({ rect_right_inv_arrow: rectRightInvArrow });
addShape({ rect_right_inv_arrow });
}
/**

View File

@@ -51,17 +51,17 @@ describe('flow db addClass', () => {
flowDb.addClass('a,b', ['stroke-width: 8px']);
const classes = flowDb.getClasses();
expect(classes.has('a')).toBe(true);
expect(classes.has('b')).toBe(true);
expect(classes.get('a')?.styles).toEqual(['stroke-width: 8px']);
expect(classes.get('b')?.styles).toEqual(['stroke-width: 8px']);
expect(classes.hasOwnProperty('a')).toBe(true);
expect(classes.hasOwnProperty('b')).toBe(true);
expect(classes['a']['styles']).toEqual(['stroke-width: 8px']);
expect(classes['b']['styles']).toEqual(['stroke-width: 8px']);
});
it('should detect single class', () => {
flowDb.addClass('a', ['stroke-width: 8px']);
const classes = flowDb.getClasses();
expect(classes.has('a')).toBe(true);
expect(classes.get('a')?.styles).toEqual(['stroke-width: 8px']);
expect(classes.hasOwnProperty('a')).toBe(true);
expect(classes['a']['styles']).toEqual(['stroke-width: 8px']);
});
});

View File

@@ -17,12 +17,12 @@ import type { FlowVertex, FlowClass, FlowSubGraph, FlowText, FlowEdge, FlowLink
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
let vertexCounter = 0;
let config = getConfig();
let vertices: Map<string, FlowVertex> = new Map();
let vertices: Record<string, FlowVertex> = {};
let edges: FlowEdge[] & { defaultInterpolate?: string; defaultStyle?: string[] } = [];
let classes: Map<string, FlowClass> = new Map();
let classes: Record<string, FlowClass> = {};
let subGraphs: FlowSubGraph[] = [];
let subGraphLookup: Map<string, FlowSubGraph> = new Map();
let tooltips: Map<string, string> = new Map();
let subGraphLookup: Record<string, FlowSubGraph> = {};
let tooltips: Record<string, string> = {};
let subCount = 0;
let firstGraphFlag = true;
let direction: string;
@@ -40,9 +40,10 @@ const sanitizeText = (txt: string) => common.sanitizeText(txt, config);
* @param id - id of the node
*/
export const lookUpDomId = function (id: string) {
for (const vertex of vertices.values()) {
if (vertex.id === id) {
return vertex.domId;
const vertexKeys = Object.keys(vertices);
for (const vertexKey of vertexKeys) {
if (vertices[vertexKey].id === id) {
return vertices[vertexKey].domId;
}
}
return id;
@@ -66,53 +67,50 @@ export const addVertex = function (
}
let txt;
let vertex = vertices.get(id);
if (vertex === undefined) {
vertex = {
if (vertices[id] === undefined) {
vertices[id] = {
id,
labelType: 'text',
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
styles: [],
classes: [],
};
vertices.set(id, vertex);
}
vertexCounter++;
if (textObj !== undefined) {
config = getConfig();
txt = sanitizeText(textObj.text.trim());
vertex.labelType = textObj.type;
vertices[id].labelType = textObj.type;
// strip quotes if string starts and ends with a quote
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
txt = txt.substring(1, txt.length - 1);
}
vertex.text = txt;
vertices[id].text = txt;
} else {
if (vertex.text === undefined) {
vertex.text = id;
if (vertices[id].text === undefined) {
vertices[id].text = id;
}
}
if (type !== undefined) {
vertex.type = type;
vertices[id].type = type;
}
if (style !== undefined && style !== null) {
style.forEach(function (s) {
vertex.styles.push(s);
vertices[id].styles.push(s);
});
}
if (classes !== undefined && classes !== null) {
classes.forEach(function (s) {
vertex.classes.push(s);
vertices[id].classes.push(s);
});
}
if (dir !== undefined) {
vertex.dir = dir;
vertices[id].dir = dir;
}
if (vertex.props === undefined) {
vertex.props = props;
if (vertices[id].props === undefined) {
vertices[id].props = props;
} else if (props !== undefined) {
Object.assign(vertex.props, props);
Object.assign(vertices[id].props, props);
}
};
@@ -210,19 +208,17 @@ export const updateLink = function (positions: ('default' | number)[], style: st
export const addClass = function (ids: string, style: string[]) {
ids.split(',').forEach(function (id) {
let classNode = classes.get(id);
if (classNode === undefined) {
classNode = { id, styles: [], textStyles: [] };
classes.set(id, classNode);
if (classes[id] === undefined) {
classes[id] = { id, styles: [], textStyles: [] };
}
if (style !== undefined && style !== null) {
style.forEach(function (s) {
if (s.match('color')) {
const newStyle = s.replace('fill', 'bgFill').replace('color', 'fill');
classNode.textStyles.push(newStyle);
classes[id].textStyles.push(newStyle);
}
classNode.styles.push(s);
classes[id].styles.push(s);
});
}
});
@@ -259,13 +255,11 @@ export const setDirection = function (dir: string) {
*/
export const setClass = function (ids: string, className: string) {
for (const id of ids.split(',')) {
const vertex = vertices.get(id);
if (vertex) {
vertex.classes.push(className);
if (vertices[id]) {
vertices[id].classes.push(className);
}
const subGraph = subGraphLookup.get(id);
if (subGraph) {
subGraph.classes.push(className);
if (subGraphLookup[id]) {
subGraphLookup[id].classes.push(className);
}
}
};
@@ -276,7 +270,7 @@ const setTooltip = function (ids: string, tooltip: string) {
}
tooltip = sanitizeText(tooltip);
for (const id of ids.split(',')) {
tooltips.set(version === 'gen-1' ? lookUpDomId(id) : id, tooltip);
tooltips[version === 'gen-1' ? lookUpDomId(id) : id] = tooltip;
}
};
@@ -309,9 +303,8 @@ const setClickFun = function (id: string, functionName: string, functionArgs: st
argList.push(id);
}
const vertex = vertices.get(id);
if (vertex) {
vertex.haveCallback = true;
if (vertices[id] !== undefined) {
vertices[id].haveCallback = true;
funs.push(function () {
const elem = document.querySelector(`[id="${domId}"]`);
if (elem !== null) {
@@ -336,17 +329,19 @@ const setClickFun = function (id: string, functionName: string, functionArgs: st
*/
export const setLink = function (ids: string, linkStr: string, target: string) {
ids.split(',').forEach(function (id) {
const vertex = vertices.get(id);
if (vertex !== undefined) {
vertex.link = utils.formatUrl(linkStr, config);
vertex.linkTarget = target;
if (vertices[id] !== undefined) {
vertices[id].link = utils.formatUrl(linkStr, config);
vertices[id].linkTarget = target;
}
});
setClass(ids, 'clickable');
};
export const getTooltip = function (id: string) {
return tooltips.get(id);
if (tooltips.hasOwnProperty(id)) {
return tooltips[id];
}
return undefined;
};
/**
@@ -440,14 +435,14 @@ funs.push(setupToolTips);
*
*/
export const clear = function (ver = 'gen-1') {
vertices = new Map();
classes = new Map();
vertices = {};
classes = {};
edges = [];
funs = [setupToolTips];
subGraphs = [];
subGraphLookup = new Map();
subGraphLookup = {};
subCount = 0;
tooltips = new Map();
tooltips = {};
firstGraphFlag = true;
version = ver;
config = getConfig();
@@ -521,7 +516,7 @@ export const addSubGraph = function (
// Remove the members in the new subgraph if they already belong to another subgraph
subGraph.nodes = makeUniq(subGraph, subGraphs).nodes;
subGraphs.push(subGraph);
subGraphLookup.set(id, subGraph);
subGraphLookup[id] = subGraph;
return id;
};

View File

@@ -1,17 +1,17 @@
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import { setConfig } from '../../diagram-api/diagramAPI.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
export const diagram = {
export const diagram: DiagramDefinition = {
parser: flowParser,
db: flowDb,
renderer: flowRendererV2,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
init: (cnf) => {
if (!cnf.flowchart) {
cnf.flowchart = {};
}

View File

@@ -1,17 +1,17 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowRenderer from './flowRenderer.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import flowDb from './flowDb.js';
import flowRenderer from './flowRenderer.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
export const diagram = {
export const diagram: DiagramDefinition = {
parser: flowParser,
db: flowDb,
renderer: flowRendererV2,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
init: (cnf) => {
if (!cnf.flowchart) {
cnf.flowchart = {};
}

View File

@@ -29,11 +29,11 @@ export const setConf = function (cnf) {
*/
export const addVertices = async function (vert, g, svgId, root, doc, diagObj) {
const svg = root.select(`[id="${svgId}"]`);
const keys = vert.keys();
const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
for (const id of keys) {
const vertex = vert.get(id);
const vertex = vert[id];
/**
* Variable for storing the classes for the vertex
@@ -246,19 +246,16 @@ export const addEdges = async function (edges, g, diagObj) {
/* eslint-disable no-fallthrough */
switch (edge.type) {
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross';
break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point';
break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle':
@@ -344,7 +341,7 @@ export const addEdges = async function (edges, g, diagObj) {
*
* @param text
* @param diagObj
* @returns {Map<string, import('../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
* @returns {Record<string, import('../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
return diagObj.db.getClasses();
@@ -428,9 +425,9 @@ export const draw = async function (text, id, _version, diagObj) {
selectAll('cluster').append('text');
for (const node of subG.nodes) {
log.info('Setting up subgraphs', node, subG.id);
g.setParent(node, subG.id);
for (let j = 0; j < subG.nodes.length; j++) {
log.info('Setting up subgraphs', subG.nodes[j], subG.id);
g.setParent(subG.nodes[j], subG.id);
}
}
await addVertices(vert, g, id, root, doc, diagObj);
@@ -471,9 +468,9 @@ export const draw = async function (text, id, _version, diagObj) {
}
// If node has a link, wrap it in an anchor SVG object.
const keys = [...vert.keys()];
keys.forEach((key) => {
const vertex = vert.get(key);
const keys = Object.keys(vert);
keys.forEach(function (key) {
const vertex = vert[key];
if (vertex.link) {
const node = select('#' + id + ' [id="' + key + '"]');

View File

@@ -268,7 +268,7 @@ export const addEdges = async function (edges, g, diagObj) {
*
* @param text
* @param diagObj
* @returns {Map<string, import('../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
* @returns {Record<string, import('../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
@@ -339,14 +339,14 @@ export const draw = async function (text, id, _version, diagObj) {
selectAll('cluster').append('text');
for (const subGNode of subG.nodes) {
for (let j = 0; j < subG.nodes.length; j++) {
log.warn(
'Setting subgraph',
subGNode,
diagObj.db.lookUpDomId(subGNode),
subG.nodes[j],
diagObj.db.lookUpDomId(subG.nodes[j]),
diagObj.db.lookUpDomId(subG.id)
);
g.setParent(diagObj.db.lookUpDomId(subGNode), diagObj.db.lookUpDomId(subG.id));
g.setParent(diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subG.id));
}
}
await addVertices(vert, g, id, root, doc, diagObj);
@@ -429,8 +429,8 @@ export const draw = async function (text, id, _version, diagObj) {
te.attr('transform', `translate(${xPos + _width / 2}, ${yPos + 14})`);
te.attr('id', id + 'Text');
for (const subGClass of subG.classes) {
clusterEl[0].classList.add(subGClass);
for (let j = 0; j < subG.classes.length; j++) {
clusterEl[0].classList.add(subG.classes[j]);
}
}
}
@@ -455,9 +455,9 @@ export const draw = async function (text, id, _version, diagObj) {
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
// If node has a link, wrap it in an anchor SVG object.
const keys = [...vert.keys()];
const keys = Object.keys(vert);
keys.forEach(function (key) {
const vertex = vert.get(key);
const vertex = vert[key];
if (vertex.link) {
const node = root.select('#' + id + ' [id="' + diagObj.db.lookUpDomId(key) + '"]');

View File

@@ -18,8 +18,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -38,8 +38,8 @@ describe('[Arrows] when parsing', () => {
expect(direction).toBe('LR');
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -58,8 +58,8 @@ describe('[Arrows] when parsing', () => {
expect(direction).toBe('RL');
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -78,8 +78,8 @@ describe('[Arrows] when parsing', () => {
expect(direction).toBe('BT');
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -99,8 +99,8 @@ describe('[Arrows] when parsing', () => {
expect(direction).toBe('TB');
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -116,8 +116,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -133,8 +133,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -150,8 +150,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(2);
expect(edges[1].start).toBe('B');
expect(edges[1].end).toBe('C');
@@ -169,8 +169,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -186,8 +186,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -203,8 +203,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -220,8 +220,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -237,8 +237,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -254,8 +254,8 @@ describe('[Arrows] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');

View File

@@ -19,8 +19,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -34,8 +34,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -49,8 +49,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -64,8 +64,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -79,8 +79,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -94,8 +94,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -109,8 +109,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -124,8 +124,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -143,8 +143,8 @@ describe('[Comments] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');

View File

@@ -48,6 +48,8 @@ describe('[Edges] when parsing', () => {
it('should handle open ended edges', function () {
const res = flow.parser.parse('graph TD;A---B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_open');
@@ -55,6 +57,8 @@ describe('[Edges] when parsing', () => {
it('should handle cross ended edges', function () {
const res = flow.parser.parse('graph TD;A--xB;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_cross');
@@ -62,6 +66,8 @@ describe('[Edges] when parsing', () => {
it('should handle open ended edges', function () {
const res = flow.parser.parse('graph TD;A--oB;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].type).toBe('arrow_circle');
@@ -75,8 +81,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -93,8 +99,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -113,8 +119,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -133,8 +139,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -158,8 +164,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -177,8 +183,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -196,8 +202,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -215,8 +221,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -234,8 +240,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -253,8 +259,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -272,8 +278,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -291,8 +297,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -310,8 +316,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -329,8 +335,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -348,8 +354,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -367,8 +373,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -386,8 +392,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -405,8 +411,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -424,8 +430,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -443,8 +449,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -462,8 +468,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
@@ -481,8 +487,8 @@ describe('[Edges] when parsing', () => {
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');

View File

@@ -23,7 +23,7 @@ describe('[Text] when parsing', () => {
expect(edges[0].type).toBe('arrow_point');
expect(edges.length).toBe(47917);
expect(vert.size).toBe(2);
expect(Object.keys(vert).length).toBe(2);
});
});
});

View File

@@ -19,12 +19,12 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('A').text).toBe('The cat in **the** hat');
expect(vert.get('A').labelType).toBe('markdown');
expect(vert.get('B').id).toBe('B');
expect(vert.get('B').text).toBe('The dog in the hog');
expect(vert.get('B').labelType).toBe('string');
expect(vert['A'].id).toBe('A');
expect(vert['A'].text).toBe('The cat in **the** hat');
expect(vert['A'].labelType).toBe('markdown');
expect(vert['B'].id).toBe('B');
expect(vert['B'].text).toBe('The dog in the hog');
expect(vert['B'].labelType).toBe('string');
expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');

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