From 6bd1da219ad474d4c81eebfc0d50f5731f089843 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Sun, 7 Apr 2024 17:27:01 -0500 Subject: [PATCH] refactor(arch): refactored code to be consistent with other diagrams --- .../diagrams/architecture/architectureDb.ts | 135 ++++++++---------- .../architecture/architectureRenderer.ts | 56 ++++---- .../architecture/architectureTypes.ts | 38 ++--- .../architecture/parser/architecture.jison | 12 +- 4 files changed, 115 insertions(+), 126 deletions(-) diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts index dbb9bb8ea..615f42561 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureDb.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -1,10 +1,10 @@ import type { - ArchitectureFields, + ArchitectureState, ArchitectureDB, ArchitectureService, ArchitectureGroup, ArchitectureDirection, - ArchitectureLine, + ArchitectureEdge, ArchitectureDirectionPairMap, ArchitectureDirectionPair, ArchitectureSpatialMap, @@ -27,56 +27,48 @@ import { import type { ArchitectureDiagramConfig } from '../../config.type.js'; import DEFAULT_CONFIG from '../../defaultConfig.js'; import type { D3Element } from '../../mermaidAPI.js'; +import { ImperativeState } from '../../utils/imperativeState.js'; -export const DEFAULT_ARCHITECTURE_CONFIG: Required = +const DEFAULT_ARCHITECTURE_CONFIG: Required = DEFAULT_CONFIG.architecture; -export const DEFAULT_ARCHITECTURE_DB: ArchitectureFields = { + +const state = new ImperativeState(() => ({ services: {}, groups: [], - lines: [], + edges: [], registeredIds: {}, config: DEFAULT_ARCHITECTURE_CONFIG, -} as const; - -let services = DEFAULT_ARCHITECTURE_DB.services; -let groups = DEFAULT_ARCHITECTURE_DB.groups; -let lines = DEFAULT_ARCHITECTURE_DB.lines; -let registeredIds = DEFAULT_ARCHITECTURE_DB.registeredIds; -let datastructures = DEFAULT_ARCHITECTURE_DB.datastructures; -let elements: Record = {}; + datastructures: undefined, + elements: {} +})) const clear = (): void => { - services = structuredClone(DEFAULT_ARCHITECTURE_DB.services); - groups = structuredClone(DEFAULT_ARCHITECTURE_DB.groups); - lines = structuredClone(DEFAULT_ARCHITECTURE_DB.lines); - registeredIds = structuredClone(DEFAULT_ARCHITECTURE_DB.registeredIds); - datastructures = undefined; - elements = {}; + state.reset() commonClear(); }; const addService = function (id: string, opts: Omit = {}) { const { icon, in: inside, title } = opts; - if (registeredIds[id] !== undefined) { - throw new Error(`The service id [${id}] is already in use by another ${registeredIds[id]}`); + if (state.records.registeredIds[id] !== undefined) { + throw new Error(`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`); } if (inside !== undefined) { if (id === inside) { throw new Error(`The service [${id}] cannot be placed within itself`); } - if (registeredIds[inside] === undefined) { + if (state.records.registeredIds[inside] === undefined) { throw new Error( `The service [${id}]'s parent does not exist. Please make sure the parent is created before this service` ); } - if (registeredIds[inside] === 'service') { + if (state.records.registeredIds[inside] === 'service') { throw new Error(`The service [${id}]'s parent is not a group`); } } - registeredIds[id] = 'service'; + state.records.registeredIds[id] = 'service'; - services[id] = { + state.records.services[id] = { id, icon, title, @@ -85,30 +77,30 @@ const addService = function (id: string, opts: Omit Object.values(services); +const getServices = (): ArchitectureService[] => Object.values(state.records.services); const addGroup = function (id: string, opts: Omit = {}) { const { icon, in: inside, title } = opts; - if (registeredIds[id] !== undefined) { - throw new Error(`The group id [${id}] is already in use by another ${registeredIds[id]}`); + if (state.records.registeredIds[id] !== undefined) { + throw new Error(`The group id [${id}] is already in use by another ${state.records.registeredIds[id]}`); } if (inside !== undefined) { if (id === inside) { throw new Error(`The group [${id}] cannot be placed within itself`); } - if (registeredIds[inside] === undefined) { + if (state.records.registeredIds[inside] === undefined) { throw new Error( `The group [${id}]'s parent does not exist. Please make sure the parent is created before this group` ); } - if (registeredIds[inside] === 'service') { + if (state.records.registeredIds[inside] === 'service') { throw new Error(`The group [${id}]'s parent is not a group`); } } - registeredIds[id] = 'group'; + state.records.registeredIds[id] = 'group'; - groups.push({ + state.records.groups.push({ id, icon, title, @@ -116,54 +108,55 @@ const addGroup = function (id: string, opts: Omit = {}) }); }; const getGroups = (): ArchitectureGroup[] => { - return groups; + return state.records.groups; }; const addEdge = function ( - lhs_id: string, - lhs_dir: ArchitectureDirection, - rhs_id: string, - rhs_dir: ArchitectureDirection, - opts: Omit = {} + lhsId: string, + lhsDir: ArchitectureDirection, + rhsId: string, + rhsDir: ArchitectureDirection, + opts: Omit = {} ) { - const { title, lhs_into, rhs_into } = opts; - if (!isArchitectureDirection(lhs_dir)) { + const { title, lhsInto: lhsInto, rhsInto: rhsInto } = opts; + if (!isArchitectureDirection(lhsDir)) { throw new Error( - `Invalid direction given for left hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${lhs_dir}` + `Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${lhsDir}` ); } - if (!isArchitectureDirection(rhs_dir)) { + if (!isArchitectureDirection(rhsDir)) { throw new Error( - `Invalid direction given for right hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${rhs_dir}` + `Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${rhsDir}` ); } - if (services[lhs_id] === undefined) { + if (state.records.services[lhsId] === undefined) { throw new Error( - `The left-hand service [${lhs_id}] does not yet exist. Please create the service before declaring an edge to it.` + `The left-hand service [${lhsId}] does not yet exist. Please create the service before declaring an edge to it.` ); } - if (services[rhs_id] === undefined) { + if (state.records.services[rhsId] === undefined) { throw new Error( - `The right-hand service [${rhs_id}] does not yet exist. Please create the service before declaring an edge to it.` + `The right-hand service [${rhsId}] does not yet exist. Please create the service before declaring an edge to it.` ); } const edge = { - lhs_id, - lhs_dir, - rhs_id, - rhs_dir, + lhsId, + lhsDir, + rhsId, + rhsDir, title, - lhs_into, - rhs_into, + lhsInto, + rhsInto, }; - lines.push(edge); + state.records.edges.push(edge); - services[lhs_id].edges.push(lines[lines.length - 1]); - services[rhs_id].edges.push(lines[lines.length - 1]); + state.records.services[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]); + state.records.services[rhsId].edges.push(state.records.edges[state.records.edges.length - 1]); }; -const getEdges = (): ArchitectureLine[] => lines; + +const getEdges = (): ArchitectureEdge[] => state.records.edges; /** * Returns the current diagram's adjacency list & spatial map. @@ -171,24 +164,24 @@ const getEdges = (): ArchitectureLine[] => lines; * @returns */ const getDataStructures = () => { - if (datastructures === undefined) { + if (state.records.datastructures === undefined) { // Create an adjacency list of the diagram to perform BFS on // Outer reduce applied on all services // Inner reduce applied on the edges for a service - const adjList = Object.entries(services).reduce<{ [id: string]: ArchitectureDirectionPairMap }>( + const adjList = Object.entries(state.records.services).reduce<{ [id: string]: ArchitectureDirectionPairMap }>( (prev, [id, service]) => { prev[id] = service.edges.reduce((prev, edge) => { - if (edge.lhs_id === id) { + if (edge.lhsId === id) { // source is LHS - const pair = getArchitectureDirectionPair(edge.lhs_dir, edge.rhs_dir); + const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir); if (pair) { - prev[pair] = edge.rhs_id; + prev[pair] = edge.rhsId; } } else { // source is RHS - const pair = getArchitectureDirectionPair(edge.rhs_dir, edge.lhs_dir); + const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir); if (pair) { - prev[pair] = edge.lhs_id; + prev[pair] = edge.lhsId; } } return prev; @@ -236,19 +229,19 @@ const getDataStructures = () => { while (Object.keys(notVisited).length > 0) { spatialMaps.push(BFS(Object.keys(notVisited)[0])); } - datastructures = { + state.records.datastructures = { adjList, spatialMaps, }; - console.log(datastructures); + console.log(state.records.datastructures); } - return datastructures; + return state.records.datastructures; }; const setElementForId = (id: string, element: D3Element) => { - elements[id] = element; + state.records.elements[id] = element; }; -const getElementById = (id: string) => elements[id]; +const getElementById = (id: string) => state.records.elements[id]; export const db: ArchitectureDB = { clear, @@ -275,7 +268,7 @@ export const db: ArchitectureDB = { * @param field * @returns */ -function getConfigField( +export function getConfigField( field: T ): Required[T] { const arch = getConfig().architecture; @@ -285,5 +278,3 @@ function getConfigField( } return DEFAULT_ARCHITECTURE_CONFIG[field]; } - -export { getConfigField }; diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 1f2c6f20d..bcf93d4c0 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -10,7 +10,7 @@ import { type ArchitectureDB, type ArchitectureDirection, type ArchitectureGroup, - type ArchitectureLine, + type ArchitectureEdge, type ArchitectureService, ArchitectureDataStructures, ArchitectureDirectionName, @@ -20,14 +20,11 @@ import { ArchitectureSpatialMap, EdgeSingularData, EdgeSingular, - NodeSingular, - NodeSingularData, nodeData, edgeData } from './architectureTypes.js'; import { select } from 'd3'; import { setupGraphViewbox } from '../../setupGraphViewbox.js'; -import type { D3Element } from '../../mermaidAPI.js'; import { drawEdges, drawGroups, drawServices } from './svgDraw.js'; import { getConfigField } from './architectureDb.js'; @@ -79,34 +76,34 @@ function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { }); } -function addEdges(lines: ArchitectureLine[], cy: cytoscape.Core) { - lines.forEach((line) => { - const { lhs_id, rhs_id, lhs_into, rhs_into, lhs_dir, rhs_dir } = line; - const edgeType = isArchitectureDirectionXY(line.lhs_dir, line.rhs_dir) +function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) { + edges.forEach((parsedEdge) => { + const { lhsId, rhsId, lhsInto, rhsInto, lhsDir, rhsDir } = parsedEdge; + const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir) ? 'segments' : 'straight'; const edge: EdgeSingularData = { - id: `${lhs_id}-${rhs_id}`, - source: lhs_id, - sourceDir: lhs_dir, - sourceArrow: lhs_into, + id: `${lhsId}-${rhsId}`, + source: lhsId, + sourceDir: lhsDir, + sourceArrow: lhsInto, sourceEndpoint: - lhs_dir === 'L' + lhsDir === 'L' ? '0 50%' - : lhs_dir === 'R' + : lhsDir === 'R' ? '100% 50%' - : lhs_dir === 'T' + : lhsDir === 'T' ? '50% 0' : '50% 100%', - target: rhs_id, - targetDir: rhs_dir, - targetArrow: rhs_into, + target: rhsId, + targetDir: rhsDir, + targetArrow: rhsInto, targetEndpoint: - rhs_dir === 'L' + rhsDir === 'L' ? '0 50%' - : rhs_dir === 'R' + : rhsDir === 'R' ? '100% 50%' - : rhs_dir === 'T' + : rhsDir === 'T' ? '50% 0' : '50% 100%', }; @@ -207,7 +204,7 @@ function getRelativeConstraints( function layoutArchitecture( services: ArchitectureService[], groups: ArchitectureGroup[], - lines: ArchitectureLine[], + edges: ArchitectureEdge[], { spatialMaps }: ArchitectureDataStructures ): Promise { return new Promise((resolve) => { @@ -272,7 +269,7 @@ function layoutArchitecture( addGroups(groups, cy); addServices(services, cy); - addEdges(lines, cy); + addEdges(edges, cy); // Use the spatial map to create alignment arrays for fcose const alignmentConstraint = getAlignments(spatialMaps); @@ -366,9 +363,9 @@ function layoutArchitecture( cy.startBatch(); for (let edge of Object.values(cy.edges())) { if (edge.data?.()) { - let { x: s_x, y: s_y } = edge.source().position(); - let { x: t_x, y: t_y } = edge.target().position(); - if (s_x !== t_x && s_y !== t_y) { + let { x: sX, y: sY } = edge.source().position(); + let { x: tX, y: tY } = edge.target().position(); + if (sX !== tX && sY !== tY) { let sEP = edge.sourceEndpoint(); let tEP = edge.targetEndpoint(); const { sourceDir } = edgeData(edge) @@ -395,14 +392,13 @@ function layoutArchitecture( export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) => { const db = diagObj.db as ArchitectureDB; - const conf: MermaidConfig = getConfig(); const services = db.getServices(); const groups = db.getGroups(); - const lines = db.getEdges(); + const edges = db.getEdges(); const ds = db.getDataStructures(); console.log('Services: ', services); - console.log('Lines: ', lines); + console.log('Edges: ', edges); console.log('Groups: ', groups); const svg: SVG = selectSvgElement(id); @@ -418,7 +414,7 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) drawServices(db, servicesElem, services); - const cy = await layoutArchitecture(services, groups, lines, ds); + const cy = await layoutArchitecture(services, groups, edges, ds); drawEdges(edgesElem, cy); drawGroups(groupElem, cy); diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts index 6f7e4046c..d7a587baf 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -141,7 +141,7 @@ export interface ArchitectureStyleOptions { export interface ArchitectureService { id: string; - edges: ArchitectureLine[]; + edges: ArchitectureEdge[]; icon?: string; title?: string; in?: string; @@ -156,14 +156,14 @@ export interface ArchitectureGroup { in?: string; } -export interface ArchitectureLine { - lhs_id: string; - lhs_dir: ArchitectureDirection; +export interface ArchitectureEdge { + lhsId: string; + lhsDir: ArchitectureDirection; title?: string; - rhs_id: string; - rhs_dir: ArchitectureDirection; - lhs_into?: boolean; - rhs_into?: boolean; + rhsId: string; + rhsDir: ArchitectureDirection; + lhsInto?: boolean; + rhsInto?: boolean; } export interface ArchitectureDB extends DiagramDB { @@ -173,13 +173,13 @@ export interface ArchitectureDB extends DiagramDB { addGroup: (id: string, opts: Omit) => void; getGroups: () => ArchitectureGroup[]; addEdge: ( - lhs_id: string, - lhs_dir: ArchitectureDirection, - rhs_id: string, - rhs_dir: ArchitectureDirection, - opts: Omit + lhsId: string, + lhsDir: ArchitectureDirection, + rhsId: string, + rhsDir: ArchitectureDirection, + opts: Omit ) => void; - getEdges: () => ArchitectureLine[]; + getEdges: () => ArchitectureEdge[]; setElementForId: (id: string, element: D3Element) => void; getElementById: (id: string) => D3Element; getDataStructures: () => ArchitectureDataStructures; @@ -192,18 +192,20 @@ export type ArchitectureDataStructures = { spatialMaps: ArchitectureSpatialMap[]; }; -export interface ArchitectureFields { +export interface ArchitectureState extends Record { services: Record; groups: ArchitectureGroup[]; - lines: ArchitectureLine[]; + edges: ArchitectureEdge[]; registeredIds: Record; datastructures?: ArchitectureDataStructures; + elements: Record; config: ArchitectureDiagramConfig; } /*=======================================*\ | Cytoscape Override Types | \*=======================================*/ + export type EdgeSingularData = { id: string; source: string; @@ -215,7 +217,7 @@ export type EdgeSingularData = { [key: string]: any; }; -export function edgeData(edge: cytoscape.EdgeSingular) { +export const edgeData = (edge: cytoscape.EdgeSingular) => { return edge.data() as EdgeSingularData; } @@ -254,7 +256,7 @@ export type NodeSingularData = { [key: string]: any; }; -export function nodeData(node: cytoscape.NodeSingular) { +export const nodeData = (node: cytoscape.NodeSingular) => { return node.data() as NodeSingularData; } diff --git a/packages/mermaid/src/diagrams/architecture/parser/architecture.jison b/packages/mermaid/src/diagrams/architecture/parser/architecture.jison index d3c9640f8..2a0ffc116 100644 --- a/packages/mermaid/src/diagrams/architecture/parser/architecture.jison +++ b/packages/mermaid/src/diagrams/architecture/parser/architecture.jison @@ -55,19 +55,19 @@ statement line_statement : id ARROW_LEFT_INTO ARROW_RIGHT_INTO id - { yy.addEdge($1, $2[1], $4, $3[1], {lhs_into: true, rhs_into: true}) } + { yy.addEdge($1, $2[1], $4, $3[1], {lhsInto: true, rhsInto: true}) } | id ARROW_LEFT_INTO ARROW_RIGHT id - { yy.addEdge($1, $2[1], $4, $3[1], {lhs_into: true}) } + { yy.addEdge($1, $2[1], $4, $3[1], {lhsInto: true}) } | id ARROW_LEFT ARROW_RIGHT_INTO id - { yy.addEdge($1, $2[0], $4, $3[1], {rhs_into: true}) } + { yy.addEdge($1, $2[0], $4, $3[1], {rhsInto: true}) } | id ARROW_LEFT ARROW_RIGHT id { yy.addEdge($1, $2[0], $4, $3[1]) } | id ARROW_LEFT_INTO title ARROW_RIGHT_INTO id - { yy.addEdge($1, $2[1], $5, $4[1], { title: $3.slice(1,-1), lhs_into: true, rhs_into: true }) } + { yy.addEdge($1, $2[1], $5, $4[1], { title: $3.slice(1,-1), lhsInto: true, rhsInto: true }) } | id ARROW_LEFT_INTO title ARROW_RIGHT id - { yy.addEdge($1, $2[1], $5, $4[1], { title: $3.slice(1,-1), lhs_into: true }) } + { yy.addEdge($1, $2[1], $5, $4[1], { title: $3.slice(1,-1), lhsInto: true }) } | id ARROW_LEFT title ARROW_RIGHT_INTO id - { yy.addEdge($1, $2[0], $5, $4[1], { title: $3.slice(1,-1), rhs_into: true }) } + { yy.addEdge($1, $2[0], $5, $4[1], { title: $3.slice(1,-1), rhsInto: true }) } | id ARROW_LEFT title ARROW_RIGHT id { yy.addEdge($1, $2[0], $5, $4[1], { title: $3.slice(1,-1) }) } ;