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 { mkdir, writeFile } from 'node:fs/promises';
import { packageOptions } from '../.build/common.js'; import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.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'); const shouldVisualize = process.argv.includes('--visualize');

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,6 @@
export default { export default {
'!(docs/**/*)*.{ts,js,html,md,mts}': [ '!(docs/**/*)*.{ts,js,html,md,mts}': [
'eslint --cache --cache-strategy content --fix', 'biome check --no-errors-on-unmatched --files-ignore-unknown=true --write',
// 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'], '.cspell/*.txt': ['tsx scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'], '**/*.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 { resolve } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import jisonPlugin from './jisonPlugin.js'; import jisonPlugin from './jisonPlugin.js';
@@ -48,7 +48,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
const external: (string | RegExp)[] = ['require', 'fs', 'path']; const external: (string | RegExp)[] = ['require', 'fs', 'path'];
console.log(entryName, packageOptions[entryName]); console.log(entryName, packageOptions[entryName]);
const { name, file, packageName } = packageOptions[entryName]; const { name, file, packageName } = packageOptions[entryName];
let output: OutputOptions = [ const output: OutputOptions = [
{ {
name, name,
format: 'esm', 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'; 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:vite": "tsx .vite/server.ts",
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite", "dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
"release": "pnpm build", "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": "pnpm biome check && pnpm lint:jison",
"lint:fix": "cross-env NODE_OPTIONS=--max_old_space_size=8192 eslint --cache --cache-strategy content --fix . && prettier --write . && tsx scripts/fixCSpell.ts", "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", "lint:jison": "tsx ./scripts/jison/lint.mts",
"contributors": "tsx scripts/updateContributors.ts", "contributors": "tsx scripts/updateContributors.ts",
"cypress": "cypress run", "cypress": "cypress run",
@@ -51,18 +52,13 @@
"author": "Knut Sveidqvist", "author": "Knut Sveidqvist",
"license": "MIT", "license": "MIT",
"standard": { "standard": {
"ignore": [ "ignore": ["**/parser/*.js", "dist/**/*.js", "cypress/**/*.js"],
"**/parser/*.js", "globals": ["page"]
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
}, },
"devDependencies": { "devDependencies": {
"@applitools/eyes-cypress": "^3.42.3", "@applitools/eyes-cypress": "^3.42.3",
"@argos-ci/cypress": "^2.0.5", "@argos-ci/cypress": "^2.0.5",
"@biomejs/biome": "1.8.2",
"@cspell/eslint-plugin": "^8.6.0", "@cspell/eslint-plugin": "^8.6.0",
"@cypress/code-coverage": "^3.12.30", "@cypress/code-coverage": "^3.12.30",
"@rollup/plugin-typescript": "^11.1.6", "@rollup/plugin-typescript": "^11.1.6",

View File

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

View File

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

View File

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

View File

@@ -447,16 +447,19 @@ export const addEdges = function (edges, diagObj, graph, svg) {
/* eslint-disable no-fallthrough */ /* eslint-disable no-fallthrough */
switch (edge.type) { switch (edge.type) {
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_cross': case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross'; edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross': case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross'; edgeData.arrowTypeEnd = 'arrow_cross';
break; break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_point': case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point'; edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point': case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point'; edgeData.arrowTypeEnd = 'arrow_point';
break; break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_circle': case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle'; edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle': case 'arrow_circle':

View File

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

View File

@@ -58,14 +58,8 @@
"author": "Knut Sveidqvist", "author": "Knut Sveidqvist",
"license": "MIT", "license": "MIT",
"standard": { "standard": {
"ignore": [ "ignore": ["**/parser/*.js", "dist/**/*.js", "cypress/**/*.js"],
"**/parser/*.js", "globals": ["page"]
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^7.0.1", "@braintree/sanitize-url": "^7.0.1",
@@ -103,8 +97,6 @@
"@types/prettier": "^3.0.0", "@types/prettier": "^3.0.0",
"@types/stylis": "^4.2.5", "@types/stylis": "^4.2.5",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"chokidar": "^3.6.0", "chokidar": "^3.6.0",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
@@ -132,10 +124,7 @@
"vitepress": "^1.0.1", "vitepress": "^1.0.1",
"vitepress-plugin-search": "1.0.4-alpha.22" "vitepress-plugin-search": "1.0.4-alpha.22"
}, },
"files": [ "files": ["dist/", "README.md"],
"dist/",
"README.md"
],
"sideEffects": false, "sideEffects": false,
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@@ -18,7 +18,7 @@ import { execFile } from 'node:child_process';
import { readFile, writeFile } from 'node:fs/promises'; import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path'; import { join } from 'node:path';
import { promisify } from 'node:util'; import { promisify } from 'node:util';
import prettier from 'prettier'; import { prettierConfig } from './prettier.js';
// Workaround for wrong AJV types, see // Workaround for wrong AJV types, see
// https://github.com/ajv-validator/ajv/issues/2132#issuecomment-1290409907 // 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` additionalProperties: false, // in JSON Schema 2019-09, these are called `unevaluatedProperties`
unreachableDefinitions: true, // definition for FontConfig is unreachable 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 // @ts-ignore No typescript declaration file
import flatmap from 'unist-util-flatmap'; import flatmap from 'unist-util-flatmap';
import { visit } from 'unist-util-visit'; import { visit } from 'unist-util-visit';
import { prettierConfig } from './prettier.js';
export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8')) export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8'))
.version as string; .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 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 // From https://github.com/vuejs/vitepress/blob/428eec3750d6b5648a77ac52d88128df0554d4d1/src/node/markdownToVue.ts#L20-L21
const includesRE = /<!--\s*@include:\s*(.*?)\s*-->/g; const includesRE = /<!--\s*@include:\s*(.*?)\s*-->/g;
const includedFiles: Set<string> = new Set(); const includedFiles: Set<string> = new Set();
@@ -367,26 +367,26 @@ async function transformJsonSchema(file: string) {
}); });
/** Location of the `schema.yaml` files */ /** 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 * Location to store the generated `schema.json` file for the website
* *
* Because Vitepress doesn't handle bundling `.json` files properly, we need * Because Vitepress doesn't handle bundling `.json` files properly, we need
* to instead place it into a `public/` subdirectory. * to instead place it into a `public/` subdirectory.
*/ */
const SCHEMA_OUTPUT_DIR = 'src/docs/public/schemas/'; const schemaOutputDir = 'src/docs/public/schemas/';
const VITEPRESS_PUBLIC_DIR = 'src/docs/public'; const vitepressPublicDir = 'src/docs/public';
/** /**
* Location to store the generated Schema Markdown docs. * Location to store the generated Schema Markdown docs.
* Links to JSON Schemas should automatically be rewritten to point to * Links to JSON Schemas should automatically be rewritten to point to
* `SCHEMA_OUTPUT_DIR`. * `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 // write .schema.json files
const jsonFileName = file const jsonFileName = file
.replace('.schema.yaml', '.schema.json') .replace('.schema.yaml', '.schema.json')
.replace(SCHEMA_INPUT_DIR, SCHEMA_OUTPUT_DIR); .replace(schemaInputDir, schemaOutputDir);
copyTransformedContents(jsonFileName, !verifyOnly, JSON.stringify(jsonSchema, undefined, 2)); copyTransformedContents(jsonFileName, !verifyOnly, JSON.stringify(jsonSchema, undefined, 2));
const schemas = traverseSchemas([schemaLoader()(jsonFileName, jsonSchema)]); const schemas = traverseSchemas([schemaLoader()(jsonFileName, jsonSchema)]);
@@ -414,7 +414,7 @@ async function transformJsonSchema(file: string) {
* @returns New absolute Vitepress path to schema * @returns New absolute Vitepress path to schema
*/ */
rewritelinks: (origin: string) => { rewritelinks: (origin: string) => {
return `/${relative(VITEPRESS_PUBLIC_DIR, origin)}`; return `/${relative(vitepressPublicDir, origin)}`;
}, },
})(schemas); })(schemas);
@@ -458,7 +458,7 @@ async function transformJsonSchema(file: string) {
parser: 'markdown', parser: 'markdown',
...prettierConfig, ...prettierConfig,
}); });
const newFileName = join(SCHEMA_MARKDOWN_OUTPUT_DIR, `${name}.md`); const newFileName = join(schemaMarkdownOutputDir, `${name}.md`);
copyTransformedContents(newFileName, !verifyOnly, formatted); copyTransformedContents(newFileName, !verifyOnly, formatted);
} }
} }

