From b5e58f407683d33afa295da348bf8d4f1b64d31e Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 27 Nov 2023 10:57:04 +0530 Subject: [PATCH] refactor: Convert flowDB to TS --- .../flowchart/{flowDb.js => flowDb.ts} | 301 ++++++++++-------- 1 file changed, 163 insertions(+), 138 deletions(-) rename packages/mermaid/src/diagrams/flowchart/{flowDb.js => flowDb.ts} (74%) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.ts similarity index 74% rename from packages/mermaid/src/diagrams/flowchart/flowDb.js rename to packages/mermaid/src/diagrams/flowchart/flowDb.ts index 9a693aabf..97bf35058 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -12,39 +12,90 @@ import { setDiagramTitle, getDiagramTitle, } from '../common/commonDb.js'; -import errorDiagram from '../error/errorDiagram.js'; + +const MAX_EDGE_COUNT = 280; + +interface FlowVertex { + id: string; + labelType: 'text'; + dir?: string; + props?: any; + type?: string; + text?: string; + link?: string; + linkTarget?: string; + haveCallback?: boolean; + domId: string; + styles: any[]; + classes: any[]; +} + +interface FlowText { + text: string; + type: 'text'; +} + +interface FlowEdge { + start: string; + end: string; + type?: string; + stroke?: string; + length?: number; + text: string; + labelType: 'text'; +} + +interface FlowClass { + id: string; + styles: string[]; + textStyles: string[]; +} + +interface FlowSubGraph { + id: string; + nodes: string[]; + title: string; + classes: string[]; + dir?: string; + labelType: string; +} + +interface FlowLink { + type: string; + stroke: string; + length?: number; +} const MERMAID_DOM_ID_PREFIX = 'flowchart-'; let vertexCounter = 0; let config = getConfig(); -let vertices = {}; -let edges = []; -let classes = {}; -let subGraphs = []; -let subGraphLookup = {}; -let tooltips = {}; +let vertices: Record = {}; +let edges: any[] & { defaultInterpolate?: string; defaultStyle?: string } = []; +let classes: Record = {}; +let subGraphs: FlowSubGraph[] = []; +let subGraphLookup: Record = {}; +let tooltips: Record = {}; let subCount = 0; let firstGraphFlag = true; -let direction; +let direction: string; -let version; // As in graph +let version: string; // As in graph // Functions to be run after graph rendering -let funs = []; +let funs: ((element: HTMLElement) => void)[] = []; -const sanitizeText = (txt) => common.sanitizeText(txt, config); +const sanitizeText = (txt: string) => common.sanitizeText(txt, config); /** * Function to lookup domId from id in the graph definition. * - * @param id - * @public + * @param id - id of the node */ -export const lookUpDomId = function (id) { - const veritceKeys = Object.keys(vertices); - for (const veritceKey of veritceKeys) { - if (vertices[veritceKey].id === id) { - return vertices[veritceKey].domId; +export const lookUpDomId = function (id: string) { + const vertexKeys = Object.keys(vertices); + for (const vertexKey of vertexKeys) { + if (vertices[vertexKey].id === id) { + return vertices[vertexKey].domId; } } return id; @@ -53,18 +104,18 @@ export const lookUpDomId = function (id) { /** * Function called by parser when a node definition has been found * - * @param _id - * @param text - * @param textObj - * @param type - * @param style - * @param classes - * @param dir - * @param props */ -export const addVertex = function (_id, textObj, type, style, classes, dir, props = {}) { +export const addVertex = function ( + _id: string, + textObj: FlowText, + type: 'group', + style: any[], + classes: any[], + dir: string, + props = {} +) { let txt; - let id = _id; + const id = _id; if (id === undefined) { return; } @@ -72,8 +123,6 @@ export const addVertex = function (_id, textObj, type, style, classes, dir, prop return; } - // if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id; - if (vertices[id] === undefined) { vertices[id] = { id: id, @@ -125,20 +174,15 @@ export const addVertex = function (_id, textObj, type, style, classes, dir, prop /** * Function called by parser when a link/edge definition has been found * - * @param _start - * @param _end - * @param type - * @param linkText - * @param linkTextObj */ -export const addSingleLink = function (_start, _end, type) { - let start = _start; - let end = _end; +export const addSingleLink = function (_start: string, _end: string, type: any) { + const start = _start; + const end = _end; // if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start; // if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end; // log.info('Got edge...', start, end); - const edge = { start: start, end: end, type: undefined, text: '', labelType: 'text' }; + const edge: FlowEdge = { start: start, end: end, type: undefined, text: '', labelType: 'text' }; log.info('abc78 Got edge...', edge); const linkTextObj = type.text; @@ -155,24 +199,22 @@ export const addSingleLink = function (_start, _end, type) { if (type !== undefined) { edge.type = type.type; edge.stroke = type.stroke; - edge.length = type.length; + edge.length = type.length > 10 ? 10 : type.length; } - if (edge?.length > 10) { - edge.length = 10; - } - if (edges.length < 280) { - log.info('abc78 pushing edge...'); - edges.push(edge); - } else { + + if (edges.length > MAX_EDGE_COUNT) { throw new Error('Too many edges'); } + + log.info('Pushing edge...'); + edges.push(edge); }; -export const addLink = function (_start, _end, type) { - log.info('addLink (abc78)', _start, _end, type); - let i, j; - for (i = 0; i < _start.length; i++) { - for (j = 0; j < _end.length; j++) { - addSingleLink(_start[i], _end[j], type); + +export const addLink = function (_start: string[], _end: string[], type: any) { + log.info('addLink', _start, _end, type); + for (const start of _start) { + for (const end of _end) { + addSingleLink(start, end, type); } } }; @@ -180,15 +222,16 @@ export const addLink = function (_start, _end, type) { /** * Updates a link's line interpolation algorithm * - * @param positions - * @param interp */ -export const updateLinkInterpolate = function (positions, interp) { +export const updateLinkInterpolate = function ( + positions: ('default' | number)[], + interpolate: string +) { positions.forEach(function (pos) { if (pos === 'default') { - edges.defaultInterpolate = interp; + edges.defaultInterpolate = interpolate; } else { - edges[pos].interpolate = interp; + edges[pos].interpolate = interpolate; } }); }; @@ -196,12 +239,10 @@ export const updateLinkInterpolate = function (positions, interp) { /** * Updates a link with a style * - * @param positions - * @param style */ -export const updateLink = function (positions, style) { +export const updateLink = function (positions: ('default' | number)[], style: any) { positions.forEach(function (pos) { - if (pos >= edges.length) { + if (typeof pos === 'number' && pos >= edges.length) { throw new Error( `The index ${pos} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${ edges.length - 1 @@ -219,7 +260,7 @@ export const updateLink = function (positions, style) { }); }; -export const addClass = function (ids, style) { +export const addClass = function (ids: string, style: string[]) { ids.split(',').forEach(function (id) { if (classes[id] === undefined) { classes[id] = { id, styles: [], textStyles: [] }; @@ -240,9 +281,8 @@ export const addClass = function (ids, style) { /** * Called by parser when a graph definition is found, stores the direction of the chart. * - * @param dir */ -export const setDirection = function (dir) { +export const setDirection = function (dir: string) { direction = dir; if (direction.match(/.* { + +export const setGen = (ver: string) => { version = ver || 'gen-2'; }; -/** @returns {string} */ + export const defaultStyle = function () { return 'fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;'; }; -/** - * Clears the internal graph db so that a new graph can be parsed. - * - * @param _id - * @param list - * @param _title - */ -export const addSubGraph = function (_id, list, _title) { - let id = _id.text.trim(); +export const addSubGraph = function ( + _id: { text: string }, + list: string[], + _title: { text: string; type: string } +) { + let id: string | undefined = _id.text.trim(); let title = _title.text; if (_id === _title && _title.text.match(/\s/)) { id = undefined; } - /** @param a */ - function uniq(a) { - const prims = { boolean: {}, number: {}, string: {} }; - const objs = []; + + function uniq(a: any[]) { + const prims: any = { boolean: {}, number: {}, string: {} }; + const objs: any[] = []; let dir; // = undefined; direction.trim(); const nodeList = a.filter(function (item) { @@ -507,9 +544,9 @@ export const addSubGraph = function (_id, list, _title) { return { nodeList, dir }; } - let nodeList = []; + let nodeList: string[] = []; - const { nodeList: nl, dir } = uniq(nodeList.concat.apply(nodeList, list)); + const { nodeList: nl, dir } = uniq([...nodeList, ...list]); nodeList = nl; if (version === 'gen-1') { for (let i = 0; i < nodeList.length; i++) { @@ -518,7 +555,6 @@ export const addSubGraph = function (_id, list, _title) { } id = id || 'subGraph' + subCount; - // if (id[0].match(/\d/)) id = lookUpDomId(id); title = title || ''; title = sanitizeText(title); subCount = subCount + 1; @@ -533,19 +569,6 @@ export const addSubGraph = function (_id, list, _title) { log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir); - /** Deletes an id from all subgraphs */ - // const del = _id => { - // subGraphs.forEach(sg => { - // const pos = sg.nodes.indexOf(_id); - // if (pos >= 0) { - // sg.nodes.splice(pos, 1); - // } - // }); - // }; - - // // Removes the members of this subgraph from any other subgraphs, a node only belong to one subgraph - // subGraph.nodes.forEach(_id => del(_id)); - // Remove the members in the new subgraph if they already belong to another subgraph subGraph.nodes = makeUniq(subGraph, subGraphs).nodes; subGraphs.push(subGraph); @@ -553,7 +576,7 @@ export const addSubGraph = function (_id, list, _title) { return id; }; -const getPosForId = function (id) { +const getPosForId = function (id: string) { for (const [i, subGraph] of subGraphs.entries()) { if (subGraph.id === id) { return i; @@ -562,12 +585,15 @@ const getPosForId = function (id) { return -1; }; let secCount = -1; -const posCrossRef = []; -const indexNodes2 = function (id, pos) { +const posCrossRef: number[] = []; +const indexNodes2 = function (id: string, pos: number): { result: boolean; count: number } { const nodes = subGraphs[pos].nodes; secCount = secCount + 1; if (secCount > 2000) { - return; + return { + result: false, + count: 0, + }; } posCrossRef[secCount] = pos; // Check if match @@ -603,13 +629,13 @@ const indexNodes2 = function (id, pos) { }; }; -export const getDepthFirstPos = function (pos) { +export const getDepthFirstPos = function (pos: number) { return posCrossRef[pos]; }; export const indexNodes = function () { secCount = -1; if (subGraphs.length > 0) { - indexNodes2('none', subGraphs.length - 1, 0); + indexNodes2('none', subGraphs.length - 1); } }; @@ -625,7 +651,7 @@ export const firstGraph = () => { return false; }; -const destructStartLink = (_str) => { +const destructStartLink = (_str: string): FlowLink => { let str = _str.trim(); let type = 'arrow_open'; @@ -657,7 +683,7 @@ const destructStartLink = (_str) => { return { type, stroke }; }; -const countChar = (char, str) => { +const countChar = (char: string, str: string) => { const length = str.length; let count = 0; for (let i = 0; i < length; ++i) { @@ -668,7 +694,7 @@ const countChar = (char, str) => { return count; }; -const destructEndLink = (_str) => { +const destructEndLink = (_str: string) => { const str = _str.trim(); let line = str.slice(0, -1); let type = 'arrow_open'; @@ -708,7 +734,7 @@ const destructEndLink = (_str) => { stroke = 'invisible'; } - let dots = countChar('.', line); + const dots = countChar('.', line); if (dots) { stroke = 'dotted'; @@ -718,7 +744,7 @@ const destructEndLink = (_str) => { return { type, stroke, length }; }; -export const destructLink = (_str, _startStr) => { +export const destructLink = (_str: string, _startStr: string) => { const info = destructEndLink(_str); let startInfo; if (_startStr) { @@ -752,7 +778,7 @@ export const destructLink = (_str, _startStr) => { }; // Todo optimizer this by caching existing nodes -const exists = (allSgs, _id) => { +const exists = (allSgs: FlowSubGraph[], _id: string) => { let res = false; allSgs.forEach((sg) => { const pos = sg.nodes.indexOf(_id); @@ -765,11 +791,9 @@ const exists = (allSgs, _id) => { /** * Deletes an id from all subgraphs * - * @param sg - * @param allSubgraphs */ -const makeUniq = (sg, allSubgraphs) => { - const res = []; +const makeUniq = (sg: FlowSubGraph, allSubgraphs: FlowSubGraph[]) => { + const res: string[] = []; sg.nodes.forEach((_id, pos) => { if (!exists(allSubgraphs, _id)) { res.push(sg.nodes[pos]); @@ -781,6 +805,7 @@ const makeUniq = (sg, allSubgraphs) => { export const lex = { firstGraph, }; + export default { defaultConfig: () => defaultConfig.flowchart, setAccTitle,