diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt index 467e48891..0efd1dcc0 100644 --- a/.cspell/misc-terms.txt +++ b/.cspell/misc-terms.txt @@ -1 +1,4 @@ +BRANDES +handdrawn +KOEPF newbranch diff --git a/.esbuild/build.ts b/.esbuild/build.ts index 310c92c7d..505c18405 100644 --- a/.esbuild/build.ts +++ b/.esbuild/build.ts @@ -56,7 +56,7 @@ const handler = (e) => { const main = async () => { await generateLangium(); - await mkdir('stats').catch(() => {}); + await mkdir('stats', { recursive: true }); const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[]; // it should build `parser` before `mermaid` because it's a dependency for (const pkg of packageNames) { diff --git a/.esbuild/server.ts b/.esbuild/server.ts index 54c8f88df..ef61ebec2 100644 --- a/.esbuild/server.ts +++ b/.esbuild/server.ts @@ -95,8 +95,7 @@ async function createServer() { return; } console.log(`${path} changed. Rebuilding...`); - - if (/\.langium$/.test(path)) { + if (path.endsWith('.langium')) { await generateLangium(); } handleFileChange(); diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index 17bebeaef..3ffba697a 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -35,7 +35,7 @@ export const mermaidUrl = ( }; const objStr: string = JSON.stringify(codeObject); let url = `http://localhost:9000/e2e.html?graph=${utf8ToB64(objStr)}`; - if (api) { + if (api && typeof graphStr === 'string') { url = `http://localhost:9000/xss.html?graph=${graphStr}`; } @@ -54,16 +54,15 @@ export const imgSnapshotTest = ( ): void => { const options: CypressMermaidConfig = { ..._options, - fontFamily: _options.fontFamily || 'courier', + fontFamily: _options.fontFamily ?? 'courier', // @ts-ignore TODO: Fix type of fontSize - fontSize: _options.fontSize || '16px', + fontSize: _options.fontSize ?? '16px', sequence: { - ...(_options.sequence || {}), + ...(_options.sequence ?? {}), actorFontFamily: 'courier', - noteFontFamily: - _options.sequence && _options.sequence.noteFontFamily - ? _options.sequence.noteFontFamily - : 'courier', + noteFontFamily: _options.sequence?.noteFontFamily + ? _options.sequence.noteFontFamily + : 'courier', messageFontFamily: 'courier', }, }; @@ -95,7 +94,7 @@ export const openURLAndVerifyRendering = ( options: CypressMermaidConfig, validation?: any ): void => { - const name: string = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); + const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); cy.visit(url); cy.window().should('have.property', 'rendered', true); diff --git a/cypress/platform/bundle-test.js b/cypress/platform/bundle-test.js index f5bf0ecd6..24ce8d753 100644 --- a/cypress/platform/bundle-test.js +++ b/cypress/platform/bundle-test.js @@ -27,7 +27,7 @@ const code3 = `flowchart TD A() B(Bold text!)`; -if (location.href.match('test-html-escaping')) { +if (/test-html-escaping/.exec(location.href)) { code = code3; } diff --git a/cypress/platform/viewer.js b/cypress/platform/viewer.js index 482a90646..c397f0e16 100644 --- a/cypress/platform/viewer.js +++ b/cypress/platform/viewer.js @@ -132,7 +132,7 @@ if (typeof document !== 'undefined') { window.addEventListener( 'load', function () { - if (this.location.href.match('xss.html')) { + if (/xss.html/.exec(this.location.href)) { this.console.log('Using api'); void contentLoadedApi().finally(markRendered); } else { diff --git a/docs/config/setup/interfaces/mermaid.DetailedError.md b/docs/config/setup/interfaces/mermaid.DetailedError.md index 6b7d471ae..3b019e58a 100644 --- a/docs/config/setup/interfaces/mermaid.DetailedError.md +++ b/docs/config/setup/interfaces/mermaid.DetailedError.md @@ -16,7 +16,7 @@ #### Defined in -[packages/mermaid/src/utils.ts:789](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L789) +[packages/mermaid/src/utils.ts:785](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L785) --- @@ -26,7 +26,7 @@ #### Defined in -[packages/mermaid/src/utils.ts:787](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L787) +[packages/mermaid/src/utils.ts:783](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L783) --- @@ -36,7 +36,7 @@ #### Defined in -[packages/mermaid/src/utils.ts:790](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L790) +[packages/mermaid/src/utils.ts:786](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L786) --- @@ -46,4 +46,4 @@ #### Defined in -[packages/mermaid/src/utils.ts:785](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L785) +[packages/mermaid/src/utils.ts:781](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/utils.ts#L781) diff --git a/docs/config/setup/modules/mermaid.md b/docs/config/setup/modules/mermaid.md index d51400d6e..10e9330b5 100644 --- a/docs/config/setup/modules/mermaid.md +++ b/docs/config/setup/modules/mermaid.md @@ -44,7 +44,7 @@ #### Defined in -[packages/mermaid/src/Diagram.ts:9](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/Diagram.ts#L9) +[packages/mermaid/src/Diagram.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/Diagram.ts#L10) ## Variables diff --git a/eslint.config.js b/eslint.config.js index de7b59558..ed703ea71 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -13,7 +13,8 @@ import tseslint from 'typescript-eslint'; export default tseslint.config( eslint.configs.recommended, - ...tseslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, { ignores: [ '**/dist/', @@ -101,6 +102,15 @@ export default tseslint.config( }, }, ], + // START: These rules should be turned on once the codebase is cleaned up + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/only-throw-error': 'warn', + '@typescript-eslint/prefer-promise-reject-errors': 'warn', + // END 'json/*': ['error', 'allowComments'], '@cspell/spellchecker': [ 'error', diff --git a/packages/mermaid-example-diagram/src/mermaidUtils.ts b/packages/mermaid-example-diagram/src/mermaidUtils.ts index eeeca05c5..04d0599c0 100644 --- a/packages/mermaid-example-diagram/src/mermaidUtils.ts +++ b/packages/mermaid-example-diagram/src/mermaidUtils.ts @@ -25,7 +25,7 @@ export const log: Record = { fatal: warning, }; -export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void; +export let setLogLevel: (level: keyof typeof LEVELS | number) => void; export let getConfig: () => object; export let sanitizeText: (str: string) => string; export let commonDb: () => object; diff --git a/packages/mermaid-zenuml/src/mermaidUtils.ts b/packages/mermaid-zenuml/src/mermaidUtils.ts index 623685879..413e8d2de 100644 --- a/packages/mermaid-zenuml/src/mermaidUtils.ts +++ b/packages/mermaid-zenuml/src/mermaidUtils.ts @@ -26,7 +26,7 @@ export const log: Record = { fatal: warning, }; -export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void; +export let setLogLevel: (level: keyof typeof LEVELS | number) => void; export let getConfig: () => MermaidConfig; export let sanitizeText: (str: string) => string; // eslint-disable @typescript-eslint/no-explicit-any diff --git a/packages/mermaid-zenuml/src/zenumlRenderer.ts b/packages/mermaid-zenuml/src/zenumlRenderer.ts index a1a807dce..f9dd57996 100644 --- a/packages/mermaid-zenuml/src/zenumlRenderer.ts +++ b/packages/mermaid-zenuml/src/zenumlRenderer.ts @@ -9,7 +9,7 @@ function createTemporaryZenumlContainer(id: string) { container.id = `container-${id}`; container.style.display = 'flex'; container.innerHTML = `
`; - const app = container.querySelector(`#zenUMLApp-${id}`) as HTMLElement; + const app = container.querySelector(`#zenUMLApp-${id}`)!; return { container, app }; } diff --git a/packages/mermaid/scripts/create-types-from-json-schema.mts b/packages/mermaid/scripts/create-types-from-json-schema.mts index 57b066812..1f6015bce 100644 --- a/packages/mermaid/scripts/create-types-from-json-schema.mts +++ b/packages/mermaid/scripts/create-types-from-json-schema.mts @@ -97,7 +97,7 @@ async function generateTypescript(mermaidConfigSchema: JSONSchemaType>) { - if (schema['allOf']) { + if (schema.allOf) { const { allOf, ...schemaWithoutAllOf } = schema; return { ...schemaWithoutAllOf, diff --git a/packages/mermaid/scripts/docs.mts b/packages/mermaid/scripts/docs.mts index 5e7ccb2d8..374e78870 100644 --- a/packages/mermaid/scripts/docs.mts +++ b/packages/mermaid/scripts/docs.mts @@ -88,9 +88,9 @@ const WARN_DOCSDIR_DOESNT_MATCH = `Changed files were transformed in ${SOURCE_DO const prettierConfig = (await prettier.resolveConfig('.')) ?? {}; // From https://github.com/vuejs/vitepress/blob/428eec3750d6b5648a77ac52d88128df0554d4d1/src/node/markdownToVue.ts#L20-L21 const includesRE = //g; -const includedFiles: Set = new Set(); +const includedFiles = new Set(); -const filesTransformed: Set = new Set(); +const filesTransformed = new Set(); const generateHeader = (file: string): string => { // path from file in docs/* to repo root, e.g ../ or ../../ */ @@ -181,10 +181,10 @@ export const transformToBlockQuote = ( ) => { if (vitepress) { const vitepressType = type === 'note' ? 'info' : type; - return `::: ${vitepressType} ${customTitle || ''}\n${content}\n:::`; + return `::: ${vitepressType} ${customTitle ?? ''}\n${content}\n:::`; } else { - const icon = blockIcons[type] || ''; - const title = `${icon}${customTitle || capitalize(type)}`; + const icon = blockIcons[type] ?? ''; + const title = `${icon}${customTitle ?? capitalize(type)}`; return `> **${title}** \n> ${content.replace(/\n/g, '\n> ')}`; } }; @@ -201,6 +201,7 @@ const transformIncludeStatements = (file: string, text: string): string => { includedFiles.add(changeToFinalDocDir(includePath)); return content; } catch (error) { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions throw new Error(`Failed to resolve include "${m1}" in "${file}": ${error}`); } }); diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 1374effb4..fb423b9b0 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -6,6 +6,7 @@ import { encodeEntities } from './utils.js'; import type { DetailedError } from './utils.js'; import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js'; +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void; /** diff --git a/packages/mermaid/src/config.ts b/packages/mermaid/src/config.ts index b766d9b42..1229bcd22 100644 --- a/packages/mermaid/src/config.ts +++ b/packages/mermaid/src/config.ts @@ -189,7 +189,7 @@ export const addDirective = (directive: MermaidConfig) => { sanitizeDirective(directive); // If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables - if (directive.fontFamily && (!directive.themeVariables || !directive.themeVariables.fontFamily)) { + if (directive.fontFamily && !directive.themeVariables?.fontFamily) { directive.themeVariables = { fontFamily: directive.fontFamily }; } diff --git a/packages/mermaid/src/dagre-wrapper/edgeMarker.spec.ts b/packages/mermaid/src/dagre-wrapper/edgeMarker.spec.ts index 6cfb59fab..6fb439552 100644 --- a/packages/mermaid/src/dagre-wrapper/edgeMarker.spec.ts +++ b/packages/mermaid/src/dagre-wrapper/edgeMarker.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/unbound-method */ import type { Mocked } from 'vitest'; import type { SVG } from '../diagram-api/types.js'; import { addEdgeMarkers } from './edgeMarker.js'; diff --git a/packages/mermaid/src/dagre-wrapper/index.js b/packages/mermaid/src/dagre-wrapper/index.js index 70f1a862c..c870566a7 100644 --- a/packages/mermaid/src/dagre-wrapper/index.js +++ b/packages/mermaid/src/dagre-wrapper/index.js @@ -51,7 +51,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit } } log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v))); - if (node && node.clusterNode) { + if (node?.clusterNode) { // const children = graph.children(v); log.info('Cluster identified', v, node.width, graph.node(v)); // `node.graph.setGraph` applies the graph configurations such as nodeSpacing to subgraphs as without this the default values would be used @@ -130,7 +130,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit ' height: ', node.height ); - if (node && node.clusterNode) { + if (node?.clusterNode) { // clusterDb[node.id].node = node; node.y += subGraphTitleTotalMargin; positionNode(node); diff --git a/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js b/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js index 04ee9d16b..ef0c2caa7 100644 --- a/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js +++ b/packages/mermaid/src/dagre-wrapper/mermaid-graphlib.js @@ -399,7 +399,7 @@ export const extractor = (graph, depth) => { const graphSettings = graph.graph(); let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB'; - if (clusterDb[node] && clusterDb[node].clusterData && clusterDb[node].clusterData.dir) { + if (clusterDb[node]?.clusterData?.dir) { dir = clusterDb[node].clusterData.dir; log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir); } diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js index 8abcfe5bd..611cc5b6e 100644 --- a/packages/mermaid/src/dagre-wrapper/nodes.js +++ b/packages/mermaid/src/dagre-wrapper/nodes.js @@ -901,7 +901,7 @@ const class_box = (parent, node) => { const labelContainer = shapeSvg.insert('g').attr('class', 'label'); let verticalPos = 0; - const hasInterface = node.classData.annotations && node.classData.annotations[0]; + const hasInterface = node.classData.annotations?.[0]; // 1. Create the labels const interfaceLabelText = node.classData.annotations[0] diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 76a8152b7..727842bba 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -23,7 +23,7 @@ const config: RequiredDeep = { themeCSS: undefined, // add non-JSON default config values - themeVariables: theme['default'].getThemeVariables(), + themeVariables: theme.default.getThemeVariables(), sequence: { ...defaultConfigJson.sequence, messageFont: function () { @@ -272,5 +272,5 @@ const keyify = (obj: any, prefix = ''): string[] => return [...res, prefix + el]; }, []); -export const configKeys: Set = new Set(keyify(config, '')); +export const configKeys = new Set(keyify(config, '')); export default config; diff --git a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts index 9fe6541d6..68991cffb 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts @@ -29,7 +29,7 @@ describe('DiagramAPI', () => { `[UnknownDiagramError: No diagram type detected matching given configuration for text: loki diagram]` ); const detector: DiagramDetector = (str: string) => { - return str.match('loki') !== null; + return /loki/.exec(str) !== null; }; registerDiagram( 'loki', diff --git a/packages/mermaid/src/diagram-api/diagramAPI.ts b/packages/mermaid/src/diagram-api/diagramAPI.ts index 5ea3825ef..df4514adf 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.ts @@ -30,9 +30,7 @@ export const getCommonDb = () => { }; const diagrams: Record = {}; -export interface Detectors { - [key: string]: DiagramDetector; -} +export type Detectors = Record; /** * Registers the given diagram with Mermaid. diff --git a/packages/mermaid/src/diagram.spec.ts b/packages/mermaid/src/diagram.spec.ts index 4b9c907b3..873fada14 100644 --- a/packages/mermaid/src/diagram.spec.ts +++ b/packages/mermaid/src/diagram.spec.ts @@ -30,12 +30,12 @@ const getDummyDiagram = (id: string, title?: string): Awaited { test('should detect inbuilt diagrams', async () => { - const graph = (await Diagram.fromText('graph TD; A-->B')) as Diagram; + const graph = await Diagram.fromText('graph TD; A-->B'); expect(graph).toBeInstanceOf(Diagram); expect(graph.type).toBe('flowchart-v2'); - const sequence = (await Diagram.fromText( + const sequence = await Diagram.fromText( 'sequenceDiagram; Alice->>+John: Hello John, how are you?' - )) as Diagram; + ); expect(sequence).toBeInstanceOf(Diagram); expect(sequence.type).toBe('sequence'); }); diff --git a/packages/mermaid/src/diagrams/block/blockDB.ts b/packages/mermaid/src/diagrams/block/blockDB.ts index 361e23d5c..d6e35ed15 100644 --- a/packages/mermaid/src/diagrams/block/blockDB.ts +++ b/packages/mermaid/src/diagrams/block/blockDB.ts @@ -8,9 +8,9 @@ import { clear as commonClear } from '../common/commonDb.js'; import type { Block, ClassDef } from './blockTypes.js'; // Initialize the node database for simple lookups -let blockDatabase: Map = new Map(); +let blockDatabase = new Map(); let edgeList: Block[] = []; -let edgeCount: Map = new Map(); +let edgeCount = new Map(); const COLOR_KEYWORD = 'color'; const FILL_KEYWORD = 'fill'; @@ -18,7 +18,7 @@ const BG_FILL = 'bgFill'; const STYLECLASS_SEP = ','; const config = getConfig(); -let classes: Map = new Map(); +let classes = new Map(); const sanitizeText = (txt: string) => common.sanitizeText(txt, config); @@ -42,7 +42,7 @@ export const addStyleClass = function (id: string, styleAttributes = '') { const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim(); // replace some style keywords - if (attrib.match(COLOR_KEYWORD)) { + if (RegExp(COLOR_KEYWORD).exec(attrib)) { const newStyle1 = fixedAttrib.replace(FILL_KEYWORD, BG_FILL); const newStyle2 = newStyle1.replace(COLOR_KEYWORD, FILL_KEYWORD); foundClass.textStyles.push(newStyle2); @@ -89,7 +89,7 @@ export const setCssClass = function (itemIds: string, cssClassName: string) { }); }; -const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block): void => { +const populateBlockDatabase = (_blockList: Block[], parent: Block): void => { const blockList = _blockList.flat(); const children = []; for (const block of blockList) { @@ -101,7 +101,7 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block): continue; } if (block.type === 'applyClass') { - setCssClass(block.id, block?.styleClass || ''); + setCssClass(block.id, block?.styleClass ?? ''); continue; } if (block.type === 'applyStyles') { @@ -111,7 +111,7 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block): continue; } if (block.type === 'column-setting') { - parent.columns = block.columns || -1; + parent.columns = block.columns ?? -1; } else if (block.type === 'edge') { const count = (edgeCount.get(block.id) ?? 0) + 1; edgeCount.set(block.id, count); @@ -145,7 +145,7 @@ const populateBlockDatabase = (_blockList: Block[] | Block[][], parent: Block): } if (block.type === 'space') { // log.debug('abc95 space', block); - const w = block.width || 1; + const w = block.width ?? 1; for (let j = 0; j < w; j++) { const newBlock = clone(block); newBlock.id = newBlock.id + '-' + j; @@ -168,7 +168,7 @@ const clear = (): void => { commonClear(); rootBlock = { id: 'root', type: 'composite', children: [], columns: -1 } as Block; blockDatabase = new Map([['root', rootBlock]]); - blocks = [] as Block[]; + blocks = []; classes = new Map(); edgeList = []; diff --git a/packages/mermaid/src/diagrams/block/blockRenderer.ts b/packages/mermaid/src/diagrams/block/blockRenderer.ts index 6e3f61a4d..99b89ceeb 100644 --- a/packages/mermaid/src/diagrams/block/blockRenderer.ts +++ b/packages/mermaid/src/diagrams/block/blockRenderer.ts @@ -1,7 +1,6 @@ import { select as d3select } from 'd3'; import type { Diagram } from '../../Diagram.js'; import * as configApi from '../../config.js'; -import type { MermaidConfig } from '../../config.type.js'; import insertMarkers from '../../dagre-wrapper/markers.js'; import { log } from '../../logger.js'; import { configureSvgSize } from '../../setupGraphViewbox.js'; @@ -71,7 +70,7 @@ export const draw = async function ( const magicFactor = Math.max(1, Math.round(0.125 * (bounds2.width / bounds2.height))); const height = bounds2.height + magicFactor + 10; const width = bounds2.width + 10; - const { useMaxWidth } = conf as Exclude; + const { useMaxWidth } = conf!; configureSvgSize(svg, height, width, !!useMaxWidth); log.debug('Here Bounds', bounds, bounds2); svg.attr( diff --git a/packages/mermaid/src/diagrams/block/layout.ts b/packages/mermaid/src/diagrams/block/layout.ts index cb9496a69..7f44a5f19 100644 --- a/packages/mermaid/src/diagrams/block/layout.ts +++ b/packages/mermaid/src/diagrams/block/layout.ts @@ -2,7 +2,8 @@ import type { BlockDB } from './blockDB.js'; import type { Block } from './blockTypes.js'; import { log } from '../../logger.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; -const padding = getConfig()?.block?.padding || 8; +// TODO: This means the number we provide in diagram's config will never be used. Should fix. +const padding = getConfig()?.block?.padding ?? 8; interface BlockPosition { px: number; @@ -42,7 +43,7 @@ const getMaxChildSize = (block: Block) => { // find max width of children // log.debug('getMaxChildSize abc95 (start) parent:', block.id); for (const child of block.children) { - const { width, height, x, y } = child.size || { width: 0, height: 0, x: 0, y: 0 }; + const { width, height, x, y } = child.size ?? { width: 0, height: 0, x: 0, y: 0 }; log.debug( 'getMaxChildSize abc95 child:', child.id, @@ -60,7 +61,7 @@ const getMaxChildSize = (block: Block) => { continue; } if (width > maxWidth) { - maxWidth = width / (block.widthInColumns || 1); + maxWidth = width / (block.widthInColumns ?? 1); } if (height > maxHeight) { maxHeight = height; @@ -104,10 +105,10 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh for (const child of block.children) { if (child.size) { log.debug( - `abc95 Setting size of children of ${block.id} id=${child.id} ${maxWidth} ${maxHeight} ${child.size}` + `abc95 Setting size of children of ${block.id} id=${child.id} ${maxWidth} ${maxHeight} ${JSON.stringify(child.size)}` ); child.size.width = - maxWidth * (child.widthInColumns || 1) + padding * ((child.widthInColumns || 1) - 1); + maxWidth * (child.widthInColumns ?? 1) + padding * ((child.widthInColumns ?? 1) - 1); child.size.height = maxHeight; child.size.x = 0; child.size.y = 0; @@ -121,10 +122,10 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh setBlockSizes(child, db, maxWidth, maxHeight); } - const columns = block.columns || -1; + const columns = block.columns ?? -1; let numItems = 0; for (const child of block.children) { - numItems += child.widthInColumns || 1; + numItems += child.widthInColumns ?? 1; } // The width and height in number blocks @@ -204,13 +205,13 @@ function layoutBlocks(block: Block, db: BlockDB) { log.debug( `abc85 layout blocks (=>layoutBlocks) ${block.id} x: ${block?.size?.x} y: ${block?.size?.y} width: ${block?.size?.width}` ); - const columns = block.columns || -1; + const columns = block.columns ?? -1; log.debug('layoutBlocks columns abc95', block.id, '=>', columns, block); if ( block.children && // find max width of children block.children.length > 0 ) { - const width = block?.children[0]?.size?.width || 0; + const width = block?.children[0]?.size?.width ?? 0; const widthOfChildren = block.children.length * width + (block.children.length - 1) * padding; log.debug('widthOfChildren 88', widthOfChildren, 'posX'); @@ -249,7 +250,7 @@ function layoutBlocks(block: Block, db: BlockDB) { } ${halfWidth} padding=${padding} width=${width} halfWidth=${halfWidth} => x:${ child.size.x } y:${child.size.y} ${child.widthInColumns} (width * (child?.w || 1)) / 2 ${ - (width * (child?.widthInColumns || 1)) / 2 + (width * (child?.widthInColumns ?? 1)) / 2 }` ); @@ -263,15 +264,13 @@ function layoutBlocks(block: Block, db: BlockDB) { child.id }startingPosX${startingPosX}${padding}${halfWidth}=>x:${child.size.x}y:${child.size.y}${ child.widthInColumns - }(width * (child?.w || 1)) / 2${(width * (child?.widthInColumns || 1)) / 2}` + }(width * (child?.w || 1)) / 2${(width * (child?.widthInColumns ?? 1)) / 2}` ); } - - // posY += height + padding; if (child.children) { layoutBlocks(child, db); } - columnPos += child?.widthInColumns || 1; + columnPos += child?.widthInColumns ?? 1; log.debug('abc88 columnsPos', child, columnPos); } } diff --git a/packages/mermaid/src/diagrams/block/parser/block.spec.ts b/packages/mermaid/src/diagrams/block/parser/block.spec.ts index 295dabf89..1bb8691c1 100644 --- a/packages/mermaid/src/diagrams/block/parser/block.spec.ts +++ b/packages/mermaid/src/diagrams/block/parser/block.spec.ts @@ -1,9 +1,6 @@ // @ts-ignore: jison doesn't export types import block from './block.jison'; import db from '../blockDB.js'; -import { cleanupComments } from '../../../diagram-api/comments.js'; -import { prepareTextForParsing } from '../blockUtils.js'; -import { setConfig } from '../../../config.js'; describe('Block diagram', function () { describe('when parsing an block diagram graph it should handle > ', function () { @@ -13,7 +10,7 @@ describe('Block diagram', function () { block.parser.yy.getLogger = () => console; }); - it('a diagram with a node', async () => { + it('a diagram with a node', () => { const str = `block-beta id `; @@ -24,7 +21,7 @@ describe('Block diagram', function () { expect(blocks[0].id).toBe('id'); expect(blocks[0].label).toBe('id'); }); - it('a node with a square shape and a label', async () => { + it('a node with a square shape and a label', () => { const str = `block-beta id["A label"] `; @@ -36,7 +33,7 @@ describe('Block diagram', function () { expect(blocks[0].label).toBe('A label'); expect(blocks[0].type).toBe('square'); }); - it('a diagram with multiple nodes', async () => { + it('a diagram with multiple nodes', () => { const str = `block-beta id1 id2 @@ -52,7 +49,7 @@ describe('Block diagram', function () { expect(blocks[1].label).toBe('id2'); expect(blocks[1].type).toBe('na'); }); - it('a diagram with multiple nodes', async () => { + it('a diagram with multiple nodes', () => { const str = `block-beta id1 id2 @@ -73,7 +70,7 @@ describe('Block diagram', function () { expect(blocks[2].type).toBe('na'); }); - it('a node with a square shape and a label', async () => { + it('a node with a square shape and a label', () => { const str = `block-beta id["A label"] id2`; @@ -88,7 +85,7 @@ describe('Block diagram', function () { expect(blocks[1].label).toBe('id2'); expect(blocks[1].type).toBe('na'); }); - it('a diagram with multiple nodes with edges abc123', async () => { + it('a diagram with multiple nodes with edges abc123', () => { const str = `block-beta id1["first"] --> id2["second"] `; @@ -102,7 +99,7 @@ describe('Block diagram', function () { expect(edges[0].end).toBe('id2'); expect(edges[0].arrowTypeEnd).toBe('arrow_point'); }); - it('a diagram with multiple nodes with edges abc123', async () => { + it('a diagram with multiple nodes with edges abc123', () => { const str = `block-beta id1["first"] -- "a label" --> id2["second"] `; @@ -117,7 +114,7 @@ describe('Block diagram', function () { expect(edges[0].arrowTypeEnd).toBe('arrow_point'); expect(edges[0].label).toBe('a label'); }); - it('a diagram with column statements', async () => { + it('a diagram with column statements', () => { const str = `block-beta columns 2 block1["Block 1"] @@ -128,7 +125,7 @@ describe('Block diagram', function () { const blocks = db.getBlocks(); expect(blocks.length).toBe(1); }); - it('a diagram withput column statements', async () => { + it('a diagram withput column statements', () => { const str = `block-beta block1["Block 1"] `; @@ -138,7 +135,7 @@ describe('Block diagram', function () { const blocks = db.getBlocks(); expect(blocks.length).toBe(1); }); - it('a diagram with auto column statements', async () => { + it('a diagram with auto column statements', () => { const str = `block-beta columns auto block1["Block 1"] @@ -150,7 +147,7 @@ describe('Block diagram', function () { expect(blocks.length).toBe(1); }); - it('blocks next to each other', async () => { + it('blocks next to each other', () => { const str = `block-beta columns 2 block1["Block 1"] @@ -164,7 +161,7 @@ describe('Block diagram', function () { expect(blocks.length).toBe(2); }); - it('blocks on top of each other', async () => { + it('blocks on top of each other', () => { const str = `block-beta columns 1 block1["Block 1"] @@ -178,7 +175,7 @@ describe('Block diagram', function () { expect(blocks.length).toBe(2); }); - it('compound blocks 2', async () => { + it('compound blocks 2', () => { const str = `block-beta block aBlock["ABlock"] @@ -206,7 +203,7 @@ describe('Block diagram', function () { expect(bBlock.label).toBe('BBlock'); expect(bBlock.type).toBe('square'); }); - it('compound blocks of compound blocks', async () => { + it('compound blocks of compound blocks', () => { const str = `block-beta block aBlock["ABlock"] @@ -241,7 +238,7 @@ describe('Block diagram', function () { expect(bBlock.label).toBe('BBlock'); expect(bBlock.type).toBe('square'); }); - it('compound blocks with title', async () => { + it('compound blocks with title', () => { const str = `block-beta block:compoundBlock["Compound block"] columns 1 @@ -266,7 +263,7 @@ describe('Block diagram', function () { expect(block2.label).toBe('Block 2'); expect(block2.type).toBe('square'); }); - it('blocks mixed with compound blocks', async () => { + it('blocks mixed with compound blocks', () => { const str = `block-beta columns 1 block1["Block 1"] @@ -293,7 +290,7 @@ describe('Block diagram', function () { expect(block2.type).toBe('square'); }); - it('Arrow blocks', async () => { + it('Arrow blocks', () => { const str = `block-beta columns 3 block1["Block 1"] @@ -317,7 +314,7 @@ describe('Block diagram', function () { expect(blockArrow.type).toBe('block_arrow'); expect(blockArrow.directions).toContain('right'); }); - it('Arrow blocks with multiple points', async () => { + it('Arrow blocks with multiple points', () => { const str = `block-beta columns 1 A @@ -340,7 +337,7 @@ describe('Block diagram', function () { expect(blockArrow.directions).toContain('down'); expect(blockArrow.directions).not.toContain('right'); }); - it('blocks with different widths', async () => { + it('blocks with different widths', () => { const str = `block-beta columns 3 one["One Slot"] @@ -355,7 +352,7 @@ describe('Block diagram', function () { const two = blocks[1]; expect(two.widthInColumns).toBe(2); }); - it('empty blocks', async () => { + it('empty blocks', () => { const str = `block-beta columns 3 space @@ -374,7 +371,7 @@ describe('Block diagram', function () { expect(sp2.type).toBe('space'); expect(middle.label).toBe('In the middle'); }); - it('classDef statements applied to a block', async () => { + it('classDef statements applied to a block', () => { const str = `block-beta classDef black color:#ffffff, fill:#000000; @@ -392,7 +389,7 @@ describe('Block diagram', function () { expect(black.id).toBe('black'); expect(black.styles[0]).toEqual('color:#ffffff'); }); - it('style statements applied to a block', async () => { + it('style statements applied to a block', () => { const str = `block-beta columns 1 B["A wide one in the middle"] diff --git a/packages/mermaid/src/diagrams/block/renderHelpers.ts b/packages/mermaid/src/diagrams/block/renderHelpers.ts index fc73ca2ec..97eca4074 100644 --- a/packages/mermaid/src/diagrams/block/renderHelpers.ts +++ b/packages/mermaid/src/diagrams/block/renderHelpers.ts @@ -11,7 +11,7 @@ function getNodeFromBlock(block: Block, db: BlockDB, positioned = false) { let classStr = 'default'; if ((vertex?.classes?.length || 0) > 0) { - classStr = (vertex?.classes || []).join(' '); + classStr = (vertex?.classes ?? []).join(' '); } classStr = classStr + ' flowchart-label'; @@ -85,12 +85,12 @@ function getNodeFromBlock(block: Block, db: BlockDB, positioned = false) { shape = 'rect'; } - const styles = getStylesFromArray(vertex?.styles || []); + const styles = getStylesFromArray(vertex?.styles ?? []); // Use vertex id as text in the box if no text is provided by the graph definition const vertexText = vertex.label; - const bounds = vertex.size || { width: 0, height: 0, x: 0, y: 0 }; + const bounds = vertex.size ?? { width: 0, height: 0, x: 0, y: 0 }; // Add the node const node = { labelStyle: styles.labelStyle, @@ -109,7 +109,7 @@ function getNodeFromBlock(block: Block, db: BlockDB, positioned = false) { positioned, intersect: undefined, type: vertex.type, - padding: padding ?? (getConfig()?.block?.padding || 0), + padding: padding ?? getConfig()?.block?.padding ?? 0, }; return node; } @@ -214,7 +214,7 @@ export async function insertEdges( { x: end.x, y: end.y }, ]; // edge.points = points; - await insertEdge( + insertEdge( elem, { v: edge.start, w: edge.end, name: edge.id }, { @@ -239,7 +239,7 @@ export async function insertEdges( points, classes: 'edge-thickness-normal edge-pattern-solid flowchart-link LS-a1 LE-b1', }); - await positionEdgeLabel( + positionEdgeLabel( { ...edge, x: points[1].x, y: points[1].y }, { originalPath: points, diff --git a/packages/mermaid/src/diagrams/c4/c4Renderer.js b/packages/mermaid/src/diagrams/c4/c4Renderer.js index 959eba295..58dd808fd 100644 --- a/packages/mermaid/src/diagrams/c4/c4Renderer.js +++ b/packages/mermaid/src/diagrams/c4/c4Renderer.js @@ -258,21 +258,21 @@ export const drawC4ShapeArray = function (currentBounds, diagram, c4ShapeArray, c4ShapeLabelConf.fontSize = c4ShapeLabelConf.fontSize + 2; c4ShapeLabelConf.fontWeight = 'bold'; calcC4ShapeTextWH('label', c4Shape, c4ShapeTextWrap, c4ShapeLabelConf, textLimitWidth); - c4Shape['label'].Y = Y + 8; - Y = c4Shape['label'].Y + c4Shape['label'].height; + c4Shape.label.Y = Y + 8; + Y = c4Shape.label.Y + c4Shape.label.height; if (c4Shape.type && c4Shape.type.text !== '') { c4Shape.type.text = '[' + c4Shape.type.text + ']'; let c4ShapeTypeConf = c4ShapeFont(conf, c4Shape.typeC4Shape.text); calcC4ShapeTextWH('type', c4Shape, c4ShapeTextWrap, c4ShapeTypeConf, textLimitWidth); - c4Shape['type'].Y = Y + 5; - Y = c4Shape['type'].Y + c4Shape['type'].height; + c4Shape.type.Y = Y + 5; + Y = c4Shape.type.Y + c4Shape.type.height; } else if (c4Shape.techn && c4Shape.techn.text !== '') { c4Shape.techn.text = '[' + c4Shape.techn.text + ']'; let c4ShapeTechnConf = c4ShapeFont(conf, c4Shape.techn.text); calcC4ShapeTextWH('techn', c4Shape, c4ShapeTextWrap, c4ShapeTechnConf, textLimitWidth); - c4Shape['techn'].Y = Y + 5; - Y = c4Shape['techn'].Y + c4Shape['techn'].height; + c4Shape.techn.Y = Y + 5; + Y = c4Shape.techn.Y + c4Shape.techn.height; } let rectHeight = Y; @@ -281,11 +281,11 @@ export const drawC4ShapeArray = function (currentBounds, diagram, c4ShapeArray, if (c4Shape.descr && c4Shape.descr.text !== '') { let c4ShapeDescrConf = c4ShapeFont(conf, c4Shape.typeC4Shape.text); calcC4ShapeTextWH('descr', c4Shape, c4ShapeTextWrap, c4ShapeDescrConf, textLimitWidth); - c4Shape['descr'].Y = Y + 20; - Y = c4Shape['descr'].Y + c4Shape['descr'].height; + c4Shape.descr.Y = Y + 20; + Y = c4Shape.descr.Y + c4Shape.descr.height; rectWidth = Math.max(c4Shape.label.width, c4Shape.descr.width); - rectHeight = Y - c4Shape['descr'].textLines * 5; + rectHeight = Y - c4Shape.descr.textLines * 5; } rectWidth = rectWidth + conf.c4ShapePadding; @@ -482,8 +482,8 @@ function drawInsideBoundary( currentBoundaryLabelConf, currentBounds.data.widthLimit ); - currentBoundary['label'].Y = Y + 8; - Y = currentBoundary['label'].Y + currentBoundary['label'].height; + currentBoundary.label.Y = Y + 8; + Y = currentBoundary.label.Y + currentBoundary.label.height; if (currentBoundary.type && currentBoundary.type.text !== '') { currentBoundary.type.text = '[' + currentBoundary.type.text + ']'; @@ -495,8 +495,8 @@ function drawInsideBoundary( currentBoundaryTypeConf, currentBounds.data.widthLimit ); - currentBoundary['type'].Y = Y + 5; - Y = currentBoundary['type'].Y + currentBoundary['type'].height; + currentBoundary.type.Y = Y + 5; + Y = currentBoundary.type.Y + currentBoundary.type.height; } if (currentBoundary.descr && currentBoundary.descr.text !== '') { @@ -509,8 +509,8 @@ function drawInsideBoundary( currentBoundaryDescrConf, currentBounds.data.widthLimit ); - currentBoundary['descr'].Y = Y + 20; - Y = currentBoundary['descr'].Y + currentBoundary['descr'].height; + currentBoundary.descr.Y = Y + 20; + Y = currentBoundary.descr.Y + currentBoundary.descr.height; } if (i == 0 || i % c4BoundaryInRow === 0) { diff --git a/packages/mermaid/src/diagrams/class/classDb.ts b/packages/mermaid/src/diagrams/class/classDb.ts index 4cfa0bd29..cb8b90af4 100644 --- a/packages/mermaid/src/diagrams/class/classDb.ts +++ b/packages/mermaid/src/diagrams/class/classDb.ts @@ -26,10 +26,10 @@ import type { const MERMAID_DOM_ID_PREFIX = 'classId-'; let relations: ClassRelation[] = []; -let classes: Map = new Map(); +let classes = new Map(); let notes: ClassNote[] = []; let classCounter = 0; -let namespaces: Map = new Map(); +let namespaces = new Map(); let namespaceCounter = 0; let functions: any[] = []; @@ -223,7 +223,7 @@ export const cleanupLabel = function (label: string) { export const setCssClass = function (ids: string, className: string) { ids.split(',').forEach(function (_id) { let id = _id; - if (_id[0].match(/\d/)) { + if (/\d/.exec(_id[0])) { id = MERMAID_DOM_ID_PREFIX + id; } const classNode = classes.get(id); @@ -266,7 +266,7 @@ export const setLink = function (ids: string, linkStr: string, target: string) { const config = getConfig(); ids.split(',').forEach(function (_id) { let id = _id; - if (_id[0].match(/\d/)) { + if (/\d/.exec(_id[0])) { id = MERMAID_DOM_ID_PREFIX + id; } const theClass = classes.get(id); @@ -320,7 +320,7 @@ const setClickFunc = function (_domId: string, functionName: string, functionArg let item = argList[i].trim(); /* Removes all double quotes at the start and end of an argument */ /* This preserves all starting and ending whitespace inside */ - if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') { + if (item.startsWith('"') && item.endsWith('"')) { item = item.substr(1, item.length - 2); } argList[i] = item; diff --git a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts index b7b6ad98f..0f02efa0d 100644 --- a/packages/mermaid/src/diagrams/class/classRenderer-v2.ts +++ b/packages/mermaid/src/diagrams/class/classRenderer-v2.ts @@ -343,7 +343,7 @@ export const draw = async function (text: string, id: string, _version: string, } const root = securityLevel === 'sandbox' - ? select(sandboxElement!.nodes()[0]!.contentDocument.body) + ? select(sandboxElement.nodes()[0]!.contentDocument.body) : select('body'); const svg = root.select(`[id="${id}"]`); @@ -363,8 +363,7 @@ export const draw = async function (text: string, id: string, _version: string, // Add label rects for non html labels if (!conf?.htmlLabels) { - const doc = - securityLevel === 'sandbox' ? sandboxElement!.nodes()[0]!.contentDocument : document; + const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0]!.contentDocument : document; const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label'); for (const label of labels) { // Get dimensions of label diff --git a/packages/mermaid/src/diagrams/class/classTypes.ts b/packages/mermaid/src/diagrams/class/classTypes.ts index 5c5812ee1..f1955a224 100644 --- a/packages/mermaid/src/diagrams/class/classTypes.ts +++ b/packages/mermaid/src/diagrams/class/classTypes.ts @@ -77,7 +77,7 @@ export class ClassMember { if (this.memberType === 'method') { const methodRegEx = /([#+~-])?(.+)\((.*)\)([\s$*])?(.*)([$*])?/; - const match = input.match(methodRegEx); + const match = methodRegEx.exec(input); if (match) { const detectedVisibility = match[1] ? match[1].trim() : ''; @@ -92,7 +92,7 @@ export class ClassMember { if (potentialClassifier === '') { const lastChar = this.returnType.substring(this.returnType.length - 1); - if (lastChar.match(/[$*]/)) { + if (/[$*]/.exec(lastChar)) { potentialClassifier = lastChar; this.returnType = this.returnType.substring(0, this.returnType.length - 1); } @@ -107,7 +107,7 @@ export class ClassMember { this.visibility = firstChar as Visibility; } - if (lastChar.match(/[$*]/)) { + if (/[$*]/.exec(lastChar)) { potentialClassifier = lastChar; } diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts index 66cc5ff6f..e24c8e85c 100644 --- a/packages/mermaid/src/diagrams/common/common.ts +++ b/packages/mermaid/src/diagrams/common/common.ts @@ -34,13 +34,13 @@ function setupDompurifyHooks() { DOMPurify.addHook('beforeSanitizeAttributes', (node: Element) => { if (node.tagName === 'A' && node.hasAttribute('target')) { - node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') || ''); + node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? ''); } }); DOMPurify.addHook('afterSanitizeAttributes', (node: Element) => { if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) { - node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) || ''); + node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? ''); node.removeAttribute(TEMPORARY_ATTRIBUTE); if (node.getAttribute('target') === '_blank') { node.setAttribute('rel', 'noopener'); @@ -83,6 +83,7 @@ export const sanitizeText = (text: string, config: MermaidConfig): string => { return text; } if (config.dompurifyConfig) { + // eslint-disable-next-line @typescript-eslint/no-base-to-string text = DOMPurify.sanitize(sanitizeMore(text, config), config.dompurifyConfig).toString(); } else { text = DOMPurify.sanitize(sanitizeMore(text, config), { diff --git a/packages/mermaid/src/diagrams/er/erDb.js b/packages/mermaid/src/diagrams/er/erDb.js index 745ef938a..f24f48198 100644 --- a/packages/mermaid/src/diagrams/er/erDb.js +++ b/packages/mermaid/src/diagrams/er/erDb.js @@ -26,12 +26,16 @@ const Identification = { NON_IDENTIFYING: 'NON_IDENTIFYING', IDENTIFYING: 'IDENTIFYING', }; - +/** + * Add entity + * @param {string} name - The name of the entity + * @param {string | undefined} alias - The alias of the entity + */ const addEntity = function (name, alias = undefined) { if (!entities.has(name)) { - entities.set(name, { attributes: [], alias: alias }); + entities.set(name, { attributes: [], alias }); log.info('Added new entity :', name); - } else if (entities.has(name) && !entities.get(name).alias && alias) { + } else if (!entities.get(name).alias && alias) { entities.get(name).alias = alias; log.info(`Add alias '${alias}' to entity '${name}'`); } diff --git a/packages/mermaid/src/diagrams/flowchart/flowChartShapes.spec.js b/packages/mermaid/src/diagrams/flowchart/flowChartShapes.spec.js index 96e6f6fd7..3a1bd865f 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowChartShapes.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/flowChartShapes.spec.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ import { addToRender } from './flowChartShapes.js'; describe('flowchart shapes', function () { diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index 797130e71..d03f1d989 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -17,12 +17,12 @@ import type { FlowVertex, FlowClass, FlowSubGraph, FlowText, FlowEdge, FlowLink const MERMAID_DOM_ID_PREFIX = 'flowchart-'; let vertexCounter = 0; let config = getConfig(); -let vertices: Map = new Map(); +let vertices = new Map(); let edges: FlowEdge[] & { defaultInterpolate?: string; defaultStyle?: string[] } = []; -let classes: Map = new Map(); +let classes = new Map(); let subGraphs: FlowSubGraph[] = []; -let subGraphLookup: Map = new Map(); -let tooltips: Map = new Map(); +let subGraphLookup = new Map(); +let tooltips = new Map(); let subCount = 0; let firstGraphFlag = true; let direction: string; @@ -84,7 +84,7 @@ export const addVertex = function ( txt = sanitizeText(textObj.text.trim()); vertex.labelType = textObj.type; // strip quotes if string starts and ends with a quote - if (txt[0] === '"' && txt[txt.length - 1] === '"') { + if (txt.startsWith('"') && txt.endsWith('"')) { txt = txt.substring(1, txt.length - 1); } vertex.text = txt; @@ -132,7 +132,7 @@ export const addSingleLink = function (_start: string, _end: string, type: any) edge.text = sanitizeText(linkTextObj.text.trim()); // strip quotes if string starts and ends with a quote - if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') { + if (edge.text.startsWith('"') && edge.text.endsWith('"')) { edge.text = edge.text.substring(1, edge.text.length - 1); } edge.labelType = linkTextObj.type; @@ -218,7 +218,7 @@ export const addClass = function (ids: string, style: string[]) { if (style !== undefined && style !== null) { style.forEach(function (s) { - if (s.match('color')) { + if (/color/.exec(s)) { const newStyle = s.replace('fill', 'bgFill').replace('color', 'fill'); classNode.textStyles.push(newStyle); } @@ -234,16 +234,16 @@ export const addClass = function (ids: string, style: string[]) { */ export const setDirection = function (dir: string) { direction = dir; - if (direction.match(/.*/)) { + if (/.*>/.exec(direction)) { direction = 'LR'; } - if (direction.match(/.*v/)) { + if (/.*v/.exec(direction)) { direction = 'TB'; } if (direction === 'TD') { @@ -297,7 +297,7 @@ const setClickFun = function (id: string, functionName: string, functionArgs: st let item = argList[i].trim(); /* Removes all double quotes at the start and end of an argument */ /* This preserves all starting and ending whitespace inside */ - if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') { + if (item.startsWith('"') && item.endsWith('"')) { item = item.substr(1, item.length - 2); } argList[i] = item; @@ -469,7 +469,7 @@ export const addSubGraph = function ( ) { let id: string | undefined = _id.text.trim(); let title = _title.text; - if (_id === _title && _title.text.match(/\s/)) { + if (_id === _title && /\s/.exec(_title.text)) { id = undefined; } @@ -503,7 +503,7 @@ export const addSubGraph = function ( } } - id = id || 'subGraph' + subCount; + id = id ?? 'subGraph' + subCount; title = title || ''; title = sanitizeText(title); subCount = subCount + 1; @@ -651,21 +651,21 @@ const destructEndLink = (_str: string) => { switch (str.slice(-1)) { case 'x': type = 'arrow_cross'; - if (str[0] === 'x') { + if (str.startsWith('x')) { type = 'double_' + type; line = line.slice(1); } break; case '>': type = 'arrow_point'; - if (str[0] === '<') { + if (str.startsWith('<')) { type = 'double_' + type; line = line.slice(1); } break; case 'o': type = 'arrow_circle'; - if (str[0] === 'o') { + if (str.startsWith('o')) { type = 'double_' + type; line = line.slice(1); } @@ -675,11 +675,11 @@ const destructEndLink = (_str: string) => { let stroke = 'normal'; let length = line.length - 1; - if (line[0] === '=') { + if (line.startsWith('=')) { stroke = 'thick'; } - if (line[0] === '~') { + if (line.startsWith('~')) { stroke = 'invisible'; } diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js index 7c81b7e6d..1dce1391e 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js @@ -366,8 +366,8 @@ export const draw = async function (text, id, _version, diagObj) { } const { securityLevel, flowchart: conf } = getConfig(); - const nodeSpacing = conf.nodeSpacing || 50; - const rankSpacing = conf.rankSpacing || 50; + const nodeSpacing = conf.nodeSpacing ?? 50; + const rankSpacing = conf.rankSpacing ?? 50; // Handle root and document for when rendering in sandbox mode let sandboxElement; @@ -420,14 +420,13 @@ export const draw = async function (text, id, _version, diagObj) { log.info('Edges', edges); let i = 0; for (i = subGraphs.length - 1; i >= 0; i--) { - // for (let i = 0; i < subGraphs.length; i++) { subG = subGraphs[i]; selectAll('cluster').append('text'); - for (let j = 0; j < subG.nodes.length; j++) { - log.info('Setting up subgraphs', subG.nodes[j], subG.id); - g.setParent(subG.nodes[j], subG.id); + for (const node of subG.nodes) { + log.info('Setting up subgraphs', node, subG.id); + g.setParent(node, subG.id); } } await addVertices(vert, g, id, root, doc, diagObj); diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js index ca558987c..314c6aa52 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js @@ -301,8 +301,8 @@ export const draw = async function (text, id, _version, diagObj) { if (dir === undefined) { dir = 'TD'; } - const nodeSpacing = conf.nodeSpacing || 50; - const rankSpacing = conf.rankSpacing || 50; + const nodeSpacing = conf.nodeSpacing ?? 50; + const rankSpacing = conf.rankSpacing ?? 50; // Create the input mermaid.graph const g = new graphlib.Graph({ @@ -339,14 +339,14 @@ export const draw = async function (text, id, _version, diagObj) { selectAll('cluster').append('text'); - for (let j = 0; j < subG.nodes.length; j++) { + for (const node of subG.nodes) { log.warn( 'Setting subgraph', - subG.nodes[j], - diagObj.db.lookUpDomId(subG.nodes[j]), + node, + diagObj.db.lookUpDomId(node), diagObj.db.lookUpDomId(subG.id) ); - g.setParent(diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subG.id)); + g.setParent(diagObj.db.lookUpDomId(node), diagObj.db.lookUpDomId(subG.id)); } } await addVertices(vert, g, id, root, doc, diagObj); @@ -429,8 +429,8 @@ export const draw = async function (text, id, _version, diagObj) { te.attr('transform', `translate(${xPos + _width / 2}, ${yPos + 14})`); te.attr('id', id + 'Text'); - for (let j = 0; j < subG.classes.length; j++) { - clusterEl[0].classList.add(subG.classes[j]); + for (const className of subG.classes) { + clusterEl[0].classList.add(className); } } } diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js index bdf778b54..79bf75453 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer.spec.js @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ import { addVertices, addEdges } from './flowRenderer.js'; import { setConfig } from '../../diagram-api/diagramAPI.js'; diff --git a/packages/mermaid/src/diagrams/gantt/ganttDb.js b/packages/mermaid/src/diagrams/gantt/ganttDb.js index 160f29713..15c7fab97 100644 --- a/packages/mermaid/src/diagrams/gantt/ganttDb.js +++ b/packages/mermaid/src/diagrams/gantt/ganttDb.js @@ -676,7 +676,7 @@ const setClickFun = function (id, functionName, functionArgs) { let item = argList[i].trim(); /* Removes all double quotes at the start and end of an argument */ /* This preserves all starting and ending whitespace inside */ - if (item.charAt(0) === '"' && item.charAt(item.length - 1) === '"') { + if (item.startsWith('"') && item.endsWith('"')) { item = item.substr(1, item.length - 2); } argList[i] = item; diff --git a/packages/mermaid/src/diagrams/git/gitGraphAst.js b/packages/mermaid/src/diagrams/git/gitGraphAst.js index e049d5754..0f7ca29a2 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphAst.js +++ b/packages/mermaid/src/diagrams/git/gitGraphAst.js @@ -90,7 +90,7 @@ export const setDirection = function (dir) { let options = {}; export const setOptions = function (rawOptString) { log.debug('options str', rawOptString); - rawOptString = rawOptString && rawOptString.trim(); + rawOptString = rawOptString?.trim(); rawOptString = rawOptString || '{}'; try { options = JSON.parse(rawOptString); diff --git a/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js b/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js index 3398dd55f..d498577fe 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js +++ b/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js @@ -36,7 +36,7 @@ describe('when parsing a gitGraph', function () { parser.parse(str); const commits = parser.yy.getCommits(); - expect(parser.yy.getOptions()['key']).toBe('value'); + expect(parser.yy.getOptions().key).toBe('value'); expect(commits.size).toBe(1); expect(parser.yy.getCurrentBranch()).toBe('main'); expect(parser.yy.getDirection()).toBe('LR'); diff --git a/packages/mermaid/src/diagrams/pie/pieRenderer.ts b/packages/mermaid/src/diagrams/pie/pieRenderer.ts index 5a3698e1e..8f3b9cc5b 100644 --- a/packages/mermaid/src/diagrams/pie/pieRenderer.ts +++ b/packages/mermaid/src/diagrams/pie/pieRenderer.ts @@ -119,6 +119,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => { return ((datum.data.value / sum) * 100).toFixed(0) + '%'; }) .attr('transform', (datum: d3.PieArcDatum): string => { + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands return 'translate(' + labelArcGenerator.centroid(datum) + ')'; }) .style('text-anchor', 'middle') diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts index f1507a1b9..c7d478ed1 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantBuilder.ts @@ -127,7 +127,7 @@ export class QuadrantBuilder { private config: QuadrantBuilderConfig; private themeConfig: QuadrantBuilderThemeConfig; private data: QuadrantBuilderData; - private classes: Map = new Map(); + private classes = new Map(); constructor() { this.config = this.getDefaultConfig(); @@ -493,8 +493,8 @@ export class QuadrantBuilder { const props: QuadrantPointType = { x: xAxis(point.x), y: yAxis(point.y), - fill: point.color || this.themeConfig.quadrantPointFill, - radius: point.radius || this.config.pointRadius, + fill: point.color ?? this.themeConfig.quadrantPointFill, + radius: point.radius ?? this.config.pointRadius, text: { text: point.text, fill: this.themeConfig.quadrantPointTextFill, @@ -505,8 +505,8 @@ export class QuadrantBuilder { fontSize: this.config.pointLabelFontSize, rotation: 0, }, - strokeColor: point.strokeColor || this.themeConfig.quadrantPointFill, - strokeWidth: point.strokeWidth || '0px', + strokeColor: point.strokeColor ?? this.themeConfig.quadrantPointFill, + strokeWidth: point.strokeWidth ?? '0px', }; return props; }); diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts index c2295da4d..6d2435cd4 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantRenderer.ts @@ -46,10 +46,10 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram const group = svg.append('g').attr('class', 'main'); - const width = conf.quadrantChart?.chartWidth || 500; - const height = conf.quadrantChart?.chartHeight || 500; + const width = conf.quadrantChart?.chartWidth ?? 500; + const height = conf.quadrantChart?.chartHeight ?? 500; - configureSvgSize(svg, height, width, conf.quadrantChart?.useMaxWidth || true); + configureSvgSize(svg, height, width, conf.quadrantChart?.useMaxWidth ?? true); svg.attr('viewBox', '0 0 ' + width + ' ' + height); diff --git a/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts b/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts index 169aee873..007cda6f9 100644 --- a/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts +++ b/packages/mermaid/src/diagrams/sankey/parser/sankey.spec.ts @@ -13,7 +13,7 @@ describe('Sankey diagram', function () { sankey.parser.yy.clear(); }); - it('parses csv', async () => { + it('parses csv', () => { const csv = path.resolve(__dirname, './energy.csv'); const data = fs.readFileSync(csv, 'utf8'); const graphDefinition = prepareTextForParsing(cleanupComments('sankey-beta\n\n ' + data)); diff --git a/packages/mermaid/src/diagrams/sankey/sankeyDB.ts b/packages/mermaid/src/diagrams/sankey/sankeyDB.ts index 735ef045b..c3e1a9901 100644 --- a/packages/mermaid/src/diagrams/sankey/sankeyDB.ts +++ b/packages/mermaid/src/diagrams/sankey/sankeyDB.ts @@ -15,7 +15,7 @@ let links: SankeyLink[] = []; // Array of nodes guarantees their order let nodes: SankeyNode[] = []; // We also have to track nodes uniqueness (by ID) -let nodesMap: Map = new Map(); +let nodesMap = new Map(); const clear = (): void => { links = []; @@ -28,7 +28,7 @@ class SankeyLink { constructor( public source: SankeyNode, public target: SankeyNode, - public value: number = 0 + public value = 0 ) {} } diff --git a/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts b/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts index 51f808ff4..a981a346e 100644 --- a/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts +++ b/packages/mermaid/src/diagrams/sankey/sankeyRenderer.ts @@ -5,7 +5,6 @@ import { scaleOrdinal as d3scaleOrdinal, schemeTableau10 as d3schemeTableau10, } from 'd3'; - import type { SankeyNode as d3SankeyNode } from 'd3-sankey'; import { sankey as d3Sankey, @@ -41,7 +40,7 @@ const alignmentsMap: Record< export const draw = function (text: string, id: string, _version: string, diagObj: Diagram): void { // Get Sankey config const { securityLevel, sankey: conf } = getConfig(); - const defaultSankeyConfig = defaultConfig!.sankey!; + const defaultSankeyConfig = defaultConfig.sankey!; // TODO: // This code repeats for every diagram @@ -160,7 +159,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb .attr('class', 'link') .style('mix-blend-mode', 'multiply'); - const linkColor = conf?.linkColor || 'gradient'; + const linkColor = conf?.linkColor ?? 'gradient'; if (linkColor === 'gradient') { const gradient = link diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts index 19f6d561a..69ddeaf18 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts @@ -46,7 +46,7 @@ const state = new ImperativeState(() => ({ export const addBox = function (data: { text: string; color: string; wrap: boolean }) { state.records.boxes.push({ name: data.text, - wrap: (data.wrap === undefined && autoWrap()) || !!data.wrap, + wrap: data.wrap ?? autoWrap(), fill: data.color, actorKeys: [], }); @@ -80,18 +80,18 @@ export const addActor = function ( } // Don't allow null descriptions, either - if (description == null || description.text == null) { - description = { text: name, wrap: null, type }; + if (description?.text == null) { + description = { text: name, type }; } if (type == null || description.text == null) { - description = { text: name, wrap: null, type }; + description = { text: name, type }; } state.records.actors.set(id, { box: assignedBox, name: name, description: description.text, - wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap, + wrap: description.wrap ?? autoWrap(), prevActor: state.records.prevActor, links: {}, properties: {}, @@ -145,7 +145,7 @@ export const addMessage = function ( from: idFrom, to: idTo, message: message.text, - wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap, + wrap: message.wrap ?? autoWrap(), answer: answer, }); }; @@ -155,10 +155,10 @@ export const addSignal = function ( idTo?: Message['to'], message?: { text: string; wrap: boolean }, messageType?: number, - activate: boolean = false + activate = false ) { if (messageType === LINETYPE.ACTIVE_END) { - const cnt = activationCount(idFrom || ''); + const cnt = activationCount(idFrom ?? ''); if (cnt < 1) { // Bail out as there is an activation signal from an inactive participant const error = new Error('Trying to inactivate an inactive participant (' + idFrom + ')'); @@ -178,7 +178,7 @@ export const addSignal = function ( from: idFrom, to: idTo, message: message?.text ?? '', - wrap: (message?.wrap === undefined && autoWrap()) || !!message?.wrap, + wrap: message?.wrap ?? autoWrap(), type: messageType, activate, }); @@ -228,13 +228,24 @@ export const setWrap = function (wrapSetting?: boolean) { state.records.wrapEnabled = wrapSetting; }; +const extractWrap = (text?: string): { cleanedText?: string; wrap?: boolean } => { + if (text === undefined) { + return {}; + } + text = text.trim(); + const wrap = + /^:?wrap:/.exec(text) !== null ? true : /^:?nowrap:/.exec(text) !== null ? false : undefined; + const cleanedText = (wrap === undefined ? text : text.replace(/^:?(?:no)?wrap:/, '')).trim(); + return { cleanedText, wrap }; +}; + export const autoWrap = () => { // if setWrap has been called, use that value, otherwise use the value from the config // TODO: refactor, always use the config value let setWrap update the config value if (state.records.wrapEnabled !== undefined) { return state.records.wrapEnabled; } - return getConfig()?.sequence?.wrap; + return getConfig().sequence?.wrap ?? false; }; export const clear = function () { @@ -244,16 +255,12 @@ export const clear = function () { export const parseMessage = function (str: string) { const trimmedStr = str.trim(); + const { wrap, cleanedText } = extractWrap(trimmedStr); const message = { - text: trimmedStr.replace(/^:?(?:no)?wrap:/, '').trim(), - wrap: - trimmedStr.match(/^:?wrap:/) !== null - ? true - : trimmedStr.match(/^:?nowrap:/) !== null - ? false - : undefined, + text: cleanedText, + wrap, }; - log.debug(`parseMessage: ${message}`); + log.debug(`parseMessage: ${JSON.stringify(message)}`); return message; }; @@ -261,12 +268,12 @@ export const parseMessage = function (str: string) { // The color can be rgb,rgba,hsl,hsla, or css code names #hex codes are not supported for now because of the way the char # is handled // We extract first segment as color, the rest of the line is considered as text export const parseBoxData = function (str: string) { - const match = str.match(/^((?:rgba?|hsla?)\s*\(.*\)|\w*)(.*)$/); - let color = match != null && match[1] ? match[1].trim() : 'transparent'; - let title = match != null && match[2] ? match[2].trim() : undefined; + const match = /^((?:rgba?|hsla?)\s*\(.*\)|\w*)(.*)$/.exec(str); + let color = match?.[1] ? match[1].trim() : 'transparent'; + let title = match?.[2] ? match[2].trim() : undefined; // check that the string is a color - if (window && window.CSS) { + if (window?.CSS) { if (!window.CSS.supports('color', color)) { color = 'transparent'; title = str.trim(); @@ -279,21 +286,11 @@ export const parseBoxData = function (str: string) { title = str.trim(); } } - + const { wrap, cleanedText } = extractWrap(title); return { - color: color, - text: - title !== undefined - ? sanitizeText(title.replace(/^:?(?:no)?wrap:/, ''), getConfig()) - : undefined, - wrap: - title !== undefined - ? title.match(/^:?wrap:/) !== null - ? true - : title.match(/^:?nowrap:/) !== null - ? false - : undefined - : undefined, + text: cleanedText ? sanitizeText(cleanedText, getConfig()) : undefined, + color, + wrap, }; }; @@ -352,7 +349,7 @@ export const addNote = function ( actor: actor, placement: placement, message: message.text, - wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap, + wrap: message.wrap ?? autoWrap(), }; //@ts-ignore: Coerce actor into a [to, from, ...] array @@ -363,7 +360,7 @@ export const addNote = function ( from: actors[0], to: actors[1], message: message.text, - wrap: (message.wrap === undefined && autoWrap()) || !!message.wrap, + wrap: message.wrap ?? autoWrap(), type: LINETYPE.NOTE, placement: placement, }); @@ -461,12 +458,12 @@ export const addDetails = function (actorId: string, text: { text: string }) { const text = elem.innerHTML; const details = JSON.parse(text); // add the deserialized text to the actor's property field. - if (details['properties']) { - insertProperties(actor, details['properties']); + if (details.properties) { + insertProperties(actor, details.properties); } - if (details['links']) { - insertLinks(actor, details['links']); + if (details.links) { + insertLinks(actor, details.links); } } catch (e) { log.error('error while parsing actor details text', e); @@ -474,13 +471,14 @@ export const addDetails = function (actorId: string, text: { text: string }) { }; export const getActorProperty = function (actor: Actor, key: string) { - if (actor !== undefined && actor.properties !== undefined) { + if (actor?.properties !== undefined) { return actor.properties[key]; } return undefined; }; +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents export const apply = function (param: any | AddMessageParams | AddMessageParams[]) { if (Array.isArray(param)) { param.forEach(function (item) { @@ -544,7 +542,7 @@ export const apply = function (param: any | AddMessageParams | AddMessageParams[ if (param.to !== state.records.lastCreated) { throw new Error( 'The created participant ' + - state.records.lastCreated + + state.records.lastCreated.name + ' does not have an associated creating message after its declaration. Please check the sequence diagram.' ); } else { @@ -557,7 +555,7 @@ export const apply = function (param: any | AddMessageParams | AddMessageParams[ ) { throw new Error( 'The destroyed participant ' + - state.records.lastDestroyed + + state.records.lastDestroyed.name + ' does not have an associated destroying message after its declaration. Please check the sequence diagram.' ); } else { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js index 5f29b9f73..7f6b80ca5 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js +++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js @@ -1339,15 +1339,15 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com await mermaidAPI.parse(str); const actors = diagram.db.getActors(); - expect(actors.get('a').links['Repo']).toBe('https://repo.contoso.com/'); - expect(actors.get('b').links['Repo']).toBe(undefined); - expect(actors.get('a').links['Dashboard']).toBe('https://dashboard.contoso.com/'); - expect(actors.get('b').links['Dashboard']).toBe('https://dashboard.contoso.com/'); + expect(actors.get('a').links.Repo).toBe('https://repo.contoso.com/'); + expect(actors.get('b').links.Repo).toBe(undefined); + expect(actors.get('a').links.Dashboard).toBe('https://dashboard.contoso.com/'); + expect(actors.get('b').links.Dashboard).toBe('https://dashboard.contoso.com/'); expect(actors.get('a').links['On-Call']).toBe('https://oncall.contoso.com/?svc=alice'); - expect(actors.get('c').links['Dashboard']).toBe(undefined); - expect(actors.get('a').links['Endpoint']).toBe('https://alice.contoso.com'); - expect(actors.get('a').links['Swagger']).toBe('https://swagger.contoso.com'); - expect(actors.get('a').links['Tests']).toBe('https://tests.contoso.com/?svc=alice@contoso.com'); + expect(actors.get('c').links.Dashboard).toBe(undefined); + expect(actors.get('a').links.Endpoint).toBe('https://alice.contoso.com'); + expect(actors.get('a').links.Swagger).toBe('https://swagger.contoso.com'); + expect(actors.get('a').links.Tests).toBe('https://tests.contoso.com/?svc=alice@contoso.com'); }); it('should handle properties EXPERIMENTAL: USE WITH CAUTION', async () => { @@ -1363,11 +1363,11 @@ properties b: {"class": "external-service-actor", "icon": "@computer"} await mermaidAPI.parse(str); const actors = diagram.db.getActors(); - expect(actors.get('a').properties['class']).toBe('internal-service-actor'); - expect(actors.get('b').properties['class']).toBe('external-service-actor'); - expect(actors.get('a').properties['icon']).toBe('@clock'); - expect(actors.get('b').properties['icon']).toBe('@computer'); - expect(actors.get('c').properties['class']).toBe(undefined); + expect(actors.get('a').properties.class).toBe('internal-service-actor'); + expect(actors.get('b').properties.class).toBe('external-service-actor'); + expect(actors.get('a').properties.icon).toBe('@clock'); + expect(actors.get('b').properties.icon).toBe('@computer'); + expect(actors.get('c').properties.class).toBe(undefined); }); it('should handle box', async () => { @@ -1519,7 +1519,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { diagram.renderer.bounds.init(); conf = diagram.db.getConfig(); }); - it('should handle a simple bound call', async () => { + it('should handle a simple bound call', () => { diagram.renderer.bounds.insert(100, 100, 200, 200); const { bounds } = diagram.renderer.bounds.getBounds(); @@ -1528,7 +1528,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { expect(bounds.stopx).toBe(200); expect(bounds.stopy).toBe(200); }); - it('should handle an expanding bound', async () => { + it('should handle an expanding bound', () => { diagram.renderer.bounds.insert(100, 100, 200, 200); diagram.renderer.bounds.insert(25, 50, 300, 400); @@ -1538,7 +1538,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { expect(bounds.stopx).toBe(300); expect(bounds.stopy).toBe(400); }); - it('should handle inserts within the bound without changing the outer bounds', async () => { + it('should handle inserts within the bound without changing the outer bounds', () => { diagram.renderer.bounds.insert(100, 100, 200, 200); diagram.renderer.bounds.insert(25, 50, 300, 400); diagram.renderer.bounds.insert(125, 150, 150, 200); @@ -1549,7 +1549,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { expect(bounds.stopx).toBe(300); expect(bounds.stopy).toBe(400); }); - it('should handle a loop without expanding the area', async () => { + it('should handle a loop without expanding the area', () => { diagram.renderer.bounds.insert(25, 50, 300, 400); diagram.renderer.bounds.verticalPos = 150; diagram.renderer.bounds.newLoop(); @@ -1570,7 +1570,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { expect(bounds.stopx).toBe(300); expect(bounds.stopy).toBe(400); }); - it('should handle multiple loops withtout expanding the bounds', async () => { + it('should handle multiple loops withtout expanding the bounds', () => { diagram.renderer.bounds.insert(100, 100, 1000, 1000); diagram.renderer.bounds.verticalPos = 200; diagram.renderer.bounds.newLoop(); @@ -1601,7 +1601,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { expect(bounds.stopx).toBe(1000); expect(bounds.stopy).toBe(1000); }); - it('should handle a loop that expands the area', async () => { + it('should handle a loop that expands the area', () => { diagram.renderer.bounds.insert(100, 100, 200, 200); diagram.renderer.bounds.verticalPos = 200; diagram.renderer.bounds.newLoop(); diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index d9fbb16c5..5299f1b1e 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -495,7 +495,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO } }; -const addActorRenderingData = async function ( +const addActorRenderingData = function ( diagram, actors, createdActors: Map, @@ -822,7 +822,7 @@ export const draw = async function (_text: string, id: string, _version: string, actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey)); } - await addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false); + addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false); const loopWidths = await calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj); // The arrow head definition is attached to the svg once @@ -1076,7 +1076,7 @@ export const draw = async function (_text: string, id: string, _version: string, box.stopx = box.startx + box.width; box.stopy = box.starty + box.height; box.stroke = 'rgb(0,0,0, 0.5)'; - await svgDraw.drawBox(diagram, box, conf); + svgDraw.drawBox(diagram, box, conf); } if (hasBoxes) { @@ -1147,7 +1147,7 @@ async function getMaxMessageWidthPerActor( actors: Map, messages: any[], diagObj: Diagram -): Promise<{ [id: string]: number }> { +): Promise> { const maxMessageWidthPerActor = {}; for (const msg of messages) { @@ -1581,7 +1581,7 @@ const calculateLoopBounds = async function (messages, actors, _maxWidthPerActor, const lastActorActivationIdx = bounds.activations .map((a) => a.actor) .lastIndexOf(msg.from); - delete bounds.activations.splice(lastActorActivationIdx, 1)[0]; + bounds.activations.splice(lastActorActivationIdx, 1).splice(0, 1); } break; } diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index bcc06602f..51968ef9f 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -115,6 +115,7 @@ export const drawKatex = async function (elem, textData, msgModel = null) { stopx = temp; } + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands textElem.attr('x', Math.round(startx + Math.abs(startx - stopx) / 2 - dim.width / 2)); if (textData.class === 'loopText') { textElem.attr('y', Math.round(starty)); @@ -325,7 +326,7 @@ export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => { * @param {any} conf - DrawText implementation discriminator object * @param {boolean} isFooter - If the actor is the footer one */ -const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) { +const drawActorTypeParticipant = function (elem, actor, conf, isFooter) { const actorY = isFooter ? actor.stopy : actor.starty; const center = actor.x + actor.width / 2; const centerY = actorY + 5; @@ -359,8 +360,8 @@ const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) { const rect = svgDrawCommon.getNoteRect(); var cssclass = 'actor'; - if (actor.properties != null && actor.properties['class']) { - cssclass = actor.properties['class']; + if (actor.properties?.class) { + cssclass = actor.properties.class; } else { rect.fill = '#eaeaea'; } @@ -380,8 +381,8 @@ const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) { const rectElem = drawRect(g, rect); actor.rectData = rect; - if (actor.properties != null && actor.properties['icon']) { - const iconSrc = actor.properties['icon'].trim(); + if (actor.properties?.icon) { + const iconSrc = actor.properties.icon.trim(); if (iconSrc.charAt(0) === '@') { svgDrawCommon.drawEmbeddedImage(g, rect.x + rect.width - 20, rect.y + 10, iconSrc.substr(1)); } else { @@ -389,7 +390,7 @@ const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) { } } - await _drawTextCandidateFunc(conf, hasKatex(actor.description))( + _drawTextCandidateFunc(conf, hasKatex(actor.description))( actor.description, g, rect.x, @@ -410,7 +411,7 @@ const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) { return height; }; -const drawActorTypeActor = async function (elem, actor, conf, isFooter) { +const drawActorTypeActor = function (elem, actor, conf, isFooter) { const actorY = isFooter ? actor.stopy : actor.starty; const center = actor.x + actor.width / 2; const centerY = actorY + 80; @@ -491,7 +492,7 @@ const drawActorTypeActor = async function (elem, actor, conf, isFooter) { const bounds = actElem.node().getBBox(); actor.height = bounds.height; - await _drawTextCandidateFunc(conf, hasKatex(actor.description))( + _drawTextCandidateFunc(conf, hasKatex(actor.description))( actor.description, actElem, rect.x, @@ -514,12 +515,12 @@ export const drawActor = async function (elem, actor, conf, isFooter) { } }; -export const drawBox = async function (elem, box, conf) { +export const drawBox = function (elem, box, conf) { const boxplusTextGroup = elem.append('g'); const g = boxplusTextGroup; drawBackgroundRect(g, box); if (box.name) { - await _drawTextCandidateFunc(conf)( + _drawTextCandidateFunc(conf)( box.name, g, box.x, diff --git a/packages/mermaid/src/diagrams/sequence/types.ts b/packages/mermaid/src/diagrams/sequence/types.ts index 5cc6ae249..10c1c8ed3 100644 --- a/packages/mermaid/src/diagrams/sequence/types.ts +++ b/packages/mermaid/src/diagrams/sequence/types.ts @@ -78,8 +78,7 @@ export interface AddMessageParams { | 'breakEnd' | 'parOverStart' | 'parOverEnd' - | 'parOverAnd' - | 'parOverEnd'; + | 'parOverAnd'; activate: boolean; } diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js index 85c09c536..836b6fe07 100644 --- a/packages/mermaid/src/diagrams/state/stateDb.js +++ b/packages/mermaid/src/diagrams/state/stateDb.js @@ -465,7 +465,7 @@ export const addStyleClass = function (id, styleAttributes = '') { const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim(); // replace some style keywords - if (attrib.match(COLOR_KEYWORD)) { + if (RegExp(COLOR_KEYWORD).exec(attrib)) { const newStyle1 = fixedAttrib.replace(FILL_KEYWORD, BG_FILL); const newStyle2 = newStyle1.replace(COLOR_KEYWORD, FILL_KEYWORD); foundClass.textStyles.push(newStyle2); diff --git a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js index 0ecc0a73a..acb10427b 100644 --- a/packages/mermaid/src/diagrams/state/stateRenderer-v2.js +++ b/packages/mermaid/src/diagrams/state/stateRenderer-v2.js @@ -362,8 +362,7 @@ const setupDoc = (g, parentParsedItem, doc, diagramStates, diagramDb, altFlag) = const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => { let dir = defaultDir; if (parsedItem.doc) { - for (let i = 0; i < parsedItem.doc.length; i++) { - const parsedItemDoc = parsedItem.doc[i]; + for (const parsedItemDoc of parsedItem.doc) { if (parsedItemDoc.stmt === 'dir') { dir = parsedItemDoc.value; } diff --git a/packages/mermaid/src/diagrams/timeline/svgDraw.js b/packages/mermaid/src/diagrams/timeline/svgDraw.js index c5fc2a3c4..723dc46f1 100644 --- a/packages/mermaid/src/diagrams/timeline/svgDraw.js +++ b/packages/mermaid/src/diagrams/timeline/svgDraw.js @@ -515,8 +515,7 @@ export const drawNode = function (elem, node, fullSection, conf) { .attr('text-anchor', 'middle') .call(wrap, node.width); const bbox = txt.node().getBBox(); - const fontSize = - conf.fontSize && conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize; + const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize; node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding; node.height = Math.max(node.height, node.maxHeight); node.width = node.width + 2 * node.padding; @@ -540,8 +539,7 @@ export const getVirtualNodeHeight = function (elem, node, conf) { .attr('text-anchor', 'middle') .call(wrap, node.width); const bbox = txt.node().getBBox(); - const fontSize = - conf.fontSize && conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize; + const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize; textElem.remove(); return bbox.height + fontSize * 1.1 * 0.5 + node.padding; }; diff --git a/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts b/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts index 2f1f15689..b3405bc1b 100644 --- a/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts +++ b/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts @@ -115,8 +115,7 @@ export const draw = function (text: string, id: string, version: string, diagObj maxEventCount = Math.max(maxEventCount, task.events.length); //calculate maxEventLineLength let maxEventLineLengthTemp = 0; - for (let j = 0; j < task.events.length; j++) { - const event = task.events[j]; + for (const event of task.events) { const eventNode = { descr: event, section: task.section, diff --git a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/bandAxis.ts b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/bandAxis.ts index 864ef1316..98eb31235 100644 --- a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/bandAxis.ts +++ b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/bandAxis.ts @@ -40,6 +40,6 @@ export class BandAxis extends BaseAxis { } getScaleValue(value: string): number { - return this.scale(value) || this.getRange()[0]; + return this.scale(value) ?? this.getRange()[0]; } } diff --git a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/baseAxis.ts b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/baseAxis.ts index c3240a4a7..ef60cc85f 100644 --- a/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/baseAxis.ts +++ b/packages/mermaid/src/diagrams/xychart/chartBuilder/components/axis/baseAxis.ts @@ -58,7 +58,7 @@ export abstract class BaseAxis implements Axis { abstract recalculateScale(): void; - abstract getTickValues(): Array; + abstract getTickValues(): (string | number)[]; getTickDistance(): number { const range = this.getRange(); diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index ad81e9e0b..940fc6940 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -9,9 +9,9 @@ const allMarkdownTransformers: MarkdownOptions = { light: 'github-light', dark: 'github-dark', }, - // eslint-disable-next-line @typescript-eslint/no-misused-promises - config: async (md) => { - await MermaidExample(md); + + config: (md) => { + MermaidExample(md); }, }; diff --git a/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts b/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts index 64a069b4c..d1aeee5ac 100644 --- a/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts +++ b/packages/mermaid/src/docs/.vitepress/mermaid-markdown-all.ts @@ -1,6 +1,6 @@ import type { MarkdownRenderer } from 'vitepress'; -const MermaidExample = async (md: MarkdownRenderer) => { +const MermaidExample = (md: MarkdownRenderer) => { const defaultRenderer = md.renderer.rules.fence; if (!defaultRenderer) { diff --git a/packages/mermaid/src/docs/.vitepress/theme/index.ts b/packages/mermaid/src/docs/.vitepress/theme/index.ts index dae9fd8ff..3ce3aea23 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/index.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/index.ts @@ -35,7 +35,7 @@ export default { const url = new URL(window.location.origin + to); const newPath = getRedirect(url); if (newPath) { - console.log(`Redirecting to ${newPath} from ${window.location}`); + console.log(`Redirecting to ${newPath} from ${window.location.toString()}`); // router.go isn't loading the ID properly. window.location.href = `/${newPath}`; } diff --git a/packages/mermaid/src/docs/vite.config.ts b/packages/mermaid/src/docs/vite.config.ts index ed5f4bab9..399e5c65e 100644 --- a/packages/mermaid/src/docs/vite.config.ts +++ b/packages/mermaid/src/docs/vite.config.ts @@ -49,12 +49,12 @@ export default defineConfig({ // TODO: will be fixed in the next vitepress release. name: 'fix-virtual', - async resolveId(id: string) { + resolveId(id: string) { if (id === virtualModuleId) { return resolvedVirtualModuleId; } }, - async load(this, id: string) { + load(this, id: string) { if (id === resolvedVirtualModuleId) { return `export default ${JSON.stringify({ securityLevel: 'loose', diff --git a/packages/mermaid/src/logger.ts b/packages/mermaid/src/logger.ts index d69615b3d..cffd2112c 100644 --- a/packages/mermaid/src/logger.ts +++ b/packages/mermaid/src/logger.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable no-console */ @@ -28,12 +29,11 @@ export const log: Record = { * * @param level - The level to set the logging to. Default is `"fatal"` */ -export const setLogLevel = function (level: keyof typeof LEVELS | number | string = 'fatal') { +export const setLogLevel = function (level: keyof typeof LEVELS | number = 'fatal') { let numericLevel: number = LEVELS.fatal; if (typeof level === 'string') { - level = level.toLowerCase(); - if (level in LEVELS) { - numericLevel = LEVELS[level as keyof typeof LEVELS]; + if (level.toLowerCase() in LEVELS) { + numericLevel = LEVELS[level]; } } else if (typeof level === 'number') { numericLevel = level; diff --git a/packages/mermaid/src/mermaid.spec.ts b/packages/mermaid/src/mermaid.spec.ts index d03f0ee9d..586d3605c 100644 --- a/packages/mermaid/src/mermaid.spec.ts +++ b/packages/mermaid/src/mermaid.spec.ts @@ -5,7 +5,7 @@ import { addDiagrams } from './diagram-api/diagram-orchestration.js'; import { beforeAll, describe, it, expect, vi, afterEach } from 'vitest'; import type { DiagramDefinition } from './diagram-api/types.js'; -beforeAll(async () => { +beforeAll(() => { addDiagrams(); }); const spyOn = vi.spyOn; @@ -18,7 +18,7 @@ afterEach(() => { describe('when using mermaid and ', () => { describe('when detecting chart type ', () => { - it('should not start rendering with mermaid.startOnLoad set to false', async () => { + it('should not start rendering with mermaid.startOnLoad set to false', () => { mermaid.startOnLoad = false; document.body.innerHTML = '
graph TD;\na;
'; spyOn(mermaid, 'run'); @@ -26,7 +26,7 @@ describe('when using mermaid and ', () => { expect(mermaid.run).not.toHaveBeenCalled(); }); - it('should start rendering with both startOnLoad set', async () => { + it('should start rendering with both startOnLoad set', () => { mermaid.startOnLoad = true; document.body.innerHTML = '
graph TD;\na;
'; spyOn(mermaid, 'run'); @@ -34,7 +34,7 @@ describe('when using mermaid and ', () => { expect(mermaid.run).toHaveBeenCalled(); }); - it('should start rendering with mermaid.startOnLoad', async () => { + it('should start rendering with mermaid.startOnLoad', () => { mermaid.startOnLoad = true; document.body.innerHTML = '
graph TD;\na;
'; spyOn(mermaid, 'run'); @@ -42,7 +42,7 @@ describe('when using mermaid and ', () => { expect(mermaid.run).toHaveBeenCalled(); }); - it('should start rendering as a default with no changes performed', async () => { + it('should start rendering as a default with no changes performed', () => { document.body.innerHTML = '
graph TD;\na;
'; spyOn(mermaid, 'run'); mermaid.contentLoaded(); @@ -74,7 +74,7 @@ describe('when using mermaid and ', () => { [ { id: 'dummyError', - detector: (text) => /dummyError/.test(text), + detector: (text) => text.includes('dummyError'), loader: () => Promise.reject('dummyError'), }, ], @@ -114,7 +114,7 @@ describe('when using mermaid and ', () => { [ { id: 'dummy', - detector: (text) => /dummy/.test(text), + detector: (text) => text.includes('dummy'), loader: () => { loaded = true; return Promise.resolve({ @@ -133,7 +133,7 @@ describe('when using mermaid and ', () => { [ { id: 'dummy2', - detector: (text) => /dummy2/.test(text), + detector: (text) => text.includes('dummy2'), loader: () => { loaded = true; return Promise.resolve({ diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index 41ec0c984..7958f397e 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -606,26 +606,26 @@ describe('mermaidAPI', () => { let error: any = { message: '' }; try { // @ts-ignore This is a read-only property. Typescript will not allow assignment, but regular javascript might. - mermaidAPI['defaultConfig'] = config; + mermaidAPI.defaultConfig = config; } catch (e) { error = e; } expect(error.message).toBe( "Cannot assign to read only property 'defaultConfig' of object '#'" ); - expect(mermaidAPI.defaultConfig['logLevel']).toBe(5); + expect(mermaidAPI.defaultConfig.logLevel).toBe(5); }); it('prevents changes to global defaults (direct)', () => { let error: any = { message: '' }; try { - mermaidAPI.defaultConfig['logLevel'] = 0; + mermaidAPI.defaultConfig.logLevel = 0; } catch (e) { error = e; } expect(error.message).toBe( "Cannot assign to read only property 'logLevel' of object '#'" ); - expect(mermaidAPI.defaultConfig['logLevel']).toBe(5); + expect(mermaidAPI.defaultConfig.logLevel).toBe(5); }); it('prevents sneaky changes to global defaults (assignWithDepth)', () => { const config = { @@ -640,7 +640,7 @@ describe('mermaidAPI', () => { expect(error.message).toBe( "Cannot assign to read only property 'logLevel' of object '#'" ); - expect(mermaidAPI.defaultConfig['logLevel']).toBe(5); + expect(mermaidAPI.defaultConfig.logLevel).toBe(5); }); }); @@ -648,7 +648,7 @@ describe('mermaidAPI', () => { it('allows dompurify config to be set', () => { mermaidAPI.initialize({ dompurifyConfig: { ADD_ATTR: ['onclick'] } }); - expect(mermaidAPI!.getConfig()!.dompurifyConfig!.ADD_ATTR).toEqual(['onclick']); + expect(mermaidAPI.getConfig().dompurifyConfig!.ADD_ATTR).toEqual(['onclick']); }); }); diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index ffd7d4bea..7bca4d995 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -128,7 +128,7 @@ export const createCssStyles = ( // classDefs defined in the diagram text if (classDefs instanceof Map) { - const htmlLabels = config.htmlLabels || config.flowchart?.htmlLabels; // TODO why specifically check the Flowchart diagram config? + const htmlLabels = config.htmlLabels ?? config.flowchart?.htmlLabels; // TODO why specifically check the Flowchart diagram config? const cssHtmlElements = ['> *', 'span']; // TODO make a constant const cssShapeElements = ['rect', 'polygon', 'ellipse', 'circle', 'path']; // TODO make a constant diff --git a/packages/mermaid/src/styles.spec.ts b/packages/mermaid/src/styles.spec.ts index 698b2beaf..70e9e7ec5 100644 --- a/packages/mermaid/src/styles.spec.ts +++ b/packages/mermaid/src/styles.spec.ts @@ -31,7 +31,7 @@ import packet from './diagrams/packet/styles.js'; import block from './diagrams/block/styles.js'; import themes from './themes/index.js'; -async function checkValidStylisCSSStyleSheet(stylisString: string) { +function checkValidStylisCSSStyleSheet(stylisString: string) { const cssString = serialize(compile(`#my-svg-id{${stylisString}}`), stringify); const errors = validate(cssString, 'this-file-was-created-by-tests.css') as Error[]; @@ -51,6 +51,7 @@ async function checkValidStylisCSSStyleSheet(stylisString: string) { if (unexpectedErrors.length > 0) { throw new Error( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `The given CSS string was invalid: ${errors}.\n\n` + 'Copy the below CSS into https://jigsaw.w3.org/css-validator/validator to help debug where the invalid CSS is:\n\n' + `Original CSS value was ${cssString}` @@ -75,7 +76,7 @@ describe('styles', () => { const styles = getStyles(diagramType, '', getConfig().themeVariables); - await checkValidStylisCSSStyleSheet(styles); + checkValidStylisCSSStyleSheet(styles); }); /** @@ -110,7 +111,7 @@ describe('styles', () => { themes[themeId].getThemeVariables() ); - await checkValidStylisCSSStyleSheet(styles); + checkValidStylisCSSStyleSheet(styles); }); } } diff --git a/packages/mermaid/src/styles.ts b/packages/mermaid/src/styles.ts index fde079450..50d502bac 100644 --- a/packages/mermaid/src/styles.ts +++ b/packages/mermaid/src/styles.ts @@ -17,8 +17,8 @@ const getStyles = ( } & FlowChartStyleOptions ) => { let diagramStyles = ''; - if (type in themes && themes[type as keyof typeof themes]) { - diagramStyles = themes[type as keyof typeof themes](options); + if (type in themes && themes[type]) { + diagramStyles = themes[type](options); } else { log.warn(`No theme found for ${type}`); } diff --git a/packages/mermaid/src/tests/MockedD3.ts b/packages/mermaid/src/tests/MockedD3.ts index 2f00e4924..35871f14e 100644 --- a/packages/mermaid/src/tests/MockedD3.ts +++ b/packages/mermaid/src/tests/MockedD3.ts @@ -60,7 +60,7 @@ export class MockedD3 { if (beforeSelector === undefined) { this._children.push(newMock); } else { - const idOnly = beforeSelector[0] == '#' ? beforeSelector.substring(1) : beforeSelector; + const idOnly = beforeSelector.startsWith('#') ? beforeSelector.substring(1) : beforeSelector; const foundIndex = this._children.findIndex((child) => child.id === idOnly); if (foundIndex < 0) { this._children.push(newMock); diff --git a/packages/mermaid/src/themes/theme-neutral.js b/packages/mermaid/src/themes/theme-neutral.js index 4134a985b..40963839e 100644 --- a/packages/mermaid/src/themes/theme-neutral.js +++ b/packages/mermaid/src/themes/theme-neutral.js @@ -156,8 +156,8 @@ class Theme { // Setup the label color for the set this.scaleLabelColor = this.scaleLabelColor || (this.darkMode ? 'black' : this.labelTextColor); - this['cScaleLabel0'] = this['cScaleLabel0'] || this.cScale1; - this['cScaleLabel2'] = this['cScaleLabel2'] || this.cScale1; + this.cScaleLabel0 = this.cScaleLabel0 || this.cScale1; + this.cScaleLabel2 = this.cScaleLabel2 || this.cScale1; for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) { this['cScaleLabel' + i] = this['cScaleLabel' + i] || this.scaleLabelColor; } diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 0f7fec61e..386c10738 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -177,11 +177,7 @@ export const detectDirective = function ( if (match.index === directiveRegex.lastIndex) { directiveRegex.lastIndex++; } - if ( - (match && !type) || - (type && match[1] && match[1].match(type)) || - (type && match[2] && match[2].match(type)) - ) { + if ((match && !type) || (type && match[1]?.match(type)) || (type && match[2]?.match(type))) { const type = match[1] ? match[1] : match[2]; const args = match[3] ? match[3].trim() : match[4] ? JSON.parse(match[4].trim()) : null; result.push({ type, args }); diff --git a/packages/mermaid/src/utils/lineWithOffset.ts b/packages/mermaid/src/utils/lineWithOffset.ts index af0cd3b46..114dda2bd 100644 --- a/packages/mermaid/src/utils/lineWithOffset.ts +++ b/packages/mermaid/src/utils/lineWithOffset.ts @@ -45,7 +45,12 @@ export const getLineFunctionsWithOffset = ( edge: Pick ) => { return { - x: function (d: Point | [number, number], i: number, data: (Point | [number, number])[]) { + x: function ( + this: void, + d: Point | [number, number], + i: number, + data: (Point | [number, number])[] + ) { let offset = 0; if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) { // Handle first point @@ -70,7 +75,12 @@ export const getLineFunctionsWithOffset = ( } return pointTransformer(d).x + offset; }, - y: function (d: Point | [number, number], i: number, data: (Point | [number, number])[]) { + y: function ( + this: void, + d: Point | [number, number], + i: number, + data: (Point | [number, number])[] + ) { // Same handling as X above let offset = 0; if (i === 0 && Object.hasOwn(markerOffsets, edge.arrowTypeStart)) { diff --git a/packages/parser/src/language/common/tokenBuilder.ts b/packages/parser/src/language/common/tokenBuilder.ts index f99763454..1511dd390 100644 --- a/packages/parser/src/language/common/tokenBuilder.ts +++ b/packages/parser/src/language/common/tokenBuilder.ts @@ -20,6 +20,7 @@ export abstract class AbstractMermaidTokenBuilder extends DefaultTokenBuilder { // to restrict users, they mustn't have any non-whitespace characters after the keyword. tokenTypes.forEach((tokenType: TokenType): void => { if (this.keywords.has(tokenType.name) && tokenType.PATTERN !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-base-to-string tokenType.PATTERN = new RegExp(tokenType.PATTERN.toString() + '(?:(?=%%)|(?!\\S))'); } }); diff --git a/packages/parser/src/parse.ts b/packages/parser/src/parse.ts index 577a1cea6..992b96506 100644 --- a/packages/parser/src/parse.ts +++ b/packages/parser/src/parse.ts @@ -9,17 +9,17 @@ const initializers = { info: async () => { const { createInfoServices } = await import('./language/info/index.js'); const parser = createInfoServices().Info.parser.LangiumParser; - parsers['info'] = parser; + parsers.info = parser; }, packet: async () => { const { createPacketServices } = await import('./language/packet/index.js'); const parser = createPacketServices().Packet.parser.LangiumParser; - parsers['packet'] = parser; + parsers.packet = parser; }, pie: async () => { const { createPieServices } = await import('./language/pie/index.js'); const parser = createPieServices().Pie.parser.LangiumParser; - parsers['pie'] = parser; + parsers.pie = parser; }, } as const;