View File

@@ -24,11 +24,11 @@ describe('docs.mts', () => {
describe('is a code block', () => { describe('is a code block', () => {
const beforeCodeLine = 'test\n'; const beforeCodeLine = 'test\n';
const diagram_text = 'graph\n A --> B\n'; const diagramText = 'graph\n A --> B\n';
describe('language = "mermaid-nocode"', () => { describe('language = "mermaid-nocode"', () => {
const lang_keyword = 'mermaid-nocode'; const langKeyword = 'mermaid-nocode';
const contents = beforeCodeLine + '```' + lang_keyword + '\n' + diagram_text + '\n```\n'; const contents = beforeCodeLine + '```' + langKeyword + '\n' + diagramText + '\n```\n';
it('changes the language to "mermaid"', async () => { it('changes the language to "mermaid"', async () => {
const result = ( const result = (
@@ -37,17 +37,16 @@ describe('docs.mts', () => {
.process(contents) .process(contents)
).toString(); ).toString();
expect(result).toEqual( expect(result).toEqual(
beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagram_text + '\n```\n' beforeCodeLine + '\n' + '```' + 'mermaid' + '\n' + diagramText + '\n```\n'
); );
}); });
}); });
describe('language = "mermaid" | "mmd" | "mermaid-example"', () => { describe('language = "mermaid" | "mmd" | "mermaid-example"', () => {
const mermaid_keywords = ['mermaid', 'mmd', 'mermaid-example']; const mermaidKeywords = ['mermaid', 'mmd', 'mermaid-example'];
mermaid_keywords.forEach((lang_keyword) => { mermaidKeywords.forEach((langKeyword) => {
const contents = const contents = beforeCodeLine + '```' + langKeyword + '\n' + diagramText + '\n```\n';
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 () => { it('changes the language to "mermaid-example" and adds a copy of the code block with language = "mermaid"', async () => {
const result = ( const result = (
@@ -59,10 +58,10 @@ describe('docs.mts', () => {
beforeCodeLine + beforeCodeLine +
'\n' + '\n' +
'```mermaid-example\n' + '```mermaid-example\n' +
diagram_text + diagramText +
'\n```\n' + '\n```\n' +
'\n```mermaid\n' + '\n```mermaid\n' +
diagram_text + diagramText +
'\n```\n' '\n```\n'
); );
}); });
@@ -70,9 +69,9 @@ describe('docs.mts', () => {
}); });
it('calls transformToBlockQuote with the node information', async () => { it('calls transformToBlockQuote with the node information', async () => {
const lang_keyword = 'note'; const langKeyword = 'note';
const contents = const contents =
beforeCodeLine + '```' + lang_keyword + '\n' + 'This is the text\n' + '```\n'; beforeCodeLine + '```' + langKeyword + '\n' + 'This is the text\n' + '```\n';
const result = ( const result = (
await remarkBuilder().use(transformMarkdownAst, { originalFilename }).process(contents) 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({}); configApi.setSiteConfig({});
}); });
it('should set site config and config properly', () => { it('should set site config and config properly', () => {
const config_0 = { fontFamily: 'foo-font', fontSize: 150 }; const config0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0); configApi.setSiteConfig(config0);
const config_1 = configApi.getSiteConfig(); const config1 = configApi.getSiteConfig();
const config_2 = configApi.getConfig(); const config2 = configApi.getConfig();
expect(config_1.fontFamily).toEqual(config_0.fontFamily); expect(config1.fontFamily).toEqual(config0.fontFamily);
expect(config_1.fontSize).toEqual(config_0.fontSize); expect(config1.fontSize).toEqual(config0.fontSize);
expect(config_1).toEqual(config_2); expect(config1).toEqual(config2);
}); });
it('should respect secure keys when applying directives', () => { it('should respect secure keys when applying directives', () => {
const config_0: MermaidConfig = { const config0: MermaidConfig = {
fontFamily: 'foo-font', fontFamily: 'foo-font',
securityLevel: 'strict', // can't be changed securityLevel: 'strict', // can't be changed
fontSize: 12345, // can't be changed fontSize: 12345, // can't be changed
secure: [...configApi.defaultConfig.secure!, 'fontSize'], secure: [...configApi.defaultConfig.secure!, 'fontSize'],
}; };
configApi.setSiteConfig(config_0); configApi.setSiteConfig(config0);
const directive: MermaidConfig = { const directive: MermaidConfig = {
fontFamily: 'baf', fontFamily: 'baf',
// fontSize and securityLevel shouldn't be changed // fontSize and securityLevel shouldn't be changed
fontSize: 54321, fontSize: 54321,
securityLevel: 'loose', 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.fontFamily).toEqual(directive.fontFamily);
expect(cfg.fontSize).toBe(config_0.fontSize); expect(cfg.fontSize).toBe(config0.fontSize);
expect(cfg.securityLevel).toBe(config_0.securityLevel); expect(cfg.securityLevel).toBe(config0.securityLevel);
}); });
it('should allow setting partial options', () => { it('should allow setting partial options', () => {
const defaultConfig = configApi.getConfig(); const defaultConfig = configApi.getConfig();
@@ -52,30 +52,30 @@ describe('when working with site config', () => {
); );
}); });
it('should set reset config properly', () => { it('should set reset config properly', () => {
const config_0 = { fontFamily: 'foo-font', fontSize: 150 }; const config0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0); configApi.setSiteConfig(config0);
const config_1 = { fontFamily: 'baf' }; const config1 = { fontFamily: 'baf' };
configApi.setConfig(config_1); configApi.setConfig(config1);
const config_2 = configApi.getConfig(); const config2 = configApi.getConfig();
expect(config_2.fontFamily).toEqual(config_1.fontFamily); expect(config2.fontFamily).toEqual(config1.fontFamily);
configApi.reset(); configApi.reset();
const config_3 = configApi.getConfig(); const config3 = configApi.getConfig();
expect(config_3.fontFamily).toEqual(config_0.fontFamily); expect(config3.fontFamily).toEqual(config0.fontFamily);
const config_4 = configApi.getSiteConfig(); const config4 = configApi.getSiteConfig();
expect(config_4.fontFamily).toEqual(config_0.fontFamily); expect(config4.fontFamily).toEqual(config0.fontFamily);
}); });
it('should set global reset config properly', () => { it('should set global reset config properly', () => {
const config_0 = { fontFamily: 'foo-font', fontSize: 150 }; const config0 = { fontFamily: 'foo-font', fontSize: 150 };
configApi.setSiteConfig(config_0); configApi.setSiteConfig(config0);
const config_1 = configApi.getSiteConfig(); const config1 = configApi.getSiteConfig();
expect(config_1.fontFamily).toEqual(config_0.fontFamily); expect(config1.fontFamily).toEqual(config0.fontFamily);
const config_2 = configApi.getConfig(); const config2 = configApi.getConfig();
expect(config_2.fontFamily).toEqual(config_0.fontFamily); expect(config2.fontFamily).toEqual(config0.fontFamily);
configApi.setConfig({ altFontFamily: 'bar-font' }); configApi.setConfig({ altFontFamily: 'bar-font' });
const config_3 = configApi.getConfig(); const config3 = configApi.getConfig();
expect(config_3.altFontFamily).toEqual('bar-font'); expect(config3.altFontFamily).toEqual('bar-font');
configApi.reset(); configApi.reset();
const config_4 = configApi.getConfig(); const config4 = configApi.getConfig();
expect(config_4.altFontFamily).toBeUndefined(); expect(config4.altFontFamily).toBeUndefined();
}); });
}); });

View File

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

View File

@@ -119,7 +119,7 @@ const hexagon = async (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const block_arrow = async (parent, node) => { const blockArrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const f = 2; const f = 2;
@@ -140,7 +140,7 @@ const block_arrow = async (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect_left_inv_arrow = async (parent, node) => { const rectLeftInvArrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper( const { shapeSvg, bbox } = await labelHelper(
parent, parent,
node, node,
@@ -171,7 +171,7 @@ const rect_left_inv_arrow = async (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const lean_right = async (parent, node) => { const leanRight = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper(parent, node, getClassesFromNode(node), true); const { shapeSvg, bbox } = await labelHelper(parent, node, getClassesFromNode(node), true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
@@ -194,7 +194,7 @@ const lean_right = async (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const lean_left = async (parent, node) => { const leanLeft = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper( const { shapeSvg, bbox } = await labelHelper(
parent, parent,
node, node,
@@ -250,7 +250,7 @@ const trapezoid = async (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const inv_trapezoid = async (parent, node) => { const invTrapezoid = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper( const { shapeSvg, bbox } = await labelHelper(
parent, parent,
node, node,
@@ -278,7 +278,7 @@ const inv_trapezoid = async (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect_right_inv_arrow = async (parent, node) => { const rectRightInvArrow = async (parent, node) => {
const { shapeSvg, bbox } = await labelHelper( const { shapeSvg, bbox } = await labelHelper(
parent, parent,
node, node,
@@ -875,7 +875,7 @@ const end = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const class_box = (parent, node) => { const classBox = (parent, node) => {
const halfPadding = node.padding / 2; const halfPadding = node.padding / 2;
const rowPadding = 4; const rowPadding = 4;
const lineHeight = 8; const lineHeight = 8;
@@ -1111,13 +1111,13 @@ const shapes = {
doublecircle, doublecircle,
stadium, stadium,
hexagon, hexagon,
block_arrow, block_arrow: blockArrow,
rect_left_inv_arrow, rect_left_inv_arrow: rectLeftInvArrow,
lean_right, lean_right: leanRight,
lean_left, lean_left: leanLeft,
trapezoid, trapezoid,
inv_trapezoid, inv_trapezoid: invTrapezoid,
rect_right_inv_arrow, rect_right_inv_arrow: rectRightInvArrow,
cylinder, cylinder,
start, start,
end, end,
@@ -1125,7 +1125,7 @@ const shapes = {
subroutine, subroutine,
fork: forkJoin, fork: forkJoin,
join: forkJoin, join: forkJoin,
class_box, class_box: classBox,
}; };
let nodeElems = {}; 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'] 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 await expect(
.toThrowErrorMatchingInlineSnapshot(` Diagram.fromText('sequenceDiagram; A-->B')
).rejects.toThrowErrorMatchingInlineSnapshot(`
[Error: Parse error on line 1: [Error: Parse error on line 1:
...quenceDiagram; A-->B ...quenceDiagram; A-->B
-----------------------^ -----------------------^

View File

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

View File

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

View File

@@ -53,7 +53,7 @@ function hexagon(parent, bbox, node) {
* @param bbox * @param bbox
* @param node * @param node
*/ */
function rect_left_inv_arrow(parent, bbox, node) { function rectLeftInvArrow(parent, bbox, node) {
const w = bbox.width; const w = bbox.width;
const h = bbox.height; const h = bbox.height;
const points = [ const points = [
@@ -75,7 +75,7 @@ function rect_left_inv_arrow(parent, bbox, node) {
* @param bbox * @param bbox
* @param node * @param node
*/ */
function lean_right(parent, bbox, node) { function leanRight(parent, bbox, node) {
const w = bbox.width; const w = bbox.width;
const h = bbox.height; const h = bbox.height;
const points = [ const points = [
@@ -96,7 +96,7 @@ function lean_right(parent, bbox, node) {
* @param bbox * @param bbox
* @param node * @param node
*/ */
function lean_left(parent, bbox, node) { function leanLeft(parent, bbox, node) {
const w = bbox.width; const w = bbox.width;
const h = bbox.height; const h = bbox.height;
const points = [ const points = [
@@ -138,7 +138,7 @@ function trapezoid(parent, bbox, node) {
* @param bbox * @param bbox
* @param node * @param node
*/ */
function inv_trapezoid(parent, bbox, node) { function invTrapezoid(parent, bbox, node) {
const w = bbox.width; const w = bbox.width;
const h = bbox.height; const h = bbox.height;
const points = [ const points = [
@@ -159,7 +159,7 @@ function inv_trapezoid(parent, bbox, node) {
* @param bbox * @param bbox
* @param node * @param node
*/ */
function rect_right_inv_arrow(parent, bbox, node) { function rectRightInvArrow(parent, bbox, node) {
const w = bbox.width; const w = bbox.width;
const h = bbox.height; const h = bbox.height;
const points = [ const points = [
@@ -308,22 +308,22 @@ export function addToRender(render) {
render.shapes().cylinder = cylinder; render.shapes().cylinder = cylinder;
// Add custom shape for box with inverted arrow on left side // 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 // 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 // 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 // Add custom shape for box with inverted arrow on left side
render.shapes().trapezoid = trapezoid; render.shapes().trapezoid = trapezoid;
// Add custom shape for box with inverted arrow on left side // 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 // 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 */ /** @param addShape */
@@ -335,22 +335,22 @@ export function addToRenderV2(addShape) {
addShape({ cylinder }); addShape({ cylinder });
// Add custom shape for box with inverted arrow on left side // 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 // 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 // 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 // Add custom shape for box with inverted arrow on left side
addShape({ trapezoid }); addShape({ trapezoid });
// Add custom shape for box with inverted arrow on left side // 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 // 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 */ /* eslint-disable no-fallthrough */
switch (edge.type) { switch (edge.type) {
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_cross': case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross'; edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross': case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross'; edgeData.arrowTypeEnd = 'arrow_cross';
break; break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_point': case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point'; edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point': case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point'; edgeData.arrowTypeEnd = 'arrow_point';
break; break;
// biome-ignore lint/suspicious/noFallthroughSwitchClause: <explanation>
case 'double_arrow_circle': case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle'; edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle': case 'arrow_circle':
@@ -425,9 +428,9 @@ export const draw = async function (text, id, _version, diagObj) {
selectAll('cluster').append('text'); selectAll('cluster').append('text');
for (let j = 0; j < subG.nodes.length; j++) { for (const node of subG.nodes) {
log.info('Setting up subgraphs', subG.nodes[j], subG.id); log.info('Setting up subgraphs', node, subG.id);
g.setParent(subG.nodes[j], subG.id); g.setParent(node, subG.id);
} }
} }
await addVertices(vert, g, id, root, doc, diagObj); 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'); selectAll('cluster').append('text');
for (let j = 0; j < subG.nodes.length; j++) { for (const subGNode of subG.nodes) {
log.warn( log.warn(
'Setting subgraph', 'Setting subgraph',
subG.nodes[j], subGNode,
diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subGNode),
diagObj.db.lookUpDomId(subG.id) 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); 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('transform', `translate(${xPos + _width / 2}, ${yPos + 14})`);
te.attr('id', id + 'Text'); te.attr('id', id + 'Text');
for (let j = 0; j < subG.classes.length; j++) { for (const subGClass of subG.classes) {
clusterEl[0].classList.add(subG.classes[j]); 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 () { it('should throw error for escaping quotes in text state', function () {
//prettier-ignore //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'"); 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('x', 10)
.attr('y', function (d, i) { .attr('y', function (d, i) {
if (i > 0) { if (i > 0) {
// biome-ignore lint/correctness/noUnreachable: <explanation>
for (let j = 0; j < i; j++) { for (let j = 0; j < i; j++) {
prevGap += numOccurrences[i - 1][1]; prevGap += numOccurrences[i - 1][1];
return (d[1] * theGap) / 2 + prevGap * theGap + theTopPad; 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} ${'crit'} | ${false} | ${false} | ${true} | ${false}
${'active'} | ${false} | ${false} | ${false} | ${true} ${'active'} | ${false} | ${false} | ${false} | ${true}
${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false} ${'crit,milestone,done'} | ${true} | ${true} | ${true} | ${false}
`)('should handle a task with tags $tags', ({ tags, milestone, done, crit, active }) => { `)(
const str = 'should handle a task with tags $tags',
'gantt\n' + ({
'dateFormat YYYY-MM-DD\n' + tags,
'title Adding gantt diagram functionality to mermaid\n' + // Do not remove, these are used in eval.
'section Documentation\n' + milestone,
'test task:' + done,
tags + crit,
', 2014-01-01, 2014-01-04'; 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) { allowedTags.forEach(function (t) {
if (eval(t)) { if (eval(t)) {
expect(tasks[0][t]).toBeTruthy(); expect(tasks[0][t]).toBeTruthy();
} else { } else {
expect(tasks[0][t]).toBeFalsy(); expect(tasks[0][t]).toBeFalsy();
} }
}); });
}); }
);
it('should parse callback specifier with no args', function () { it('should parse callback specifier with no args', function () {
spyOn(ganttDb, 'setClickEvent'); spyOn(ganttDb, 'setClickEvent');
const str = const str =
@@ -207,7 +217,6 @@ describe('when parsing a gantt diagram it', function () {
'click cl2 call ganttTestClick("test0", test1, test2)\n'; 'click cl2 call ganttTestClick("test0", test1, test2)\n';
expect(parserFnConstructor(str)).not.toThrow(); expect(parserFnConstructor(str)).not.toThrow();
const args = '"test1", "test2", "test3"';
expect(ganttDb.setClickEvent).toHaveBeenCalledWith( expect(ganttDb.setClickEvent).toHaveBeenCalledWith(
'cl2', 'cl2',
'ganttTestClick', '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()); 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 currentCommit = commits.get(branches.get(curBranch));
const otherCommit = commits.get(branches.get(otherBranch)); 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'], expected: ['branch abc'],
}; };
throw error; throw error;
} else if (custom_id && commits.has(custom_id)) { } else if (customId && commits.has(customId)) {
let error = new Error( let error = new Error(
'Incorrect usage of "merge". Commit with id:' + 'Incorrect usage of "merge". Commit with id:' +
custom_id + customId +
' already exists, use different custom Id' ' already exists, use different custom Id'
); );
error.hash = { error.hash = {
text: 'merge ' + otherBranch + custom_id + override_type + custom_tag, text: 'merge ' + otherBranch + customId + overrideType + customTag,
token: 'merge ' + otherBranch + custom_id + override_type + custom_tag, token: 'merge ' + otherBranch + customId + overrideType + customTag,
line: '1', line: '1',
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
expected: [ 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 { // } else {
// create merge commit // create merge commit
const commit = { const commit = {
id: custom_id ? custom_id : seq + '-' + getId(), id: customId ? customId : seq + '-' + getId(),
message: 'merged branch ' + otherBranch + ' into ' + curBranch, message: 'merged branch ' + otherBranch + ' into ' + curBranch,
seq: seq++, seq: seq++,
parents: [head == null ? null : head.id, branches.get(otherBranch)], parents: [head == null ? null : head.id, branches.get(otherBranch)],
branch: curBranch, branch: curBranch,
type: commitType.MERGE, type: commitType.MERGE,
customType: override_type, customType: overrideType,
customId: custom_id ? true : false, customId: customId ? true : false,
tag: custom_tag ? custom_tag : '', tag: customTag ? customTag : '',
}; };
head = commit; head = commit;
commits.set(commit.id, commit); commits.set(commit.id, commit);

View File

@@ -405,11 +405,11 @@ const drawCommits = (svg, commits, modifyGraph) => {
text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')'); text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')'); labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
} else { } else {
let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5; let rX = -7.5 - ((bbox.width + 10) / 25) * 9.5;
let r_y = 10 + (bbox.width / 25) * 8.5; let rY = 10 + (bbox.width / 25) * 8.5;
wrapper.attr( wrapper.attr(
'transform', '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 db = diagObj.db as PieDB;
const globalConfig: MermaidConfig = getConfig(); const globalConfig: MermaidConfig = getConfig();
const pieConfig: Required<PieDiagramConfig> = cleanAndMerge(db.getConfig(), globalConfig.pie); const pieConfig: Required<PieDiagramConfig> = cleanAndMerge(db.getConfig(), globalConfig.pie);
const MARGIN = 40; const margin = 40;
const LEGEND_RECT_SIZE = 18; const legendRectSize = 18;
const LEGEND_SPACING = 4; const legendSpacing = 4;
const height = 450; const height = 450;
const pieWidth: number = height; const pieWidth: number = height;
const svg: SVG = selectSvgElement(id); const svg: SVG = selectSvgElement(id);
@@ -54,7 +54,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
outerStrokeWidth ??= 2; outerStrokeWidth ??= 2;
const textPosition: number = pieConfig.textPosition; 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: // Shape helper to build arcs:
const arcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Section>> = arc<d3.PieArcDatum<D3Section>>() const arcGenerator: d3.Arc<unknown, d3.PieArcDatum<D3Section>> = arc<d3.PieArcDatum<D3Section>>()
.innerRadius(0) .innerRadius(0)
@@ -139,25 +139,25 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.append('g') .append('g')
.attr('class', 'legend') .attr('class', 'legend')
.attr('transform', (_datum, index: number): string => { .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 offset = (height * color.domain().length) / 2;
const horizontal = 12 * LEGEND_RECT_SIZE; const horizontal = 12 * legendRectSize;
const vertical = index * height - offset; const vertical = index * height - offset;
return 'translate(' + horizontal + ',' + vertical + ')'; return 'translate(' + horizontal + ',' + vertical + ')';
}); });
legend legend
.append('rect') .append('rect')
.attr('width', LEGEND_RECT_SIZE) .attr('width', legendRectSize)
.attr('height', LEGEND_RECT_SIZE) .attr('height', legendRectSize)
.style('fill', color) .style('fill', color)
.style('stroke', color); .style('stroke', color);
legend legend
.data(arcs) .data(arcs)
.append('text') .append('text')
.attr('x', LEGEND_RECT_SIZE + LEGEND_SPACING) .attr('x', legendRectSize + legendSpacing)
.attr('y', LEGEND_RECT_SIZE - LEGEND_SPACING) .attr('y', legendRectSize - legendSpacing)
.text((datum: d3.PieArcDatum<D3Section>): string => { .text((datum: d3.PieArcDatum<D3Section>): string => {
const { label, value } = datum.data; const { label, value } = datum.data;
if (db.getShowData()) { if (db.getShowData()) {
@@ -173,7 +173,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.map((node) => (node as Element)?.getBoundingClientRect().width ?? 0) .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 // Set viewBox
svg.attr('viewBox', `0 0 ${totalWidth} ${height}`); svg.attr('viewBox', `0 0 ${totalWidth} ${height}`);

View File

@@ -45,13 +45,13 @@ class SankeyNode {
constructor(public ID: string) {} constructor(public ID: string) {}
} }
const findOrCreateNode = (ID: string): SankeyNode => { const findOrCreateNode = (id: string): SankeyNode => {
ID = common.sanitizeText(ID, getConfig()); id = common.sanitizeText(id, getConfig());
let node = nodesMap.get(ID); let node = nodesMap.get(id);
if (node === undefined) { if (node === undefined) {
node = new SankeyNode(ID); node = new SankeyNode(id);
nodesMap.set(ID, node); nodesMap.set(id, node);
nodes.push(node); nodes.push(node);
} }
return 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('height', rectData.height);
rectElem.attr('rx', rectData.rx); rectElem.attr('rx', rectData.rx);
rectElem.attr('ry', rectData.ry); rectElem.attr('ry', rectData.ry);
let linkY = 20;
if (links != null) { if (links != null) {
var linkY = 20;
for (let key in links) { for (let key in links) {
var linkElem = g.append('a'); const linkElem = g.append('a');
var sanitizedLink = sanitizeUrl(links[key]); const sanitizedLink = sanitizeUrl(links[key]);
linkElem.attr('xlink:href', sanitizedLink); linkElem.attr('xlink:href', sanitizedLink);
linkElem.attr('target', '_blank'); 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[0]).toEqual('background:#bbb');
expect(classes.get('exampleStyleClass').styles[1]).toEqual('border:1px solid red'); expect(classes.get('exampleStyleClass').styles[1]).toEqual('border:1px solid red');
const state_a = stateDb.getState('a'); const stateA = stateDb.getState('a');
expect(state_a.classes.length).toEqual(1); expect(stateA.classes.length).toEqual(1);
expect(state_a.classes[0]).toEqual('exampleStyleClass'); expect(stateA.classes[0]).toEqual('exampleStyleClass');
}); });
it('can be applied to a state with an id containing _', function () { 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[0]).toBe('background:#bbb');
expect(classes.get('exampleStyleClass').styles[1]).toBe('border:1px solid red'); expect(classes.get('exampleStyleClass').styles[1]).toBe('border:1px solid red');
const state_a_a = stateDiagram.parser.yy.getState('a_a'); const stateAA = stateDiagram.parser.yy.getState('a_a');
expect(state_a_a.classes.length).toEqual(1); expect(stateAA.classes.length).toEqual(1);
expect(state_a_a.classes[0]).toEqual('exampleStyleClass'); expect(stateAA.classes[0]).toEqual('exampleStyleClass');
}); });
describe('::: syntax', () => { describe('::: syntax', () => {

View File

@@ -130,12 +130,12 @@ describe('state diagram V2, ', function () {
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2()); stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
const rels = stateDb.getRelations(); const rels = stateDb.getRelations();
const rel_1_2 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State2'); const rel12 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State2');
expect(rel_1_2.relationTitle).toEqual('Transition 1'); expect(rel12.relationTitle).toEqual('Transition 1');
const rel_1_3 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State3'); const rel13 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State3');
expect(rel_1_3.relationTitle).toEqual('Transition 2'); expect(rel13.relationTitle).toEqual('Transition 2');
const rel_1_4 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State4'); const rel14 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State4');
expect(rel_1_4.relationTitle).toEqual('Transition 3'); expect(rel14.relationTitle).toEqual('Transition 3');
}); });
}); });
@@ -408,10 +408,10 @@ describe('state diagram V2, ', function () {
expect(states.get('Active').doc[0].id).toEqual('Idle'); expect(states.get('Active').doc[0].id).toEqual('Idle');
const rels = stateDb.getRelations(); const rels = stateDb.getRelations();
const rel_Inactive_Idle = rels.find((rel) => rel.id1 === 'Inactive' && rel.id2 === 'Idle'); const relInactiveIdle = rels.find((rel) => rel.id1 === 'Inactive' && rel.id2 === 'Idle');
expect(rel_Inactive_Idle.relationTitle).toEqual('ACT'); expect(relInactiveIdle.relationTitle).toEqual('ACT');
const rel_Active_Active = rels.find((rel) => rel.id1 === 'Active' && rel.id2 === 'Active'); const relActiveActive = rels.find((rel) => rel.id1 === 'Active' && rel.id2 === 'Active');
expect(rel_Active_Active.relationTitle).toEqual('LOG'); 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) => { const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
let dir = defaultDir; let dir = defaultDir;
if (parsedItem.doc) { if (parsedItem.doc) {
for (let i = 0; i < parsedItem.doc.length; i++) { for (const parsedItemDoc of parsedItem.doc) {
const parsedItemDoc = parsedItem.doc[i];
if (parsedItemDoc.stmt === 'dir') { if (parsedItemDoc.stmt === 'dir') {
dir = parsedItemDoc.value; dir = parsedItemDoc.value;
} }

View File

@@ -15,9 +15,7 @@ const genSections = (options) => {
for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) { for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) {
const sw = '' + (17 - 3 * i); const sw = '' + (17 - 3 * i);
sections += ` sections += `
.section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${ .section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${i - 1} path {
i - 1
} path {
fill: ${options['cScale' + i]}; fill: ${options['cScale' + i]};
} }
.section-${i - 1} text { .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 //1. Fetch the configuration
const conf = getConfig(); const conf = getConfig();
// @ts-expect-error - wrong config? // @ts-expect-error - wrong config?
const LEFT_MARGIN = conf.leftMargin ?? 50; const leftMargin = conf.leftMargin ?? 50;
log.debug('timeline', diagObj.db); log.debug('timeline', diagObj.db);
@@ -68,7 +68,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
//let sectionBeginX = 0; //let sectionBeginX = 0;
let depthY = 0; let depthY = 0;
let sectionBeginY = 0; let sectionBeginY = 0;
let masterX = 50 + LEFT_MARGIN; let masterX = 50 + leftMargin;
//sectionBeginX = masterX; //sectionBeginX = masterX;
let masterY = 50; let masterY = 50;
sectionBeginY = 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); maxEventCount = Math.max(maxEventCount, task.events.length);
//calculate maxEventLineLength //calculate maxEventLineLength
let maxEventLineLengthTemp = 0; let maxEventLineLengthTemp = 0;
for (let j = 0; j < task.events.length; j++) { for (const event of task.events) {
const event = task.events[j];
const eventNode = { const eventNode = {
descr: event, descr: event,
section: task.section, section: task.section,
@@ -203,7 +202,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
svg svg
.append('text') .append('text')
.text(title) .text(title)
.attr('x', box.width / 2 - LEFT_MARGIN) .attr('x', box.width / 2 - leftMargin)
.attr('font-size', '4ex') .attr('font-size', '4ex')
.attr('font-weight', 'bold') .attr('font-weight', 'bold')
.attr('y', 20); .attr('y', 20);
@@ -215,9 +214,9 @@ export const draw = function (text: string, id: string, version: string, diagObj
// Draw activity line // Draw activity line
lineWrapper lineWrapper
.append('line') .append('line')
.attr('x1', LEFT_MARGIN) .attr('x1', leftMargin)
.attr('y1', depthY) // One section head + one task + margins .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('y2', depthY)
.attr('stroke-width', 4) .attr('stroke-width', 4)
.attr('stroke', 'black') .attr('stroke', 'black')

View File

@@ -75,8 +75,8 @@ function textSanitizer(text: string) {
return sanitizeText(text.trim(), config); return sanitizeText(text.trim(), config);
} }
function setTmpSVGG(SVGG: Group) { function setTmpSVGG(svgg: Group) {
tmpSVGGroup = SVGG; tmpSVGGroup = svgg;
} }
function setOrientation(orientation: string) { function setOrientation(orientation: string) {
if (orientation === 'horizontal') { 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 { version } from '../../../package.json';
import MermaidExample from './mermaid-markdown-all.js'; import MermaidExample from './mermaid-markdown-all.js';

View File

@@ -1,5 +1,5 @@
import contributorUsernamesJson from './contributor-names.json'; 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; const contributorUsernames: string[] = contributorUsernamesJson;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

97
pnpm-lock.yaml generated
View File

@@ -14,6 +14,9 @@ importers:
'@argos-ci/cypress': '@argos-ci/cypress':
specifier: ^2.0.5 specifier: ^2.0.5
version: 2.0.5(cypress@13.7.3) version: 2.0.5(cypress@13.7.3)
'@biomejs/biome':
specifier: 1.8.2
version: 1.8.2
'@cspell/eslint-plugin': '@cspell/eslint-plugin':
specifier: ^8.6.0 specifier: ^8.6.0
version: 8.7.0(eslint@8.57.0) version: 8.7.0(eslint@8.57.0)
@@ -300,12 +303,6 @@ importers:
'@types/uuid': '@types/uuid':
specifier: ^9.0.8 specifier: ^9.0.8
version: 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: ajv:
specifier: ^8.12.0 specifier: ^8.12.0
version: 8.12.0 version: 8.12.0
@@ -1517,6 +1514,59 @@ packages:
'@bcoe/v8-coverage@0.2.3': '@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} 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': '@braintree/sanitize-url@7.0.1':
resolution: {integrity: sha512-URg8UM6lfC9ZYqFipItRSxYJdgpU5d2Z4KnjsJ+rj6tgAmGme7E+PQNCiud8g0HDaZKMovu2qjfa0f5Ge0Vlsg==} resolution: {integrity: sha512-URg8UM6lfC9ZYqFipItRSxYJdgpU5d2Z4KnjsJ+rj6tgAmGme7E+PQNCiud8g0HDaZKMovu2qjfa0f5Ge0Vlsg==}
@@ -11061,6 +11111,41 @@ snapshots:
'@bcoe/v8-coverage@0.2.3': {} '@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': {} '@braintree/sanitize-url@7.0.1': {}
'@chevrotain/cst-dts-gen@11.0.3': '@chevrotain/cst-dts-gen@11.0.3':

View File

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

View File

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