Compare commits

...

17 Commits

Author SHA1 Message Date
Sidharth Vinod
83fb2d933c Test 2024-06-22 21:58:44 +05:30
Sidharth Vinod
48303a030d Format on pre-commit 2024-06-21 17:02:32 +05:30
Sidharth Vinod
054ed6c69c Merge branch 'develop' into sidv/biome 2024-06-21 16:40:31 +05:30
Sidharth Vinod
89e061aa51 chore: Fix gantt test 2024-06-21 16:24:02 +05:30
Sidharth Vinod
a1badd5167 chore: Fix shape names 2024-06-21 16:00:06 +05:30
Sidharth Vinod
55a8e4cf7e chore: remove comments in json 2024-06-21 15:39:22 +05:30
Sidharth Vinod
e7577ed51e chore: Add eslint-disable 2024-06-21 15:36:01 +05:30
Sidharth Vinod
d2b42ebd74 chore: Add eslint-disable 2024-06-21 15:28:20 +05:30
Sidharth Vinod
ba34386a69 Fix prettier formatting on generation.
We should replace this with Biome as well, but it's a bit complicated.
2024-06-21 15:18:30 +05:30
Sidharth Vinod
a23b891f20 chore: Add .eslintignore 2024-06-21 14:45:07 +05:30
Sidharth Vinod
3a5793f948 chore: Enable eslint in CI 2024-06-21 14:44:24 +05:30
Sidharth Vinod
0a5315cd5a chore: Add biome renovate config 2024-06-21 14:44:24 +05:30
Sidharth Vinod
98f32bfdfe chore: Remove unused deps 2024-06-21 14:44:24 +05:30
Sidharth Vinod
22a00a5f8b chore: Update lint scripts 2024-06-21 14:44:23 +05:30
Sidharth Vinod
3381717e86 chore: Biome fix for-of loops 2024-06-21 14:44:23 +05:30
Sidharth Vinod
08dfdfed82 chore: Biome auto fixes 2024-06-21 14:44:23 +05:30
Sidharth Vinod
2c80d806cc feat: Add biome 2024-06-21 14:44:23 +05:30
60 changed files with 698 additions and 444 deletions

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 { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
import { type 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 { Plugin } from 'esbuild';
import type { Plugin } from 'esbuild';
export const jisonPlugin: Plugin = {
name: 'jison',

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);
let output: BuildOptions = buildOptions({
const output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[outFileName]: `src/${file}`,

View File

@@ -37,9 +37,9 @@ jobs:
- name: Run Linting
shell: bash
run: |
if ! pnpm run lint; then
if ! pnpm run lint:ci; then
# print a nice error message on lint failure
ERROR_MESSAGE='Running `pnpm run lint` failed.'
ERROR_MESSAGE='Running `pnpm run lint:ci` 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,10 +1,6 @@
export default {
'!(docs/**/*)*.{ts,js,html,md,mts}': [
'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',
'biome check --no-errors-on-unmatched --files-ignore-unknown=true --write',
],
'.cspell/*.txt': ['tsx scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'],

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import { build, InlineConfig, type PluginOption } from 'vite';
import { build, type 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];
let output: OutputOptions = [
const output: OutputOptions = [
{
name,
format: 'esm',

View File

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

229
biome.json Normal file
View File

@@ -0,0 +1,229 @@
{
"$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

@@ -25,8 +25,9 @@
"dev:vite": "tsx .vite/server.ts",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
"release": "pnpm build",
"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": "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:jison": "tsx ./scripts/jison/lint.mts",
"contributors": "tsx scripts/updateContributors.ts",
"cypress": "cypress run",
@@ -51,18 +52,13 @@
"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",

View File

@@ -12,12 +12,7 @@
},
"./*": "./*"
},
"keywords": [
"diagram",
"markdown",
"example",
"mermaid"
],
"keywords": ["diagram", "markdown", "example", "mermaid"],
"scripts": {
"prepublishOnly": "pnpm -w run build"
},
@@ -28,14 +23,8 @@
"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",
@@ -47,11 +36,6 @@
"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 THEME_COLOR_LIMIT = getConfig().themeVariables.THEME_COLOR_LIMIT;
const themeColorLimit = 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 < THEME_COLOR_LIMIT; i++) {
for (i = 0; i < themeColorLimit; i++) {
const section = g.append('g').attr('class', 'section-' + i);
section
.append('rect')

View File

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

View File

@@ -447,16 +447,19 @@ 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':

View File

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

View File

@@ -58,14 +58,8 @@
"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",
@@ -103,8 +97,6 @@
"@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",
@@ -132,10 +124,7 @@
"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,7 +18,7 @@ import { execFile } from 'node:child_process';
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { promisify } from 'node:util';
import prettier from 'prettier';
import { prettierConfig } from './prettier.js';
// Workaround for wrong AJV types, see
// https://github.com/ajv-validator/ajv/issues/2132#issuecomment-1290409907
@@ -141,7 +141,7 @@ async function generateTypescript(mermaidConfigSchema: JSONSchemaType<MermaidCon
{
additionalProperties: false, // in JSON Schema 2019-09, these are called `unevaluatedProperties`
unreachableDefinitions: true, // definition for FontConfig is unreachable
style: (await prettier.resolveConfig('.')) ?? {},
style: prettierConfig,
}
);

View File

@@ -52,6 +52,7 @@ 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;
@@ -85,7 +86,6 @@ 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 SCHEMA_INPUT_DIR = 'src/schemas/';
const schemaInputDir = '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 SCHEMA_OUTPUT_DIR = 'src/docs/public/schemas/';
const VITEPRESS_PUBLIC_DIR = 'src/docs/public';
const schemaOutputDir = 'src/docs/public/schemas/';
const vitepressPublicDir = '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 SCHEMA_MARKDOWN_OUTPUT_DIR = join('src', 'docs', 'config', 'schema-docs');
const schemaMarkdownOutputDir = join('src', 'docs', 'config', 'schema-docs');
// write .schema.json files
const jsonFileName = file
.replace('.schema.yaml', '.schema.json')
.replace(SCHEMA_INPUT_DIR, SCHEMA_OUTPUT_DIR);
.replace(schemaInputDir, schemaOutputDir);
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(VITEPRESS_PUBLIC_DIR, origin)}`;
return `/${relative(vitepressPublicDir, origin)}`;
},
})(schemas);
@@ -458,7 +458,7 @@ async function transformJsonSchema(file: string) {
parser: 'markdown',
...prettierConfig,
});
const newFileName = join(SCHEMA_MARKDOWN_OUTPUT_DIR, `${name}.md`);
const newFileName = join(schemaMarkdownOutputDir, `${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 diagram_text = 'graph\n A --> B\n';
const diagramText = 'graph\n A --> B\n';
describe('language = "mermaid-nocode"', () => {
const lang_keyword = 'mermaid-nocode';
const contents = beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
const langKeyword = 'mermaid-nocode';
const contents = beforeCodeLine + '```' + langKeyword + '\n' + diagramText + '\n```\n';
it('changes the language to "mermaid"', async () => {
const result = (
@@ -37,17 +37,16 @@ describe('docs.mts', () => {
.process(contents)
).toString();
expect(result).toEqual(
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagram_text + '\n```\n'
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagramText + '\n```\n'
);
});
});
describe('language = "mermaid" | "mmd" | "mermaid-example"', () => {
const mermaid_keywords = ['mermaid', 'mmd', 'mermaid-example'];
const mermaidKeywords = ['mermaid', 'mmd', 'mermaid-example'];
mermaid_keywords.forEach((lang_keyword) => {
const contents =
beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n';
mermaidKeywords.forEach((langKeyword) => {
const contents = beforeCodeLine + '```' + langKeyword + '\n' + diagramText + '\n```\n';
it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', async () => {
const result = (
@@ -59,10 +58,10 @@ describe('docs.mts', () => {
beforeCodeLine +
'\n' +
'```mermaid-example\n' +
diagram_text +
diagramText +
'\n```\n' +
'\n```mermaid\n' +
diagram_text +
diagramText +
'\n```\n'
);
});
@@ -70,9 +69,9 @@ describe('docs.mts', () => {
});
it('calls transformToBlockQuote with the node information', async () => {
const lang_keyword = 'note';
const langKeyword = 'note';
const contents =
beforeCodeLine + '```' + lang_keyword + '\n' + 'This is the text\n' + '```\n';
beforeCodeLine + '```' + langKeyword + '\n' + 'This is the text\n' + '```\n';
const result = (
await remarkBuilder().use(transformMarkdownAst, { originalFilename }).process(contents)

View File

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

View File

@@ -8,32 +8,32 @@ describe('when working with site config', () => {
configApi.setSiteConfig({});
});
it('should set site config and config properly', () => {
const config_0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0);
const config_1 = configApi.getSiteConfig();
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);
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);
});
it('should respect secure keys when applying directives', () => {
const config_0: MermaidConfig = {
const config0: MermaidConfig = {
fontFamily: 'foo-font',
securityLevel: 'strict', // can't be changed
fontSize: 12345, // can't be changed
secure: [...configApi.defaultConfig.secure!, 'fontSize'],
};
configApi.setSiteConfig(config_0);
configApi.setSiteConfig(config0);
const directive: MermaidConfig = {
fontFamily: 'baf',
// fontSize and securityLevel shouldn't be changed
fontSize: 54321,
securityLevel: 'loose',
};
const cfg: MermaidConfig = configApi.updateCurrentConfig(config_0, [directive]);
const cfg: MermaidConfig = configApi.updateCurrentConfig(config0, [directive]);
expect(cfg.fontFamily).toEqual(directive.fontFamily);
expect(cfg.fontSize).toBe(config_0.fontSize);
expect(cfg.securityLevel).toBe(config_0.securityLevel);
expect(cfg.fontSize).toBe(config0.fontSize);
expect(cfg.securityLevel).toBe(config0.securityLevel);
});
it('should allow setting partial options', () => {
const defaultConfig = configApi.getConfig();
@@ -52,30 +52,30 @@ describe('when working with site config', () => {
);
});
it('should set reset config properly', () => {
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);
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);
configApi.reset();
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);
const config3 = configApi.getConfig();
expect(config3.fontFamily).toEqual(config0.fontFamily);
const config4 = configApi.getSiteConfig();
expect(config4.fontFamily).toEqual(config0.fontFamily);
});
it('should set global reset config properly', () => {
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);
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);
configApi.setConfig({ altFontFamily: 'bar-font' });
const config_3 = configApi.getConfig();
expect(config_3.altFontFamily).toEqual('bar-font');
const config3 = configApi.getConfig();
expect(config3.altFontFamily).toEqual('bar-font');
configApi.reset();
const config_4 = configApi.getConfig();
expect(config_4.altFontFamily).toBeUndefined();
const config4 = configApi.getConfig();
expect(config4.altFontFamily).toBeUndefined();
});
});

View File

@@ -13,13 +13,13 @@ export default intersectPolygon;
* @param point
*/
function intersectPolygon(node, polyPoints, point) {
var x1 = node.x;
var y1 = node.y;
let x1 = node.x;
let y1 = node.y;
var intersections = [];
let intersections = [];
var minX = Number.POSITIVE_INFINITY;
var minY = Number.POSITIVE_INFINITY;
let minX = Number.POSITIVE_INFINITY;
let 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);
}
var left = x1 - node.width / 2 - minX;
var top = y1 - node.height / 2 - minY;
let left = x1 - node.width / 2 - minX;
let top = y1 - node.height / 2 - minY;
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(
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(
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) {
var pdx = p.x - point.x;
var pdy = p.y - point.y;
var distp = Math.sqrt(pdx * pdx + pdy * pdy);
let pdx = p.x - point.x;
let pdy = p.y - point.y;
let distp = Math.sqrt(pdx * pdx + pdy * pdy);
var qdx = q.x - point.x;
var qdy = q.y - point.y;
var distq = Math.sqrt(qdx * qdx + qdy * qdy);
let qdx = q.x - point.x;
let qdy = q.y - point.y;
let 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 block_arrow = async (parent, node) => {
const blockArrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const f = 2;
@@ -140,7 +140,7 @@ const block_arrow = async (parent, node) => {
return shapeSvg;
};
const rect_left_inv_arrow = async (parent, node) => {
const rectLeftInvArrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -171,7 +171,7 @@ const rect_left_inv_arrow = async (parent, node) => {
return shapeSvg;
};
const lean_right = async (parent, node) => {
const leanRight = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(parent, node, getClassesFromNode(node), true);
const w = bbox.width + node.padding;
@@ -194,7 +194,7 @@ const lean_right = async (parent, node) => {
return shapeSvg;
};
const lean_left = async (parent, node) => {
const leanLeft = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -250,7 +250,7 @@ const trapezoid = async (parent, node) => {
return shapeSvg;
};
const inv_trapezoid = async (parent, node) => {
const invTrapezoid = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -278,7 +278,7 @@ const inv_trapezoid = async (parent, node) => {
return shapeSvg;
};
const rect_right_inv_arrow = async (parent, node) => {
const rectRightInvArrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(
parent,
node,
@@ -875,7 +875,7 @@ const end = (parent, node) => {
return shapeSvg;
};
const class_box = (parent, node) => {
const classBox = (parent, node) => {
const halfPadding = node.padding / 2;
const rowPadding = 4;
const lineHeight = 8;
@@ -1111,13 +1111,13 @@ const shapes = {
doublecircle,
stadium,
hexagon,
block_arrow,
rect_left_inv_arrow,
lean_right,
lean_left,
block_arrow: blockArrow,
rect_left_inv_arrow: rectLeftInvArrow,
lean_right: leanRight,
lean_left: leanLeft,
trapezoid,
inv_trapezoid,
rect_right_inv_arrow,
inv_trapezoid: invTrapezoid,
rect_right_inv_arrow: rectRightInvArrow,
cylinder,
start,
end,
@@ -1125,7 +1125,7 @@ const shapes = {
subroutine,
fork: forkJoin,
join: forkJoin,
class_box,
class_box: classBox,
};
let nodeElems = {};

View File

@@ -70,8 +70,9 @@ 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

@@ -78,28 +78,28 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
x = labelPosition.x;
y = labelPosition.y;
let p1_card_x, p1_card_y;
let p2_card_x, p2_card_y;
let p1CardX, p1CardY;
let p2CardX, p2CardY;
if (l % 2 !== 0 && l > 1) {
let cardinality_1_point = utils.calcCardinalityPosition(
let cardinality1Point = utils.calcCardinalityPosition(
relation.relation.type1 !== 'none',
path.points,
path.points[0]
);
let cardinality_2_point = utils.calcCardinalityPosition(
let cardinality2Point = utils.calcCardinalityPosition(
relation.relation.type2 !== 'none',
path.points,
path.points[l - 1]
);
log.debug('cardinality_1_point ' + JSON.stringify(cardinality_1_point));
log.debug('cardinality_2_point ' + JSON.stringify(cardinality_2_point));
log.debug('cardinality_1_point ' + JSON.stringify(cardinality1Point));
log.debug('cardinality_2_point ' + JSON.stringify(cardinality2Point));
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;
p1CardX = cardinality1Point.x;
p1CardY = cardinality1Point.y;
p2CardX = cardinality2Point.x;
p2CardY = cardinality2Point.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', p1_card_x)
.attr('y', p1_card_y)
.attr('x', p1CardX)
.attr('y', p1CardY)
.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', p2_card_x)
.attr('y', p2_card_y)
.attr('x', p2CardX)
.attr('y', p2CardY)
.attr('fill', 'black')
.attr('font-size', '6')
.text(relation.relationTitle2);

View File

@@ -30,18 +30,18 @@ const setupDompurifyHooksIfNotSetup = (() => {
})();
function setupDompurifyHooks() {
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target';
const temporaryAttribute = 'data-temp-href-target';
DOMPurify.addHook('beforeSanitizeAttributes', (node: Element) => {
if (node.tagName === 'A' && node.hasAttribute('target')) {
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') || '');
node.setAttribute(temporaryAttribute, node.getAttribute('target') || '');
}
});
DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => {
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) || '');
node.removeAttribute(TEMPORARY_ATTRIBUTE);
if (node.tagName === 'A' && node.hasAttribute(temporaryAttribute)) {
node.setAttribute('target', node.getAttribute(temporaryAttribute) || '');
node.removeAttribute(temporaryAttribute);
if (node.getAttribute('target') === '_blank') {
node.setAttribute('rel', 'noopener');
}

View File

@@ -53,7 +53,7 @@ function hexagon(parent, bbox, node) {
* @param bbox
* @param node
*/
function rect_left_inv_arrow(parent, bbox, node) {
function rectLeftInvArrow(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -75,7 +75,7 @@ function rect_left_inv_arrow(parent, bbox, node) {
* @param bbox
* @param node
*/
function lean_right(parent, bbox, node) {
function leanRight(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -96,7 +96,7 @@ function lean_right(parent, bbox, node) {
* @param bbox
* @param node
*/
function lean_left(parent, bbox, node) {
function leanLeft(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 inv_trapezoid(parent, bbox, node) {
function invTrapezoid(parent, bbox, node) {
const w = bbox.width;
const h = bbox.height;
const points = [
@@ -159,7 +159,7 @@ function inv_trapezoid(parent, bbox, node) {
* @param bbox
* @param node
*/
function rect_right_inv_arrow(parent, bbox, node) {
function rectRightInvArrow(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 = rect_left_inv_arrow;
render.shapes().rect_left_inv_arrow = rectLeftInvArrow;
// Add custom shape for box with inverted arrow on left side
render.shapes().lean_right = lean_right;
render.shapes().lean_right = leanRight;
// Add custom shape for box with inverted arrow on left side
render.shapes().lean_left = lean_left;
render.shapes().lean_left = leanLeft;
// 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 = inv_trapezoid;
render.shapes().inv_trapezoid = invTrapezoid;
// Add custom shape for box with inverted arrow on right side
render.shapes().rect_right_inv_arrow = rect_right_inv_arrow;
render.shapes().rect_right_inv_arrow = rectRightInvArrow;
}
/** @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 });
addShape({ rect_left_inv_arrow: rectLeftInvArrow });
// Add custom shape for box with inverted arrow on left side
addShape({ lean_right });
addShape({ lean_right: leanRight });
// Add custom shape for box with inverted arrow on left side
addShape({ lean_left });
addShape({ lean_left: leanLeft });
// 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 });
addShape({ inv_trapezoid: invTrapezoid });
// Add custom shape for box with inverted arrow on right side
addShape({ rect_right_inv_arrow });
addShape({ rect_right_inv_arrow: rectRightInvArrow });
}
/**

View File

@@ -246,16 +246,19 @@ 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':
@@ -425,9 +428,9 @@ export const draw = async function (text, id, _version, diagObj) {
selectAll('cluster').append('text');
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);
for (const node of subG.nodes) {
log.info('Setting up subgraphs', node, subG.id);
g.setParent(node, subG.id);
}
}
await addVertices(vert, g, id, root, doc, diagObj);

View File

@@ -339,14 +339,14 @@ export const draw = async function (text, id, _version, diagObj) {
selectAll('cluster').append('text');
for (let j = 0; j < subG.nodes.length; j++) {
for (const subGNode of subG.nodes) {
log.warn(
'Setting subgraph',
subG.nodes[j],
diagObj.db.lookUpDomId(subG.nodes[j]),
subGNode,
diagObj.db.lookUpDomId(subGNode),
diagObj.db.lookUpDomId(subG.id)
);
g.setParent(diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subG.id));
g.setParent(diagObj.db.lookUpDomId(subGNode), 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 (let j = 0; j < subG.classes.length; j++) {
clusterEl[0].classList.add(subG.classes[j]);
for (const subGClass of subG.classes) {
clusterEl[0].classList.add(subGClass);
}
}
}

View File

@@ -591,7 +591,7 @@ describe('[Text] when parsing', () => {
it('should throw error for escaping quotes in text state', function () {
//prettier-ignore
const str = 'graph TD; A[This is a \"()\" in text];'; //eslint-disable-line no-useless-escape
const str = 'graph TD; A[This is a "()" in text];'; //eslint-disable-line no-useless-escape
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});

View File

@@ -724,6 +724,7 @@ export const draw = function (text, id, version, diagObj) {
.attr('x', 10)
.attr('y', function (d, i) {
if (i > 0) {
// biome-ignore lint/correctness/noUnreachable: <explanation>
for (let j = 0; j < i; j++) {
prevGap += numOccurrences[i - 1][1];
return (d[1] * theGap) / 2 + prevGap * theGap + theTopPad;

View File

@@ -157,30 +157,40 @@ describe('when parsing a gantt diagram it', function () {
${'crit'} | ${false} | ${false} | ${true} | ${false}
${'active'} | ${false} | ${false} | ${false} | ${true}
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
`)('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => {
const str =
'gantt\n' +
'dateFormat YYYY-MM-DD\n' +
'title Adding gantt diagram functionality to mermaid\n' +
'section Documentation\n' +
'test task:' +
tags +
', 2014-01-01, 2014-01-04';
`)(
'should handle a task with tags $tags',
({
tags,
// Do not remove, these are used in eval.
milestone,
done,
crit,
active,
}) => {
const str =
'gantt\n' +
'dateFormat YYYY-MM-DD\n' +
'title Adding gantt diagram functionality to mermaid\n' +
'section Documentation\n' +
'test task:' +
tags +
', 2014-01-01, 2014-01-04';
const allowedTags = ['active', 'done', 'crit', 'milestone'];
const allowedTags = ['active', 'done', 'crit', 'milestone'];
expect(parserFnConstructor(str)).not.toThrow();
expect(parserFnConstructor(str)).not.toThrow();
const tasks = parser.yy.getTasks();
const tasks = parser.yy.getTasks();
allowedTags.forEach(function (t) {
if (eval(t)) {
expect(tasks[0][t]).toBeTruthy();
} else {
expect(tasks[0][t]).toBeFalsy();
}
});
});
allowedTags.forEach(function (t) {
if (eval(t)) {
expect(tasks[0][t]).toBeTruthy();
} else {
expect(tasks[0][t]).toBeFalsy();
}
});
}
);
it('should parse callback specifier with no args', function () {
spyOn(ganttDb, 'setClickEvent');
const str =
@@ -207,7 +217,6 @@ describe('when parsing a gantt diagram it', function () {
'click cl2 call ganttTestClick("test0", test1, test2)\n';
expect(parserFnConstructor(str)).not.toThrow();
const args = '"test1", "test2", "test3"';
expect(ganttDb.setClickEvent).toHaveBeenCalledWith(
'cl2',
'ganttTestClick',

View File

@@ -147,9 +147,9 @@ export const branch = function (name, order) {
}
};
export const merge = function (otherBranch, custom_id, override_type, custom_tag) {
export const merge = function (otherBranch, customId, overrideType, customTag) {
otherBranch = common.sanitizeText(otherBranch, getConfig());
custom_id = common.sanitizeText(custom_id, getConfig());
customId = common.sanitizeText(customId, getConfig());
const currentCommit = commits.get(branches.get(curBranch));
const otherCommit = commits.get(branches.get(otherBranch));
@@ -209,19 +209,19 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
expected: ['branch abc'],
};
throw error;
} else if (custom_id && commits.has(custom_id)) {
} else if (customId && commits.has(customId)) {
let error = new Error(
'Incorrect usage of "merge". Commit with id:' +
custom_id +
customId +
' already exists, use different custom Id'
);
error.hash = {
text: 'merge ' + otherBranch + custom_id + override_type + custom_tag,
token: 'merge ' + otherBranch + custom_id + override_type + custom_tag,
text: 'merge ' + otherBranch + customId + overrideType + customTag,
token: 'merge ' + otherBranch + customId + overrideType + customTag,
line: '1',
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
expected: [
'merge ' + otherBranch + ' ' + custom_id + '_UNIQUE ' + override_type + ' ' + custom_tag,
'merge ' + otherBranch + ' ' + customId + '_UNIQUE ' + overrideType + ' ' + customTag,
],
};
@@ -237,15 +237,15 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
// } else {
// create merge commit
const commit = {
id: custom_id ? custom_id : seq + '-' + getId(),
id: customId ? customId : seq + '-' + getId(),
message: 'merged branch ' + otherBranch + ' into ' + curBranch,
seq: seq++,
parents: [head == null ? null : head.id, branches.get(otherBranch)],
branch: curBranch,
type: commitType.MERGE,
customType: override_type,
customId: custom_id ? true : false,
tag: custom_tag ? custom_tag : '',
customType: overrideType,
customId: customId ? true : false,
tag: customTag ? customTag : '',
};
head = commit;
commits.set(commit.id, commit);

View File

@@ -405,11 +405,11 @@ const drawCommits = (svg, commits, modifyGraph) => {
text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
} else {
let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5;
let r_y = 10 + (bbox.width / 25) * 8.5;
let rX = -7.5 - ((bbox.width + 10) / 25) * 9.5;
let rY = 10 + (bbox.width / 25) * 8.5;
wrapper.attr(
'transform',
'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')'
'translate(' + rX + ', ' + rY + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')'
);
}
}

View File

@@ -40,9 +40,9 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
const db = diagObj.db as PieDB;
const globalConfig: MermaidConfig = getConfig();
const pieConfig: Required<PieDiagramConfig> = cleanAndMerge(db.getConfig(), globalConfig.pie);
const MARGIN = 40;
const LEGEND_RECT_SIZE = 18;
const LEGEND_SPACING = 4;
const margin = 40;
const legendRectSize = 18;
const legendSpacing = 4;
const height = 450;
const pieWidth: number = height;
const svg: SVG = selectSvgElement(id);
@@ -54,7 +54,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
outerStrokeWidth ??= 2;
const textPosition: number = pieConfig.textPosition;
const radius: number = Math.min(pieWidth, height) / 2 - MARGIN;
const radius: number = Math.min(pieWidth, height) / 2 - margin;
// Shape helper to build arcs:
const arcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Section>> = arc<d3.PieArcDatum<D3Section>>()
.innerRadius(0)
@@ -139,25 +139,25 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.append('g')
.attr('class', 'legend')
.attr('transform', (_datum, index: number): string => {
const height = LEGEND_RECT_SIZE + LEGEND_SPACING;
const height = legendRectSize + legendSpacing;
const offset = (height * color.domain().length) / 2;
const horizontal = 12 * LEGEND_RECT_SIZE;
const horizontal = 12 * legendRectSize;
const vertical = index * height - offset;
return 'translate(' + horizontal + ',' + vertical + ')';
});
legend
.append('rect')
.attr('width', LEGEND_RECT_SIZE)
.attr('height', LEGEND_RECT_SIZE)
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend
.data(arcs)
.append('text')
.attr('x', LEGEND_RECT_SIZE + LEGEND_SPACING)
.attr('y', LEGEND_RECT_SIZE - LEGEND_SPACING)
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text((datum: d3.PieArcDatum<D3Section>): string => {
const { label, value } = datum.data;
if (db.getShowData()) {
@@ -173,7 +173,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.map((node) => (node as Element)?.getBoundingClientRect().width ?? 0)
);
const totalWidth = pieWidth + MARGIN + LEGEND_RECT_SIZE + LEGEND_SPACING + longestTextWidth;
const totalWidth = pieWidth + margin + legendRectSize + legendSpacing + longestTextWidth;
// Set viewBox
svg.attr('viewBox', `0 0 ${totalWidth} ${height}`);

View File

@@ -45,13 +45,13 @@ class SankeyNode {
constructor(public ID: string) {}
}
const findOrCreateNode = (ID: string): SankeyNode => {
ID = common.sanitizeText(ID, getConfig());
const findOrCreateNode = (id: string): SankeyNode => {
id = common.sanitizeText(id, getConfig());
let node = nodesMap.get(ID);
let node = nodesMap.get(id);
if (node === undefined) {
node = new SankeyNode(ID);
nodesMap.set(ID, node);
node = new SankeyNode(id);
nodesMap.set(id, node);
nodes.push(node);
}
return node;

View File

@@ -49,11 +49,11 @@ export const drawPopup = function (elem, actor, minMenuWidth, textAttrs, forceMe
rectElem.attr('height', rectData.height);
rectElem.attr('rx', rectData.rx);
rectElem.attr('ry', rectData.ry);
let linkY = 20;
if (links != null) {
var linkY = 20;
for (let key in links) {
var linkElem = g.append('a');
var sanitizedLink = sanitizeUrl(links[key]);
const linkElem = g.append('a');
const sanitizedLink = sanitizeUrl(links[key]);
linkElem.attr('xlink:href', sanitizedLink);
linkElem.attr('target', '_blank');

View File

@@ -88,9 +88,9 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
expect(classes.get('exampleStyleClass').styles[0]).toEqual('background:#bbb');
expect(classes.get('exampleStyleClass').styles[1]).toEqual('border:1px solid red');
const state_a = stateDb.getState('a');
expect(state_a.classes.length).toEqual(1);
expect(state_a.classes[0]).toEqual('exampleStyleClass');
const stateA = stateDb.getState('a');
expect(stateA.classes.length).toEqual(1);
expect(stateA.classes[0]).toEqual('exampleStyleClass');
});
it('can be applied to a state with an id containing _', function () {
@@ -109,9 +109,9 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
expect(classes.get('exampleStyleClass').styles[0]).toBe('background:#bbb');
expect(classes.get('exampleStyleClass').styles[1]).toBe('border:1px solid red');
const state_a_a = stateDiagram.parser.yy.getState('a_a');
expect(state_a_a.classes.length).toEqual(1);
expect(state_a_a.classes[0]).toEqual('exampleStyleClass');
const stateAA = stateDiagram.parser.yy.getState('a_a');
expect(stateAA.classes.length).toEqual(1);
expect(stateAA.classes[0]).toEqual('exampleStyleClass');
});
describe('::: syntax', () => {

View File

@@ -130,12 +130,12 @@ describe('state diagram V2, ', function () {
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const rels = stateDb.getRelations();
const rel_1_2 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State2');
expect(rel_1_2.relationTitle).toEqual('Transition 1');
const rel_1_3 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State3');
expect(rel_1_3.relationTitle).toEqual('Transition 2');
const rel_1_4 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State4');
expect(rel_1_4.relationTitle).toEqual('Transition 3');
const rel12 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State2');
expect(rel12.relationTitle).toEqual('Transition 1');
const rel13 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State3');
expect(rel13.relationTitle).toEqual('Transition 2');
const rel14 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State4');
expect(rel14.relationTitle).toEqual('Transition 3');
});
});
@@ -408,10 +408,10 @@ describe('state diagram V2, ', function () {
expect(states.get('Active').doc[0].id).toEqual('Idle');
const rels = stateDb.getRelations();
const rel_Inactive_Idle = rels.find((rel) => rel.id1 === 'Inactive' && rel.id2 === 'Idle');
expect(rel_Inactive_Idle.relationTitle).toEqual('ACT');
const rel_Active_Active = rels.find((rel) => rel.id1 === 'Active' && rel.id2 === 'Active');
expect(rel_Active_Active.relationTitle).toEqual('LOG');
const relInactiveIdle = rels.find((rel) => rel.id1 === 'Inactive' && rel.id2 === 'Idle');
expect(relInactiveIdle.relationTitle).toEqual('ACT');
const relActiveActive = rels.find((rel) => rel.id1 === 'Active' && rel.id2 === 'Active');
expect(relActiveActive.relationTitle).toEqual('LOG');
});
});
});

View File

@@ -362,8 +362,7 @@ const setupDoc = (g, parentParsedItem, doc, diagramStates, diagramDb, altFlag) =
const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
let dir = defaultDir;
if (parsedItem.doc) {
for (let i = 0; i < parsedItem.doc.length; i++) {
const parsedItemDoc = parsedItem.doc[i];
for (const parsedItemDoc of parsedItem.doc) {
if (parsedItemDoc.stmt === 'dir') {
dir = parsedItemDoc.value;
}

View File

@@ -15,9 +15,7 @@ const genSections = (options) => {
for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) {
const sw = '' + (17 - 3 * i);
sections += `
.section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${
i - 1
} path {
.section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${i - 1} path {
fill: ${options['cScale' + i]};
}
.section-${i - 1} text {

View File

@@ -29,7 +29,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
//1. Fetch the configuration
const conf = getConfig();
// @ts-expect-error - wrong config?
const LEFT_MARGIN = conf.leftMargin ?? 50;
const leftMargin = conf.leftMargin ?? 50;
log.debug('timeline', diagObj.db);
@@ -68,7 +68,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
//let sectionBeginX = 0;
let depthY = 0;
let sectionBeginY = 0;
let masterX = 50 + LEFT_MARGIN;
let masterX = 50 + leftMargin;
//sectionBeginX = masterX;
let masterY = 50;
sectionBeginY = 50;
@@ -115,8 +115,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
maxEventCount = Math.max(maxEventCount, task.events.length);
//calculate maxEventLineLength
let maxEventLineLengthTemp = 0;
for (let j = 0; j < task.events.length; j++) {
const event = task.events[j];
for (const event of task.events) {
const eventNode = {
descr: event,
section: task.section,
@@ -203,7 +202,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
svg
.append('text')
.text(title)
.attr('x', box.width / 2 - LEFT_MARGIN)
.attr('x', box.width / 2 - leftMargin)
.attr('font-size', '4ex')
.attr('font-weight', 'bold')
.attr('y', 20);
@@ -215,9 +214,9 @@ export const draw = function (text: string, id: string, version: string, diagObj
// Draw activity line
lineWrapper
.append('line')
.attr('x1', LEFT_MARGIN)
.attr('x1', leftMargin)
.attr('y1', depthY) // One section head + one task + margins
.attr('x2', box.width + 3 * LEFT_MARGIN) // Subtract stroke width so arrow point is retained
.attr('x2', box.width + 3 * leftMargin) // Subtract stroke width so arrow point is retained
.attr('y2', depthY)
.attr('stroke-width', 4)
.attr('stroke', 'black')

View File

@@ -75,8 +75,8 @@ function textSanitizer(text: string) {
return sanitizeText(text.trim(), config);
}
function setTmpSVGG(SVGG: Group) {
tmpSVGGroup = SVGG;
function setTmpSVGG(svgg: Group) {
tmpSVGGroup = svgg;
}
function setOrientation(orientation: string) {
if (orientation === 'horizontal') {

View File

@@ -1,4 +1,4 @@
import { defineConfig, MarkdownOptions } from 'vitepress';
import { defineConfig, type MarkdownOptions } from 'vitepress';
import { version } from '../../../package.json';
import MermaidExample from './mermaid-markdown-all.js';

View File

@@ -1,5 +1,5 @@
import contributorUsernamesJson from './contributor-names.json';
import { CoreTeam, knut, plainTeamMembers } from './teamMembers.js';
import { type CoreTeam, knut, plainTeamMembers } from './teamMembers.js';
const contributorUsernames: string[] = contributorUsernamesJson;

View File

@@ -32,7 +32,7 @@ async function fetchAvatars() {
});
contributors = JSON.parse(await readFile(pathContributors, { encoding: 'utf-8' }));
let avatars = contributors.map((name) => {
const avatars = contributors.map((name) => {
download(`https://github.com/${name}.png?size=100`, getAvatarPath(name));
});

View File

@@ -61,7 +61,7 @@ onMounted(async () => {
//refresh images on first render
const hasImages = /<img([\w\W]+?)>/.exec(code.value)?.length > 0;
if (hasImages)
if (hasImages) {
setTimeout(() => {
let imgElements = document.getElementsByTagName('img');
let imgs = Array.from(imgElements);
@@ -80,6 +80,7 @@ onMounted(async () => {
});
}
}, 100);
}
});
onUnmounted(() => mut.disconnect());

View File

@@ -170,8 +170,9 @@ describe('when using mermaid and ', () => {
).resolves.not.toThrow();
});
it('should throw for an invalid flow definition', async () => {
await expect(mermaid.parse('graph TQ;A--x|text including URL space|B;')).rejects
.toThrowErrorMatchingInlineSnapshot(`
await expect(
mermaid.parse('graph TQ;A--x|text including URL space|B;')
).rejects.toThrowErrorMatchingInlineSnapshot(`
[Error: Lexical error on line 1. Unrecognized text.
graph TQ;A--x|text includ
-----^]

View File

@@ -98,7 +98,7 @@ const run = async function (
}
) {
try {
await runThrowsErrors(options);
await runThrowsErrors(42);
} catch (e) {
if (isDetailedError(e)) {
log.error(e.str);

View File

@@ -207,7 +207,7 @@ describe('mermaidAPI', () => {
});
it('uses the height and appends px from the svgElement given', () => {
const faux_svgElement = {
const fauxSvgElement = {
viewBox: {
baseVal: {
height: 42,
@@ -215,7 +215,7 @@ describe('mermaidAPI', () => {
},
};
const result = putIntoIFrame(inputSvgCode, faux_svgElement);
const result = putIntoIFrame(inputSvgCode, fauxSvgElement);
expect(result).toMatch(/style="(.*)height:42px;/);
});
});
@@ -226,54 +226,54 @@ describe('mermaidAPI', () => {
describe('appendDivSvgG', () => {
const fauxGNode = new MockedD3();
const parent_append_spy = vi.spyOn(fauxParentNode, 'append').mockReturnValue(fauxEnclosingDiv);
const div_append_spy = vi.spyOn(fauxEnclosingDiv, 'append').mockReturnValue(fauxSvgNode);
const parentAppendSpy = vi.spyOn(fauxParentNode, 'append').mockReturnValue(fauxEnclosingDiv);
const divAppendSpy = vi.spyOn(fauxEnclosingDiv, 'append').mockReturnValue(fauxSvgNode);
// @ts-ignore @todo TODO why is this getting a type error?
const div_attr_spy = vi.spyOn(fauxEnclosingDiv, 'attr').mockReturnValue(fauxEnclosingDiv);
const svg_append_spy = vi.spyOn(fauxSvgNode, 'append').mockReturnValue(fauxGNode);
const divAttrSpy = vi.spyOn(fauxEnclosingDiv, 'attr').mockReturnValue(fauxEnclosingDiv);
const svgAppendSpy = vi.spyOn(fauxSvgNode, 'append').mockReturnValue(fauxGNode);
// @ts-ignore @todo TODO why is this getting a type error?
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
const svgAttrSpy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
// cspell:ignore dthe
it('appends a div node', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(parent_append_spy).toHaveBeenCalledWith('div');
expect(div_append_spy).toHaveBeenCalledWith('svg');
expect(parentAppendSpy).toHaveBeenCalledWith('div');
expect(divAppendSpy).toHaveBeenCalledWith('svg');
});
it('the id for the div is "d" with the id appended', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(div_attr_spy).toHaveBeenCalledWith('id', 'dtheId');
expect(divAttrSpy).toHaveBeenCalledWith('id', 'dtheId');
});
it('sets the style for the div if one is given', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId', 'given div style', 'given x link');
expect(div_attr_spy).toHaveBeenCalledWith('style', 'given div style');
expect(divAttrSpy).toHaveBeenCalledWith('style', 'given div style');
});
it('appends a svg node to the div node', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(div_attr_spy).toHaveBeenCalledWith('id', 'dtheId');
expect(divAttrSpy).toHaveBeenCalledWith('id', 'dtheId');
});
it('sets the svg width to 100%', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(svg_attr_spy).toHaveBeenCalledWith('width', '100%');
expect(svgAttrSpy).toHaveBeenCalledWith('width', '100%');
});
it('the svg id is the id', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(svg_attr_spy).toHaveBeenCalledWith('id', 'theId');
expect(svgAttrSpy).toHaveBeenCalledWith('id', 'theId');
});
it('the svg xml namespace is the 2000 standard', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(svg_attr_spy).toHaveBeenCalledWith('xmlns', 'http://www.w3.org/2000/svg');
expect(svgAttrSpy).toHaveBeenCalledWith('xmlns', 'http://www.w3.org/2000/svg');
});
it('sets the svg xlink if one is given', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId', 'div style', 'given x link');
expect(svg_attr_spy).toHaveBeenCalledWith('xmlns:xlink', 'given x link');
expect(svgAttrSpy).toHaveBeenCalledWith('xmlns:xlink', 'given x link');
});
it('appends a g (group) node to the svg node', () => {
appendDivSvgG(fauxParentNode, 'theId', 'dtheId');
expect(svg_append_spy).toHaveBeenCalledWith('g');
expect(svgAppendSpy).toHaveBeenCalledWith('g');
});
it('returns the given parentRoot d3 nodes', () => {
expect(appendDivSvgG(fauxParentNode, 'theId', 'dtheId')).toEqual(fauxParentNode);
@@ -283,7 +283,7 @@ describe('mermaidAPI', () => {
describe('createCssStyles', () => {
const serif = 'serif';
const sansSerif = 'sans-serif';
const mocked_config_with_htmlLabels: MermaidConfig = {
const mockedConfigWithHtmlLabels: MermaidConfig = {
themeCSS: 'default',
fontFamily: serif,
altFontFamily: sansSerif,
@@ -291,15 +291,15 @@ describe('mermaidAPI', () => {
};
it('gets the cssStyles from the theme', () => {
const styles = createCssStyles(mocked_config_with_htmlLabels, null);
const styles = createCssStyles(mockedConfigWithHtmlLabels, null);
expect(styles).toMatch(/^\ndefault(.*)/);
});
it('gets the fontFamily from the config', () => {
const styles = createCssStyles(mocked_config_with_htmlLabels, new Map());
const styles = createCssStyles(mockedConfigWithHtmlLabels, new Map());
expect(styles).toMatch(/(.*)\n:root { --mermaid-font-family: serif(.*)/);
});
it('gets the alt fontFamily from the config', () => {
const styles = createCssStyles(mocked_config_with_htmlLabels, undefined);
const styles = createCssStyles(mockedConfigWithHtmlLabels, undefined);
expect(styles).toMatch(/(.*)\n:root { --mermaid-alt-font-family: sans-serif(.*)/);
});
@@ -310,13 +310,13 @@ describe('mermaidAPI', () => {
const classDefs = { classDef1, classDef2, classDef3 };
describe('the graph supports classDefs', () => {
const REGEXP_SPECIALS = ['^', '$', '?', '(', '{', '[', '.', '*', '!'];
const regexpSpecials = ['^', '$', '?', '(', '{', '[', '.', '*', '!'];
// prefix any special RegExp characters in the given string with a \ so we can use the literal character in a RegExp
function escapeForRegexp(str: string) {
const strChars = [...str]; // split into array of every char
const strEscaped = strChars.map((char) => {
if (REGEXP_SPECIALS.includes(char)) {
if (regexpSpecials.includes(char)) {
return `\\${char}`;
} else {
return char;
@@ -326,7 +326,7 @@ describe('mermaidAPI', () => {
}
// Common test expecting given styles to have .classDef1 and .classDef2 statements but not .classDef3
function expect_styles_matchesHtmlElements(styles: string, htmlElement: string) {
function expectStylesMatchesHtmlElements(styles: string, htmlElement: string) {
expect(styles).toMatch(
new RegExp(
`\\.classDef1 ${escapeForRegexp(
@@ -344,7 +344,7 @@ describe('mermaidAPI', () => {
}
// Common test expecting given textStyles to have .classDef2 and .classDef3 statements but not .classDef1
function expect_textStyles_matchesHtmlElements(textStyles: string, htmlElement: string) {
function expectTextStylesMatchesHtmlElements(textStyles: string, htmlElement: string) {
expect(textStyles).toMatch(
new RegExp(
`\\.classDef2 ${escapeForRegexp(htmlElement)} \\{ textStyle2-1 !important; }`
@@ -367,7 +367,7 @@ describe('mermaidAPI', () => {
}
// common suite and tests to verify that the right styles are created with the right htmlElements
function expect_correct_styles_with_htmlElements(mocked_config: MermaidConfig) {
function expectCorrectStylesWithHtmlElements(mockedConfig: MermaidConfig) {
describe('creates styles for "> *" and "span" elements', () => {
const htmlElements = ['> *', 'span'];
@@ -375,21 +375,21 @@ describe('mermaidAPI', () => {
// @todo TODO Can't figure out how to spy on the cssImportantStyles method.
// That would be a much better approach than manually checking the result
const styles = createCssStyles(mocked_config, new Map(Object.entries(classDefs)));
const styles = createCssStyles(mockedConfig, new Map(Object.entries(classDefs)));
htmlElements.forEach((htmlElement) => {
expect_styles_matchesHtmlElements(styles, htmlElement);
expectStylesMatchesHtmlElements(styles, htmlElement);
});
expect_textStyles_matchesHtmlElements(styles, 'tspan');
expectTextStylesMatchesHtmlElements(styles, 'tspan');
});
});
}
it('there are htmlLabels in the configuration', () => {
expect_correct_styles_with_htmlElements(mocked_config_with_htmlLabels);
expectCorrectStylesWithHtmlElements(mockedConfigWithHtmlLabels);
});
it('there are flowchart.htmlLabels in the configuration', () => {
const mocked_config_flowchart_htmlLabels: MermaidConfig = {
const mockedConfigFlowchartHtmlLabels: MermaidConfig = {
themeCSS: 'default',
fontFamily: 'serif',
altFontFamily: 'sans-serif',
@@ -397,11 +397,11 @@ describe('mermaidAPI', () => {
htmlLabels: true,
},
};
expect_correct_styles_with_htmlElements(mocked_config_flowchart_htmlLabels);
expectCorrectStylesWithHtmlElements(mockedConfigFlowchartHtmlLabels);
});
describe('no htmlLabels in the configuration', () => {
const mocked_config_no_htmlLabels = {
const mockedConfigNoHtmlLabels = {
themeCSS: 'default',
fontFamily: 'serif',
altFontFamily: 'sans-serif',
@@ -414,13 +414,13 @@ describe('mermaidAPI', () => {
// TODO Can't figure out how to spy on the cssImportantStyles method. That would be a much better approach than manually checking the result.
const styles = createCssStyles(
mocked_config_no_htmlLabels,
mockedConfigNoHtmlLabels,
new Map(Object.entries(classDefs))
);
htmlElements.forEach((htmlElement) => {
expect_styles_matchesHtmlElements(styles, htmlElement);
expectStylesMatchesHtmlElements(styles, htmlElement);
});
expect_textStyles_matchesHtmlElements(styles, 'tspan');
expectTextStylesMatchesHtmlElements(styles, 'tspan');
});
});
});
@@ -692,8 +692,9 @@ describe('mermaidAPI', () => {
).resolves.toBe(false);
});
it('resolves for valid definition', async () => {
await expect(mermaidAPI.parse('graph TD;A--x|text including URL space|B;')).resolves
.toMatchInlineSnapshot(`
await expect(
mermaidAPI.parse('graph TD;A--x|text including URL space|B;')
).resolves.toMatchInlineSnapshot(`
{
"diagramType": "flowchart-v2",
}
@@ -748,15 +749,12 @@ describe('mermaidAPI', () => {
const expectedDiagramType = testedDiagram.expectedType;
it('should set aria-roledescription to the diagram type AND should call addSVGa11yTitleDescription', async () => {
const a11yDiagramInfo_spy = vi.spyOn(accessibility, 'setA11yDiagramInfo');
const a11yTitleDesc_spy = vi.spyOn(accessibility, 'addSVGa11yTitleDescription');
const a11yDiagramInfoSpy = vi.spyOn(accessibility, 'setA11yDiagramInfo');
const a11yTitleDescSpy = vi.spyOn(accessibility, 'addSVGa11yTitleDescription');
const result = await mermaidAPI.render(id, diagramText);
expect(result.diagramType).toBe(expectedDiagramType);
expect(a11yDiagramInfo_spy).toHaveBeenCalledWith(
expect.anything(),
expectedDiagramType
);
expect(a11yTitleDesc_spy).toHaveBeenCalled();
expect(a11yDiagramInfoSpy).toHaveBeenCalledWith(expect.anything(), expectedDiagramType);
expect(a11yTitleDescSpy).toHaveBeenCalled();
});
});
});

View File

@@ -363,14 +363,14 @@ const render = async function (
const idSelector = '#' + id;
const iFrameID = 'i' + id;
const iFrameID_selector = '#' + iFrameID;
const iFrameIdSelector = '#' + iFrameID;
const enclosingDivID = 'd' + id;
const enclosingDivID_selector = '#' + enclosingDivID;
const enclosingDivIdSelector = '#' + enclosingDivID;
const removeTempElements = () => {
// -------------------------------------------------------------------------------
// Remove the temporary HTML element if appropriate
const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector;
const tmpElementSelector = isSandboxed ? iFrameIdSelector : enclosingDivIdSelector;
const node = select(tmpElementSelector).node();
if (node && 'remove' in node) {
node.remove();
@@ -442,7 +442,7 @@ const render = async function (
}
// Get the temporary div element containing the svg
const element = root.select(enclosingDivID_selector).node();
const element = root.select(enclosingDivIdSelector).node();
const diagramType = diag.type;
// -------------------------------------------------------------------------------
@@ -473,7 +473,7 @@ const render = async function (
}
// This is the d3 node for the svg element
const svgNode = root.select(`${enclosingDivID_selector} svg`);
const svgNode = root.select(`${enclosingDivIdSelector} svg`);
const a11yTitle: string | undefined = diag.db.getAccTitle?.();
const a11yDescr: string | undefined = diag.db.getAccDescription?.();
addA11yInfo(diagramType, svgNode, a11yTitle, a11yDescr);
@@ -482,13 +482,13 @@ const render = async function (
root.select(`[id="${id}"]`).selectAll('foreignobject > *').attr('xmlns', XMLNS_XHTML_STD);
// Fix for when the base tag is used
let svgCode: string = root.select(enclosingDivID_selector).node().innerHTML;
let svgCode: string = root.select(enclosingDivIdSelector).node().innerHTML;
log.debug('config.arrowMarkerAbsolute', config.arrowMarkerAbsolute);
svgCode = cleanUpSvgCode(svgCode, isSandboxed, evaluate(config.arrowMarkerAbsolute));
if (isSandboxed) {
const svgEl = root.select(enclosingDivID_selector + ' svg').node();
const svgEl = root.select(enclosingDivIdSelector + ' svg').node();
svgCode = putIntoIFrame(svgCode, svgEl);
} else if (!isLooseSecurityLevel) {
// Sanitize the svgCode using DOMPurify

View File

@@ -74,6 +74,7 @@ export class MockedD3 {
attr(attrName: string): undefined | string;
attr(attrName: string, attrValue: string): MockedD3;
attr(attrName: string, attrValue?: string): undefined | string | MockedD3 {
// biome-ignore lint/style/noArguments: <explanation>
if (arguments.length === 1) {
return this.attribs.get(attrName);
} else {
@@ -105,8 +106,8 @@ export class MockedD3 {
// Real implementation returns an HTML Element
public node = vi.fn().mockImplementation(() => {
const topElem = this._containingHTMLdoc.createElement('svg');
const elem_svgChild = this._containingHTMLdoc.createElement('svg'); // another svg element
topElem.appendChild(elem_svgChild);
const elemSvgChild = this._containingHTMLdoc.createElement('svg'); // another svg element
topElem.appendChild(elemSvgChild);
return topElem;
});

View File

@@ -11,51 +11,51 @@ addDiagrams();
describe('when assignWithDepth: should merge objects within objects', function () {
it('should handle simple, depth:1 types (identity)', function () {
const config_0 = { foo: 'bar', bar: 0 };
const config_1 = { foo: 'bar', bar: 0 };
const result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_1);
const config0 = { foo: 'bar', bar: 0 };
const config1 = { foo: 'bar', bar: 0 };
const result = assignWithDepth(config0, config1);
expect(result).toEqual(config1);
});
it('should handle simple, depth:1 types (dst: undefined)', function () {
const config_0 = undefined;
const config_1 = { foo: 'bar', bar: 0 };
const result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_1);
const config0 = undefined;
const config1 = { foo: 'bar', bar: 0 };
const result = assignWithDepth(config0, config1);
expect(result).toEqual(config1);
});
it('should handle simple, depth:1 types (src: undefined)', function () {
const config_0 = { foo: 'bar', bar: 0 };
const config_1 = undefined;
const result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_0);
const config0 = { foo: 'bar', bar: 0 };
const config1 = undefined;
const result = assignWithDepth(config0, config1);
expect(result).toEqual(config0);
});
it('should handle simple, depth:1 types (merge)', function () {
const config_0 = { foo: 'bar', bar: 0 };
const config_1 = { foo: 'foo' };
const result = assignWithDepth(config_0, config_1);
const config0 = { foo: 'bar', bar: 0 };
const config1 = { foo: 'foo' };
const result = assignWithDepth(config0, config1);
expect(result).toEqual({ foo: 'foo', bar: 0 });
});
it('should handle depth:2 types (dst: orphan)', function () {
const config_0 = { foo: 'bar', bar: { foo: 'bar' } };
const config_1 = { foo: 'bar' };
const result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_0);
const config0 = { foo: 'bar', bar: { foo: 'bar' } };
const config1 = { foo: 'bar' };
const result = assignWithDepth(config0, config1);
expect(result).toEqual(config0);
});
it('should handle depth:2 types (dst: object, src: simple type)', function () {
const config_0 = { foo: 'bar', bar: { foo: 'bar' } };
const config_1 = { foo: 'foo', bar: 'should NOT clobber' };
const result = assignWithDepth(config_0, config_1);
const config0 = { foo: 'bar', bar: { foo: 'bar' } };
const config1 = { foo: 'foo', bar: 'should NOT clobber' };
const result = assignWithDepth(config0, config1);
expect(result).toEqual({ foo: 'foo', bar: { foo: 'bar' } });
});
it('should handle depth:2 types (src: orphan)', function () {
const config_0 = { foo: 'bar' };
const config_1 = { foo: 'bar', bar: { foo: 'bar' } };
const result = assignWithDepth(config_0, config_1);
expect(result).toEqual(config_1);
const config0 = { foo: 'bar' };
const config1 = { foo: 'bar', bar: { foo: 'bar' } };
const result = assignWithDepth(config0, config1);
expect(result).toEqual(config1);
});
it('should handle depth:2 types (merge)', function () {
const config_0 = { foo: 'bar', bar: { foo: 'bar' }, boofar: 1 };
const config_1 = { foo: 'foo', bar: { bar: 0 }, foobar: 'foobar' };
const result = assignWithDepth(config_0, config_1);
const config0 = { foo: 'bar', bar: { foo: 'bar' }, boofar: 1 };
const config1 = { foo: 'foo', bar: { bar: 0 }, foobar: 'foobar' };
const result = assignWithDepth(config0, config1);
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'bar', bar: 0 },
@@ -64,17 +64,17 @@ describe('when assignWithDepth: should merge objects within objects', function (
});
});
it('should handle depth:3 types (merge with clobber because assignWithDepth::depth == 2)', function () {
const config_0 = {
const config0 = {
foo: 'bar',
bar: { foo: 'bar', bar: { foo: { message: 'this', willbe: 'clobbered' } } },
boofar: 1,
};
const config_1 = {
const config1 = {
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'clobbered other foo' } } },
foobar: 'foobar',
};
const result = assignWithDepth(config_0, config_1);
const result = assignWithDepth(config0, config1);
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'clobbered other foo' } } },
@@ -83,7 +83,7 @@ describe('when assignWithDepth: should merge objects within objects', function (
});
});
it('should handle depth:3 types (merge with clobber because assignWithDepth::depth == 1)', function () {
const config_0 = {
const config0 = {
foo: 'bar',
bar: {
foo: 'bar',
@@ -91,12 +91,12 @@ describe('when assignWithDepth: should merge objects within objects', function (
},
boofar: 1,
};
const config_1 = {
const config1 = {
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
foobar: 'foobar',
};
const result = assignWithDepth(config_0, config_1, { depth: 1 });
const result = assignWithDepth(config0, config1, { depth: 1 });
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
@@ -105,17 +105,17 @@ describe('when assignWithDepth: should merge objects within objects', function (
});
});
it('should handle depth:3 types (merge with no clobber because assignWithDepth::depth == 3)', function () {
const config_0 = {
const config0 = {
foo: 'bar',
bar: { foo: 'bar', bar: { foo: { message: '', willbe: 'present' } } },
boofar: 1,
};
const config_1 = {
const config1 = {
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'this' } } },
foobar: 'foobar',
};
const result = assignWithDepth(config_0, config_1, { depth: 3 });
const result = assignWithDepth(config0, config1, { depth: 3 });
expect(result).toEqual({
foo: 'foo',
bar: { foo: 'foo', bar: { foo: { message: 'this', willbe: 'present' } } },

View File

@@ -173,6 +173,7 @@ export const detectDirective = function (
);
let match: RegExpExecArray | null;
const result: Directive[] = [];
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
while ((match = directiveRegex.exec(text)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (match.index === directiveRegex.lastIndex) {
@@ -443,7 +444,10 @@ function calcTerminalLabelPosition(
* @param arr - Declarations
* @returns The styles grouped as strings
*/
export function getStylesFromArray(arr: string[]): { style: string; labelStyle: string } {
export function getStylesFromArray(arr: string[]): {
style: string;
labelStyle: string;
} {
let style = '';
let labelStyle = '';

View File

@@ -3,7 +3,7 @@ import * as configApi from '../config.js';
describe('getSubGraphTitleMargins', () => {
it('should get subgraph title margins after config has been set', () => {
const config_0 = {
const config0 = {
flowchart: {
subGraphTitleMargin: {
top: 10,
@@ -12,8 +12,8 @@ describe('getSubGraphTitleMargins', () => {
},
};
configApi.setSiteConfig(config_0);
expect(getSubGraphTitleMargins(config_0)).toEqual({
configApi.setSiteConfig(config0);
expect(getSubGraphTitleMargins(config0)).toEqual({
subGraphTitleTopMargin: 10,
subGraphTitleBottomMargin: 5,
subGraphTitleTotalMargin: 15,

View File

@@ -3,10 +3,7 @@
"version": "0.1.0-rc.1",
"description": "MermaidJS parser",
"author": "Yokozuna59",
"contributors": [
"Yokozuna59",
"Sidharth Vinod (https://sidharth.dev)"
],
"contributors": ["Yokozuna59", "Sidharth Vinod (https://sidharth.dev)"],
"homepage": "https://github.com/mermaid-js/mermaid/tree/develop/packages/mermaid/parser/#readme",
"types": "dist/src/index.d.ts",
"type": "module",
@@ -28,20 +25,14 @@
"directory": "packages/parser"
},
"license": "MIT",
"keywords": [
"mermaid",
"parser",
"ast"
],
"keywords": ["mermaid", "parser", "ast"],
"dependencies": {
"langium": "3.0.0"
},
"devDependencies": {
"chevrotain": "^11.0.3"
},
"files": [
"dist/"
],
"files": ["dist/"],
"publishConfig": {
"access": "public"
}

97
pnpm-lock.yaml generated
View File

@@ -14,6 +14,9 @@ importers:
'@argos-ci/cypress':
specifier: ^2.0.5
version: 2.0.5(cypress@13.7.3)
'@biomejs/biome':
specifier: 1.8.2
version: 1.8.2
'@cspell/eslint-plugin':
specifier: ^8.6.0
version: 8.7.0(eslint@8.57.0)
@@ -300,12 +303,6 @@ importers:
'@types/uuid':
specifier: ^9.0.8
version: 9.0.8
'@typescript-eslint/eslint-plugin':
specifier: ^7.3.1
version: 7.6.0(@typescript-eslint/parser@7.6.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
'@typescript-eslint/parser':
specifier: ^7.3.1
version: 7.6.0(eslint@8.57.0)(typescript@5.4.5)
ajv:
specifier: ^8.12.0
version: 8.12.0
@@ -1517,6 +1514,59 @@ packages:
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
'@biomejs/biome@1.8.2':
resolution: {integrity: sha512-XafCzLgs0xbH0bCjYKxQ63ig2V86fZQMq1jiy5pyLToWk9aHxA8GAUxyBtklPHtPYZPGEPOYglQHj4jyfUp+Iw==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/cli-darwin-arm64@1.8.2':
resolution: {integrity: sha512-l9msLsTcSIAPqMsPIhodQmb50sEfaXPLQ0YW4cdj6INmd8iaOh/V9NceQb2366vACTJgcWDQ2RzlvURek1T68g==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-x64@1.8.2':
resolution: {integrity: sha512-Fc4y/FuIxRSiB3TJ+y27vFDE/HJt4QgBuymktsIKEcBZvnKfsRjxvzVDunccRn4xbKgepnp+fn6BoS+ZIg/I3Q==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/cli-linux-arm64-musl@1.8.2':
resolution: {integrity: sha512-WpT41QJJvkZa1eZq0WmD513zkC6AYaMI39HJKmKeiUeX2NZirG+bxv1YRDhqkns1NbBqo3+qrJqBkPmOW+xAVA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-arm64@1.8.2':
resolution: {integrity: sha512-Q99qwP0qibkZxm2kfnt37OxeIlliDYf5ogi3zX9ij2DULzc+KtPA9Uj0wCljcJofOBsBYaHc7597Q+Bf/251ww==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-x64-musl@1.8.2':
resolution: {integrity: sha512-rk1Wj4d3LIlAlIAS1m2jlyfOjkNbuY1lfwKvWIAeZC51yDMzwhRD7cReE5PE+jqLDtq60PX38hDPeKd7nA1S6A==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-linux-x64@1.8.2':
resolution: {integrity: sha512-bjhhUVFchFid2gOjrvBe4fg8BShcpyFQTHuB/QQnfGxs1ddrGP30yq3fHfc6S6MoCcz9Tjd3Zzq1EfWfyy5iHA==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-win32-arm64@1.8.2':
resolution: {integrity: sha512-EUbqmCmNWT5xhnxHrCAEBzJB1AnLqxTYoRjlxiCMzGvsy5jQzhCanJ8CT9kNsApW3pfPWBWkoTa7qrwWmwnEGA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-x64@1.8.2':
resolution: {integrity: sha512-n9H5oRUCk1uNezMgyJh9+hZdtfD8PXLLeq8DUzTycIhl0I1BulIoZ/uxWgRVDFDwAR1JHu1AykISCRFNGnc4iA==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
'@braintree/sanitize-url@7.0.1':
resolution: {integrity: sha512-URg8UM6lfC9ZYqFipItRSxYJdgpU5d2Z4KnjsJ+rj6tgAmGme7E+PQNCiud8g0HDaZKMovu2qjfa0f5Ge0Vlsg==}
@@ -11061,6 +11111,41 @@ snapshots:
'@bcoe/v8-coverage@0.2.3': {}
'@biomejs/biome@1.8.2':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 1.8.2
'@biomejs/cli-darwin-x64': 1.8.2
'@biomejs/cli-linux-arm64': 1.8.2
'@biomejs/cli-linux-arm64-musl': 1.8.2
'@biomejs/cli-linux-x64': 1.8.2
'@biomejs/cli-linux-x64-musl': 1.8.2
'@biomejs/cli-win32-arm64': 1.8.2
'@biomejs/cli-win32-x64': 1.8.2
'@biomejs/cli-darwin-arm64@1.8.2':
optional: true
'@biomejs/cli-darwin-x64@1.8.2':
optional: true
'@biomejs/cli-linux-arm64-musl@1.8.2':
optional: true
'@biomejs/cli-linux-arm64@1.8.2':
optional: true
'@biomejs/cli-linux-x64-musl@1.8.2':
optional: true
'@biomejs/cli-linux-x64@1.8.2':
optional: true
'@biomejs/cli-win32-arm64@1.8.2':
optional: true
'@biomejs/cli-win32-x64@1.8.2':
optional: true
'@braintree/sanitize-url@7.0.1': {}
'@chevrotain/cst-dts-gen@11.0.3':

View File

@@ -1,4 +1,5 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base",
":rebaseStalePrs",
@@ -7,7 +8,8 @@
":automergeTesters",
":automergeLinters",
":automergeTypes",
":automergePatch"
":automergePatch",
"customManagers:biomeVersions"
],
"packageRules": [
{

View File

@@ -1,8 +1,6 @@
{
// extend your base config to share compilerOptions, etc
"extends": "./tsconfig.json",
"compilerOptions": {
// ensure that nobody can accidentally use this config for a build
"noEmit": true
},
"include": [