From 377b22e82bbea7fee6f9188c734dd83a1e57ff56 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Wed, 19 Oct 2022 22:31:37 +0530 Subject: [PATCH] fix: Type of DiagramStyleClassDef, general cleanup --- packages/mermaid/src/defaultConfig.ts | 6 +- .../mermaid/src/diagrams/flowchart/flowDb.js | 2 +- .../src/diagrams/flowchart/flowRenderer.js | 3 +- packages/mermaid/src/mermaidAPI.spec.ts | 12 +-- packages/mermaid/src/mermaidAPI.ts | 97 +++++++++++-------- packages/mermaid/src/utils.ts | 4 + 6 files changed, 70 insertions(+), 54 deletions(-) diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 710557bd9..61322da5f 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -22,7 +22,7 @@ import { MermaidConfig } from './config.type'; * * @name Configuration */ -const config: Partial = { +const config: MermaidConfig = { /** * Theme , the CSS style sheet * @@ -1069,6 +1069,7 @@ const config: Partial = { showCommitLabel: true, showBranches: true, rotateCommitLabel: true, + arrowMarkerAbsolute: false, }, /** The object containing configurations specific for c4 diagrams */ @@ -1833,9 +1834,6 @@ const config: Partial = { fontSize: 16, }; -if (config.class) config.class.arrowMarkerAbsolute = config.arrowMarkerAbsolute; -if (config.gitGraph) config.gitGraph.arrowMarkerAbsolute = config.arrowMarkerAbsolute; - const keyify = (obj: any, prefix = ''): string[] => Object.keys(obj).reduce((res: string[], el): string[] => { if (Array.isArray(obj[el])) { diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 5aa203225..9e8f25524 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -17,7 +17,7 @@ let vertexCounter = 0; let config = configApi.getConfig(); let vertices = {}; let edges = []; -let classes = []; +let classes = {}; let subGraphs = []; let subGraphLookup = {}; let tooltips = {}; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js index 0c3aa3623..c403b7fe3 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer.js +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer.js @@ -279,7 +279,8 @@ export const getClasses = function (text, diagObj) { diagObj.parse(text); return diagObj.db.getClasses(); } catch (e) { - return; + log.error(e); + return {}; } }; diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index 8206499f9..af50a6cc4 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -256,11 +256,11 @@ describe('mermaidAPI', function () { expect(styles).toMatch(/^\ndefault(.*)/); }); it('gets the fontFamily from the config', () => { - const styles = createCssStyles(mocked_config_with_htmlLabels, 'graphType', null); + const styles = createCssStyles(mocked_config_with_htmlLabels, 'graphType', {}); expect(styles).toMatch(/(.*)\n:root \{ --mermaid-font-family: serif(.*)/); }); it('gets the alt fontFamily from the config', () => { - const styles = createCssStyles(mocked_config_with_htmlLabels, 'graphType', null); + const styles = createCssStyles(mocked_config_with_htmlLabels, 'graphType', undefined); expect(styles).toMatch(/(.*)\n:root \{ --mermaid-alt-font-family: sans-serif(.*)/); }); @@ -268,7 +268,7 @@ describe('mermaidAPI', function () { const classDef1 = { id: 'classDef1', styles: ['style1-1', 'style1-2'], textStyles: [] }; const classDef2 = { id: 'classDef2', styles: [], textStyles: ['textStyle2-1'] }; const classDef3 = { id: 'classDef3', textStyles: ['textStyle3-1', 'textStyle3-2'] }; - const classDefs = [classDef1, classDef2, classDef3]; + const classDefs = { classDef1, classDef2, classDef3 }; describe('the graph supports classDefs', () => { const graphType = 'flowchart-v2'; @@ -431,7 +431,7 @@ describe('mermaidAPI', function () { it('gets the css styles created', () => { // @todo TODO if a single function in the module can be mocked, do it for createCssStyles and mock the results. - createUserStyles(mockConfig, 'flowchart-v2', [classDef1], 'someId'); + createUserStyles(mockConfig, 'flowchart-v2', { classDef1 }, 'someId'); const expectedStyles = '\ndefault' + '\n.classDef1 > * { style1-1 !important; }' + @@ -442,12 +442,12 @@ describe('mermaidAPI', function () { }); it('calls getStyles to get css for all graph, user css styles, and config theme variables', () => { - createUserStyles(mockConfig, 'someDiagram', null, 'someId'); + createUserStyles(mockConfig, 'someDiagram', {}, 'someId'); expect(getStyles).toHaveBeenCalled(); }); it('returns the result of compiling, stringifying, and serializing the css code with stylis', () => { - const result = createUserStyles(mockConfig, 'someDiagram', null, 'someId'); + const result = createUserStyles(mockConfig, 'someDiagram', {}, 'someId'); expect(compile).toHaveBeenCalled(); expect(serialize).toHaveBeenCalled(); expect(result).toEqual('stylis serialized css'); diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 54e1758fb..8f3dcebf7 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -28,7 +28,7 @@ import { attachFunctions } from './interactionDb'; import { log, setLogLevel } from './logger'; import getStyles from './styles'; import theme from './themes'; -import utils, { directiveSanitizer } from './utils'; +import utils, { directiveSanitizer, isNonEmptyArray } from './utils'; import DOMPurify from 'dompurify'; import { MermaidConfig } from './config.type'; import { evaluate } from './diagrams/common/common'; @@ -59,8 +59,11 @@ const DOMPURE_ATTR = ['dominant-baseline']; // This is what is returned from getClasses(...) methods. // It is slightly renamed to ..StyleClassDef instead of just ClassDef because "class" is a greatly ambiguous and overloaded word. // It makes it clear we're working with a style class definition, even though defining the type is currently difficult. -// @ts-ignore This is an alias for a js construct used in diagrams. -type DiagramStyleClassDef = any; +interface DiagramStyleClassDef { + id: string; + styles?: string[]; + textStyles?: string[]; +} // This makes it clear that we're working with a d3 selected element of some kind, even though it's hard to specify the exact type. // @ts-ignore Could replicate the type definition in d3. This also makes it possible to use the untyped info from the js diagram files. @@ -151,29 +154,32 @@ export const cssImportantStyles = ( * * @param {MermaidConfig} config * @param {string} graphType - * @param {null | DiagramStyleClassDef[]} classDefs - the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) + * @param {Record | null | undefined} classDefs - the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) * @returns {string} the string with all the user styles */ export const createCssStyles = ( config: MermaidConfig, graphType: string, - classDefs: DiagramStyleClassDef[] | null | undefined + classDefs: Record | null | undefined = {} ): string => { let cssStyles = ''; // user provided theme CSS info // If you add more configuration driven data into the user styles make sure that the value is // sanitized by the santizeCSS function @todo TODO where is this method? what should be used to replace it? refactor so that it's always sanitized - if (config.themeCSS !== undefined) cssStyles += `\n${config.themeCSS}`; + if (config.themeCSS !== undefined) { + cssStyles += `\n${config.themeCSS}`; + } - if (config.fontFamily !== undefined) + if (config.fontFamily !== undefined) { cssStyles += `\n:root { --mermaid-font-family: ${config.fontFamily}}`; - - if (config.altFontFamily !== undefined) + } + if (config.altFontFamily !== undefined) { cssStyles += `\n:root { --mermaid-alt-font-family: ${config.altFontFamily}}`; + } // classDefs defined in the diagram text - if (classDefs !== undefined && classDefs !== null && classDefs.length > 0) { + if (classDefs && Object.keys(classDefs).length > 0) { if (graphType === 'flowchart' || graphType === 'flowchart-v2' || graphType === 'graph') { const htmlLabels = config.htmlLabels || config.flowchart?.htmlLabels; @@ -186,22 +192,14 @@ export const createCssStyles = ( for (const classId in classDefs) { const styleClassDef = classDefs[classId]; // create the css styles for each cssElement and the styles (only if there are styles) - if (styleClassDef['styles'] && styleClassDef['styles'].length > 0) { + if (isNonEmptyArray(styleClassDef.styles)) { cssElements.forEach((cssElement) => { - cssStyles += cssImportantStyles( - styleClassDef['id'], - cssElement, - styleClassDef['styles'] - ); + cssStyles += cssImportantStyles(styleClassDef.id, cssElement, styleClassDef.styles); }); } // create the css styles for the tspan element and the text styles (only if there are textStyles) - if (styleClassDef['textStyles'] && styleClassDef['textStyles'].length > 0) { - cssStyles += cssImportantStyles( - styleClassDef['id'], - 'tspan', - styleClassDef['textStyles'] - ); + if (isNonEmptyArray(styleClassDef.textStyles)) { + cssStyles += cssImportantStyles(styleClassDef.id, 'tspan', styleClassDef.textStyles); } } } @@ -212,7 +210,7 @@ export const createCssStyles = ( export const createUserStyles = ( config: MermaidConfig, graphType: string, - classDefs: null | DiagramStyleClassDef, + classDefs: Record, svgId: string ): string => { const userCSSstyles = createCssStyles(config, graphType, classDefs); @@ -261,8 +259,7 @@ export const cleanUpSvgCode = ( * @todo TODO replace btoa(). Replace with buf.toString('base64')? */ export const putIntoIFrame = (svgCode = '', svgElement?: D3Element): string => { - let height = IFRAME_HEIGHT; // default iFrame height - if (svgElement) height = svgElement.viewBox.baseVal.height + 'px'; + const height = svgElement ? svgElement.viewBox.baseVal.height + 'px' : IFRAME_HEIGHT; const base64encodedSrc = btoa('' + svgCode + ''); return `