From 1615c6d9f996b73cd8f17b205da386cb4b00c88a Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 6 Oct 2022 19:14:25 +0800 Subject: [PATCH 1/9] fix #3391: Remove flowchart as fallback for diagram detection. --- cypress/platform/sidv.html | 14 ++++++ package.json | 3 +- packages/mermaid/src/Diagram.ts | 47 ++++++++++++------- .../mermaid/src/diagram-api/detectType.ts | 7 +-- .../src/diagram-api/diagramAPI.spec.ts | 4 +- .../mermaid/src/diagrams/flowchart/flowDb.js | 3 +- 6 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 cypress/platform/sidv.html diff --git a/cypress/platform/sidv.html b/cypress/platform/sidv.html new file mode 100644 index 000000000..c9bf56b7d --- /dev/null +++ b/cypress/platform/sidv.html @@ -0,0 +1,14 @@ + + +
+    none
+    hello world
+    
+ + + + diff --git a/package.json b/package.json index 67f546d97..f0cad549d 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "e2e": "start-server-and-test dev http://localhost:9000/ cypress", "ci": "vitest run", "test": "pnpm lint && vitest run", - "test:watch": "vitest --coverage --watch", + "test:watch": "vitest --watch", + "test:coverage": "vitest --coverage", "prepublishOnly": "pnpm build && pnpm test", "prepare": "concurrently \"husky install\" \"pnpm build\"", "pre-commit": "lint-staged" diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 231a78af6..7fba15d56 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -8,11 +8,18 @@ export class Diagram { parser; renderer; db; + private detectTypeFailed = false; // eslint-disable-next-line @typescript-eslint/ban-types constructor(public txt: string, parseError?: Function) { const cnf = configApi.getConfig(); this.txt = txt; - this.type = detectType(txt, cnf); + try { + this.type = detectType(txt, cnf); + } catch (e) { + this.handleError(e, parseError); + this.type = 'error'; + this.detectTypeFailed = true; + } const diagram = getDiagram(this.type); log.debug('Type ' + this.type); // Setup diagram @@ -32,31 +39,39 @@ export class Diagram { // eslint-disable-next-line @typescript-eslint/ban-types parse(text: string, parseError?: Function): boolean { + if (this.detectTypeFailed) { + return false; + } try { text = text + '\n'; this.db.clear(); this.parser.parse(text); return true; } catch (error) { - // Is this the correct way to access mermiad's parseError() - // method ? (or global.mermaid.parseError()) ? - if (parseError) { - if (isDetailedError(error)) { - // handle case where error string and hash were - // wrapped in object like`const error = { str, hash };` - parseError(error.str, error.hash); - } else { - // assume it is just error string and pass it on - parseError(error); - } - } else { - // No mermaid.parseError() handler defined, so re-throw it - throw error; - } + this.handleError(error, parseError); } return false; } + // eslint-disable-next-line @typescript-eslint/ban-types + handleError(error: unknown, parseError?: Function) { + // Is this the correct way to access mermiad's parseError() + // method ? (or global.mermaid.parseError()) ? + if (parseError) { + if (isDetailedError(error)) { + // handle case where error string and hash were + // wrapped in object like`const error = { str, hash };` + parseError(error.str, error.hash); + } else { + // assume it is just error string and pass it on + parseError(error); + } + } else { + // No mermaid.parseError() handler defined, so re-throw it + throw error; + } + } + getParser() { return this.parser; } diff --git a/packages/mermaid/src/diagram-api/detectType.ts b/packages/mermaid/src/diagram-api/detectType.ts index afb9a9078..3fd768ad1 100644 --- a/packages/mermaid/src/diagram-api/detectType.ts +++ b/packages/mermaid/src/diagram-api/detectType.ts @@ -35,16 +35,13 @@ const detectors: Record = {}; export const detectType = function (text: string, config?: MermaidConfig): string { text = text.replace(directive, '').replace(anyComment, '\n'); - // console.log(detectors); - for (const [key, detectorRecord] of Object.entries(detectors)) { if (detectorRecord.detector(text, config)) { return key; } } - // TODO: #3391 - // throw new Error(`No diagram type detected for text: ${text}`); - return 'flowchart'; + + throw new Error(`No diagram type detected for text: ${text}`); }; export const addDetector = (key: string, detector: DiagramDetector, path: string) => { diff --git a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts index 018e72bd4..0cef62b3e 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts @@ -15,7 +15,9 @@ describe('DiagramAPI', () => { it('should handle diagram registrations', () => { expect(() => getDiagram('loki')).toThrow(); - expect(() => detectType('loki diagram')).not.toThrow(); // TODO: #3391 + expect(() => detectType('loki diagram')).toThrow( + 'No diagram type detected for text: loki diagram' + ); const detector: DiagramDetector = (str: string) => { return str.match('loki') !== null; }; diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 192da23d3..5aa203225 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -429,8 +429,7 @@ export const clear = function (ver = 'gen-1') { vertices = {}; classes = {}; edges = []; - funs = []; - funs.push(setupToolTips); + funs = [setupToolTips]; subGraphs = []; subGraphLookup = {}; subCount = 0; From 24fb2337d79d2555c35db063ed7d57f5b191a88e Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 7 Oct 2022 16:30:44 +0800 Subject: [PATCH 2/9] cleanup --- todo-fix-root-level-tsconfig.json | 108 ------------------------------ 1 file changed, 108 deletions(-) delete mode 100644 todo-fix-root-level-tsconfig.json diff --git a/todo-fix-root-level-tsconfig.json b/todo-fix-root-level-tsconfig.json deleted file mode 100644 index 0ffa0002e..000000000 --- a/todo-fix-root-level-tsconfig.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Projects */ - // "incremental": true /* Enable incremental compilation */, - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "lib": [ - "DOM", - "ES2021" - ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - - /* Modules */ - "module": "ES6" /* Specify what module code is generated. */, - "rootDir": "./src" /* Specify the root folder within your source files. */, - "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./src" /* Specify the base directory to resolve non-relative module names. */, - // "paths": {} /* Specify a set of entries that re-map imports to additional lookup locations. */, - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [] /* Specify multiple folders that act like `./node_modules/@types`. */, - "types": [ - "vitest/globals" - ] /* Specify type package names to be included without being referenced in a source file. */, - - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - "resolveJsonModule": true /* Enable importing .json files */, - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - "allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */, - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - - /* Emit */ - "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": ["./src/**/*.ts", "./package.json"] -} From 2389f4a28512a9a6adc40cac2156bda2bc1d9b32 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 7 Oct 2022 16:33:59 +0800 Subject: [PATCH 3/9] cleanup --- packages/mermaid/src/diagram-api/text-wrap | 227 --------------------- 1 file changed, 227 deletions(-) delete mode 100644 packages/mermaid/src/diagram-api/text-wrap diff --git a/packages/mermaid/src/diagram-api/text-wrap b/packages/mermaid/src/diagram-api/text-wrap deleted file mode 100644 index 173baecec..000000000 --- a/packages/mermaid/src/diagram-api/text-wrap +++ /dev/null @@ -1,227 +0,0 @@ -export const lineBreakRegex = //gi; - -/** - * Caches results of functions based on input - * - * @param {Function} fn Function to run - * @param {Function} resolver Function that resolves to an ID given arguments the `fn` takes - * @returns {Function} An optimized caching function - */ -const memoize = (fn, resolver) => { - let cache = {}; - return (...args) => { - let n = resolver ? resolver.apply(this, args) : args[0]; - if (n in cache) { - return cache[n]; - } else { - let result = fn(...args); - cache[n] = result; - return result; - } - }; -}; -/** - * This calculates the width of the given text, font size and family. - * - * @param {any} text - The text to calculate the width of - * @param {any} config - The config for fontSize, fontFamily, and fontWeight all impacting the resulting size - * @returns {any} - The width for the given text - */ -export const calculateTextWidth = function (text, config) { - config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config); - return calculateTextDimensions(text, config).width; -}; - -export const getTextObj = function () { - return { - x: 0, - y: 0, - fill: undefined, - anchor: 'start', - style: '#666', - width: 100, - height: 100, - textMargin: 0, - rx: 0, - ry: 0, - valign: undefined, - }; -}; - -/** - * Adds text to an element - * - * @param {SVGElement} elem Element to add text to - * @param {{ - * text: string; - * x: number; - * y: number; - * anchor: 'start' | 'middle' | 'end'; - * fontFamily: string; - * fontSize: string | number; - * fontWeight: string | number; - * fill: string; - * class: string | undefined; - * textMargin: number; - * }} textData - * @returns {SVGTextElement} Text element with given styling and content - */ -export const drawSimpleText = function (elem, textData) { - // Remove and ignore br:s - const nText = textData.text.replace(lineBreakRegex, ' '); - - const textElem = elem.append('text'); - textElem.attr('x', textData.x); - textElem.attr('y', textData.y); - textElem.style('text-anchor', textData.anchor); - textElem.style('font-family', textData.fontFamily); - textElem.style('font-size', textData.fontSize); - textElem.style('font-weight', textData.fontWeight); - textElem.attr('fill', textData.fill); - if (typeof textData.class !== 'undefined') { - textElem.attr('class', textData.class); - } - - const span = textElem.append('tspan'); - span.attr('x', textData.x + textData.textMargin * 2); - span.attr('fill', textData.fill); - span.text(nText); - - return textElem; -}; - -/** - * This calculates the dimensions of the given text, font size, font family, font weight, and margins. - * - * @param {any} text - The text to calculate the width of - * @param {any} config - The config for fontSize, fontFamily, fontWeight, and margin all impacting - * the resulting size - * @returns - The width for the given text - */ -export const calculateTextDimensions = memoize( - function (text, config) { - config = Object.assign({ fontSize: 12, fontWeight: 400, fontFamily: 'Arial' }, config); - const { fontSize, fontFamily, fontWeight } = config; - if (!text) { - return { width: 0, height: 0 }; - } - - // We can't really know if the user supplied font family will render on the user agent; - // thus, we'll take the max width between the user supplied font family, and a default - // of sans-serif. - const fontFamilies = ['sans-serif', fontFamily]; - const lines = text.split(common.lineBreakRegex); - let dims = []; - - const body = select('body'); - // We don't want to leak DOM elements - if a removal operation isn't available - // for any reason, do not continue. - if (!body.remove) { - return { width: 0, height: 0, lineHeight: 0 }; - } - - const g = body.append('svg'); - - for (let fontFamily of fontFamilies) { - let cheight = 0; - let dim = { width: 0, height: 0, lineHeight: 0 }; - for (let line of lines) { - const textObj = getTextObj(); - textObj.text = line; - const textElem = drawSimpleText(g, textObj) - .style('font-size', fontSize) - .style('font-weight', fontWeight) - .style('font-family', fontFamily); - - let bBox = (textElem._groups || textElem)[0][0].getBBox(); - dim.width = Math.round(Math.max(dim.width, bBox.width)); - cheight = Math.round(bBox.height); - dim.height += cheight; - dim.lineHeight = Math.round(Math.max(dim.lineHeight, cheight)); - } - dims.push(dim); - } - - g.remove(); - - let index = - isNaN(dims[1].height) || - isNaN(dims[1].width) || - isNaN(dims[1].lineHeight) || - (dims[0].height > dims[1].height && - dims[0].width > dims[1].width && - dims[0].lineHeight > dims[1].lineHeight) - ? 0 - : 1; - return dims[index]; - }, - (text, config) => `${text}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}` -); - -const breakString = memoize( - (word, maxWidth, hyphenCharacter = '-', config) => { - config = Object.assign( - { fontSize: 12, fontWeight: 400, fontFamily: 'Arial', margin: 0 }, - config - ); - const characters = word.split(''); - const lines = []; - let currentLine = ''; - characters.forEach((character, index) => { - const nextLine = `${currentLine}${character}`; - const lineWidth = calculateTextWidth(nextLine, config); - if (lineWidth >= maxWidth) { - const currentCharacter = index + 1; - const isLastLine = characters.length === currentCharacter; - const hyphenatedNextLine = `${nextLine}${hyphenCharacter}`; - lines.push(isLastLine ? nextLine : hyphenatedNextLine); - currentLine = ''; - } else { - currentLine = nextLine; - } - }); - return { hyphenatedStrings: lines, remainingWord: currentLine }; - }, - (word, maxWidth, hyphenCharacter = '-', config) => - `${word}-${maxWidth}-${hyphenCharacter}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}` -); - -export const wrapLabel = memoize( - (label, maxWidth, config) => { - if (!label) { - return label; - } - config = Object.assign( - { fontSize: 12, fontWeight: 400, fontFamily: 'Arial', joinWith: '
' }, - config - ); - if (lineBreakRegex.test(label)) { - return label; - } - const words = label.split(' '); - const completedLines = []; - let nextLine = ''; - words.forEach((word, index) => { - const wordLength = calculateTextWidth(`${word} `, config); - const nextLineLength = calculateTextWidth(nextLine, config); - if (wordLength > maxWidth) { - const { hyphenatedStrings, remainingWord } = breakString(word, maxWidth, '-', config); - completedLines.push(nextLine, ...hyphenatedStrings); - nextLine = remainingWord; - } else if (nextLineLength + wordLength >= maxWidth) { - completedLines.push(nextLine); - nextLine = word; - } else { - nextLine = [nextLine, word].filter(Boolean).join(' '); - } - const currentWord = index + 1; - const isLastWord = currentWord === words.length; - if (isLastWord) { - completedLines.push(nextLine); - } - }); - return completedLines.filter((line) => line !== '').join(config.joinWith); - }, - (label, maxWidth, config) => - `${label}-${maxWidth}-${config.fontSize}-${config.fontWeight}-${config.fontFamily}-${config.joinWith}` -); From f4a5b80effa3ce05ade8f16c66ef05e8e960f1a6 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 7 Oct 2022 16:57:28 +0800 Subject: [PATCH 4/9] fix: Load all extraDiagrams --- packages/mermaid/src/config.type.ts | 2 +- .../mermaid/src/diagram-api/detectType.ts | 13 ++++------ .../src/diagram-api/diagram-orchestration.ts | 8 ++---- .../src/diagram-api/diagramAPI.spec.ts | 3 ++- .../mermaid/src/diagram-api/diagramAPI.ts | 22 ++-------------- packages/mermaid/src/diagram-api/types.ts | 26 +++++++++++++++++++ .../mermaid/src/diagrams/c4/c4Detector.ts | 2 +- .../src/diagrams/class/classDetector-V2.ts | 2 +- .../src/diagrams/class/classDetector.ts | 2 +- .../mermaid/src/diagrams/er/erDetector.ts | 2 +- .../src/diagrams/flowchart/flowDetector-v2.ts | 2 +- .../src/diagrams/flowchart/flowDetector.ts | 2 +- .../src/diagrams/gantt/ganttDetector.ts | 2 +- .../src/diagrams/git/gitGraphDetector.ts | 2 +- .../mermaid/src/diagrams/info/infoDetector.ts | 2 +- .../mermaid/src/diagrams/pie/pieDetector.ts | 2 +- .../requirement/requirementDetector.ts | 2 +- .../src/diagrams/sequence/sequenceDetector.ts | 2 +- .../src/diagrams/state/stateDetector-V2.ts | 2 +- .../src/diagrams/state/stateDetector.ts | 2 +- .../diagrams/user-journey/journeyDetector.ts | 2 +- packages/mermaid/src/mermaid.ts | 16 +++++++----- 22 files changed, 62 insertions(+), 58 deletions(-) create mode 100644 packages/mermaid/src/diagram-api/types.ts diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index b757eb8de..305605b6c 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -3,7 +3,7 @@ import DOMPurify from 'dompurify'; export interface MermaidConfig { - extraDiagrams?: any; + extraDiagrams?: string[]; theme?: string; themeVariables?: any; themeCSS?: string; diff --git a/packages/mermaid/src/diagram-api/detectType.ts b/packages/mermaid/src/diagram-api/detectType.ts index f5a94cbc7..d7ae4ffdf 100644 --- a/packages/mermaid/src/diagram-api/detectType.ts +++ b/packages/mermaid/src/diagram-api/detectType.ts @@ -1,8 +1,6 @@ import { MermaidConfig } from '../config.type'; +import { DetectorRecord, DiagramDetector, DiagramLoader } from './types'; -export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean; -export type DiagramLoader = (() => Promise) | null; -export type DetectorRecord = { detector: DiagramDetector; loader: DiagramLoader }; const directive = /[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi; const anyComment = /\s*%%.*\n/gm; @@ -44,11 +42,10 @@ export const detectType = function (text: string, config?: MermaidConfig): strin throw new Error(`No diagram type detected for text: ${text}`); }; -export const addDetector = ( - key: string, - detector: DiagramDetector, - loader: DiagramLoader | null -) => { +export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => { + if (detectors[key]) { + throw new Error(`Detector with key ${key} already exists`); + } detectors[key] = { detector, loader }; }; diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 1693f4f51..ec73e445d 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -1,9 +1,5 @@ -import { - registerDiagram, - registerDetector, - DiagramDefinition, - DiagramDetector, -} from './diagramAPI'; +import { registerDiagram, registerDetector } from './diagramAPI'; +import { DiagramDefinition, DiagramDetector } from './types'; // // @ts-ignore: TODO Fix ts errors // import mindmapParser from '../diagrams/mindmap/parser/mindmap'; diff --git a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts index 048f5c2e2..98e38c5c5 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts @@ -1,6 +1,7 @@ -import { detectType, DiagramDetector } from './detectType'; +import { detectType } from './detectType'; import { getDiagram, registerDiagram, registerDetector } from './diagramAPI'; import { addDiagrams } from './diagram-orchestration'; +import { DiagramDetector } from './types'; addDiagrams(); diff --git a/packages/mermaid/src/diagram-api/diagramAPI.ts b/packages/mermaid/src/diagram-api/diagramAPI.ts index 002619bbb..7db0ecabd 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.ts @@ -1,10 +1,10 @@ -import { addDetector, DiagramDetector as _DiagramDetector } from './detectType'; +import { addDetector } from './detectType'; import { log as _log, setLogLevel as _setLogLevel } from '../logger'; import { getConfig as _getConfig } from '../config'; import { sanitizeText as _sanitizeText } from '../diagrams/common/common'; -import { MermaidConfig } from '../config.type'; import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox'; import { addStylesForDiagram } from '../styles'; +import { DiagramDefinition, DiagramDetector } from './types'; /* Packaging and exposing resources for externa diagrams so that they can import @@ -13,28 +13,10 @@ import { addStylesForDiagram } from '../styles'; */ export const log = _log; export const setLogLevel = _setLogLevel; -export type DiagramDetector = _DiagramDetector; export const getConfig = _getConfig; export const sanitizeText = (text: string) => _sanitizeText(text, getConfig()); export const setupGraphViewbox = _setupGraphViewbox; -export interface InjectUtils { - _log: any; - _setLogLevel: any; - _getConfig: any; - _sanitizeText: any; - _setupGraphViewbox: any; -} - -export interface DiagramDefinition { - db: any; - renderer: any; - parser: any; - styles: any; - init?: (config: MermaidConfig) => void; - injectUtils?: (utils: InjectUtils) => void; -} - const diagrams: Record = {}; const connectCallbacks: Record = {}; // TODO fix, eslint-disable-line @typescript-eslint/no-explicit-any export interface Detectors { diff --git a/packages/mermaid/src/diagram-api/types.ts b/packages/mermaid/src/diagram-api/types.ts new file mode 100644 index 000000000..30ff25969 --- /dev/null +++ b/packages/mermaid/src/diagram-api/types.ts @@ -0,0 +1,26 @@ +import { MermaidConfig } from '../config.type'; + +export interface InjectUtils { + _log: any; + _setLogLevel: any; + _getConfig: any; + _sanitizeText: any; + _setupGraphViewbox: any; +} + +export interface DiagramDefinition { + db: any; + renderer: any; + parser: any; + styles: any; + init?: (config: MermaidConfig) => void; + injectUtils?: (utils: InjectUtils) => void; +} + +export interface DetectorRecord { + detector: DiagramDetector; + loader?: DiagramLoader; +} + +export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean; +export type DiagramLoader = (() => Promise<{ id: string; diagram: DiagramDefinition }>) | null; diff --git a/packages/mermaid/src/diagrams/c4/c4Detector.ts b/packages/mermaid/src/diagrams/c4/c4Detector.ts index 2be62bff1..49ba95b8e 100644 --- a/packages/mermaid/src/diagrams/c4/c4Detector.ts +++ b/packages/mermaid/src/diagrams/c4/c4Detector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const c4Detector: DiagramDetector = (txt) => { return txt.match(/^\s*C4Context|C4Container|C4Component|C4Dynamic|C4Deployment/) !== null; diff --git a/packages/mermaid/src/diagrams/class/classDetector-V2.ts b/packages/mermaid/src/diagrams/class/classDetector-V2.ts index a0e270100..d65caf9a8 100644 --- a/packages/mermaid/src/diagrams/class/classDetector-V2.ts +++ b/packages/mermaid/src/diagrams/class/classDetector-V2.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const classDetectorV2: DiagramDetector = (txt, config) => { // If we have confgured to use dagre-wrapper then we should return true in this function for classDiagram code thus making it use the new class diagram diff --git a/packages/mermaid/src/diagrams/class/classDetector.ts b/packages/mermaid/src/diagrams/class/classDetector.ts index 19d8bd2f5..ef6389a60 100644 --- a/packages/mermaid/src/diagrams/class/classDetector.ts +++ b/packages/mermaid/src/diagrams/class/classDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const classDetector: DiagramDetector = (txt, config) => { // If we have confgured to use dagre-wrapper then we should never return true in this function diff --git a/packages/mermaid/src/diagrams/er/erDetector.ts b/packages/mermaid/src/diagrams/er/erDetector.ts index a17eafb81..5a87a949e 100644 --- a/packages/mermaid/src/diagrams/er/erDetector.ts +++ b/packages/mermaid/src/diagrams/er/erDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const erDetector: DiagramDetector = (txt) => { return txt.match(/^\s*erDiagram/) !== null; diff --git a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts index f73748c79..c2ec736c7 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const flowDetectorV2: DiagramDetector = (txt, config) => { // If we have confgured to use dagre-wrapper then we should return true in this function for graph code thus making it use the new flowchart diagram diff --git a/packages/mermaid/src/diagrams/flowchart/flowDetector.ts b/packages/mermaid/src/diagrams/flowchart/flowDetector.ts index edc9096c0..740d12847 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDetector.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const flowDetector: DiagramDetector = (txt, config) => { // If we have confired to only use new flow charts this function shohuld always return false diff --git a/packages/mermaid/src/diagrams/gantt/ganttDetector.ts b/packages/mermaid/src/diagrams/gantt/ganttDetector.ts index 926792dcf..5de167010 100644 --- a/packages/mermaid/src/diagrams/gantt/ganttDetector.ts +++ b/packages/mermaid/src/diagrams/gantt/ganttDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const ganttDetector: DiagramDetector = (txt) => { return txt.match(/^\s*gantt/) !== null; diff --git a/packages/mermaid/src/diagrams/git/gitGraphDetector.ts b/packages/mermaid/src/diagrams/git/gitGraphDetector.ts index 1c0a015e7..f890501a5 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphDetector.ts +++ b/packages/mermaid/src/diagrams/git/gitGraphDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const gitGraphDetector: DiagramDetector = (txt) => { return txt.match(/^\s*gitGraph/) !== null; diff --git a/packages/mermaid/src/diagrams/info/infoDetector.ts b/packages/mermaid/src/diagrams/info/infoDetector.ts index 68f2ac794..8bccb578f 100644 --- a/packages/mermaid/src/diagrams/info/infoDetector.ts +++ b/packages/mermaid/src/diagrams/info/infoDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const infoDetector: DiagramDetector = (txt) => { return txt.match(/^\s*info/) !== null; diff --git a/packages/mermaid/src/diagrams/pie/pieDetector.ts b/packages/mermaid/src/diagrams/pie/pieDetector.ts index 1e122b0e0..65a011c7a 100644 --- a/packages/mermaid/src/diagrams/pie/pieDetector.ts +++ b/packages/mermaid/src/diagrams/pie/pieDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const pieDetector: DiagramDetector = (txt) => { return txt.match(/^\s*pie/) !== null; diff --git a/packages/mermaid/src/diagrams/requirement/requirementDetector.ts b/packages/mermaid/src/diagrams/requirement/requirementDetector.ts index 2e1aa93ae..164da6c1a 100644 --- a/packages/mermaid/src/diagrams/requirement/requirementDetector.ts +++ b/packages/mermaid/src/diagrams/requirement/requirementDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const requirementDetector: DiagramDetector = (txt) => { return txt.match(/^\s*requirement(Diagram)?/) !== null; diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDetector.ts b/packages/mermaid/src/diagrams/sequence/sequenceDetector.ts index e68433255..52640b134 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDetector.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const sequenceDetector: DiagramDetector = (txt) => { return txt.match(/^\s*sequenceDiagram/) !== null; diff --git a/packages/mermaid/src/diagrams/state/stateDetector-V2.ts b/packages/mermaid/src/diagrams/state/stateDetector-V2.ts index 8082a47bd..7fd9633c6 100644 --- a/packages/mermaid/src/diagrams/state/stateDetector-V2.ts +++ b/packages/mermaid/src/diagrams/state/stateDetector-V2.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const stateDetectorV2: DiagramDetector = (text, config) => { if (text.match(/^\s*stateDiagram-v2/) !== null) return true; diff --git a/packages/mermaid/src/diagrams/state/stateDetector.ts b/packages/mermaid/src/diagrams/state/stateDetector.ts index 79dd6586b..614f327c2 100644 --- a/packages/mermaid/src/diagrams/state/stateDetector.ts +++ b/packages/mermaid/src/diagrams/state/stateDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const stateDetector: DiagramDetector = (txt, config) => { // If we have confired to only use new state diagrams this function should always return false diff --git a/packages/mermaid/src/diagrams/user-journey/journeyDetector.ts b/packages/mermaid/src/diagrams/user-journey/journeyDetector.ts index 77c8688ae..535e7be9d 100644 --- a/packages/mermaid/src/diagrams/user-journey/journeyDetector.ts +++ b/packages/mermaid/src/diagrams/user-journey/journeyDetector.ts @@ -1,4 +1,4 @@ -import type { DiagramDetector } from '../../diagram-api/detectType'; +import type { DiagramDetector } from '../../diagram-api/types'; export const journeyDetector: DiagramDetector = (txt) => { return txt.match(/^\s*journey/) !== null; diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 07bd2ccfe..62430bf19 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -9,13 +9,13 @@ import { mermaidAPI } from './mermaidAPI'; import { addDetector } from './diagram-api/detectType'; import { registerDiagram, - DiagramDefinition, setLogLevel, getConfig, setupGraphViewbox, sanitizeText, } from './diagram-api/diagramAPI'; import { isDetailedError } from './utils'; +import { DiagramDefinition } from './diagram-api/types'; /** * ## init @@ -54,12 +54,14 @@ const init = async function ( ) { try { log.info('Detectors in init', mermaid.detectors); // eslint-disable-line - const conf = mermaidAPI.getConfig(); - if (typeof conf.extraDiagrams !== 'undefined' && conf.extraDiagrams.length > 0) { - // config.extraDiagrams.forEach(async (diagram: string) => { - const { id, detector, loadDiagram } = await import(conf.extraDiagrams[0]); - addDetector(id, detector, loadDiagram); - // }); + const conf = config; // TODO OR mermaidAPI.getConfig(); ? + if (conf?.extraDiagrams && conf.extraDiagrams.length > 0) { + await Promise.allSettled( + conf.extraDiagrams.map(async (diagram: string) => { + const { id, detector, loadDiagram } = await import(diagram); + addDetector(id, detector, loadDiagram); + }) + ); } mermaid.detectors.forEach(({ id, detector, path }) => { addDetector(id, detector, path); From 6fb92f6f3c6ee8ccb6bb3851483cf3af3e5160a2 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 7 Oct 2022 17:14:33 +0800 Subject: [PATCH 5/9] fix: Restore conf. --- packages/mermaid/src/mermaid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 62430bf19..04ce185d4 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -54,7 +54,7 @@ const init = async function ( ) { try { log.info('Detectors in init', mermaid.detectors); // eslint-disable-line - const conf = config; // TODO OR mermaidAPI.getConfig(); ? + const conf = mermaidAPI.getConfig(); if (conf?.extraDiagrams && conf.extraDiagrams.length > 0) { await Promise.allSettled( conf.extraDiagrams.map(async (diagram: string) => { From 3698b30809d7c95f5babb5a12b90ccb0c3a2f64b Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 8 Oct 2022 13:14:16 +0800 Subject: [PATCH 6/9] fix: Optimize diagram loading --- packages/mermaid/src/__mocks__/mermaidAPI.ts | 6 +- .../mermaid/src/diagram-api/detectType.ts | 2 + .../src/diagram-api/diagram-orchestration.ts | 63 +++++++------------ .../src/diagram-api/diagramAPI.spec.ts | 19 +++--- .../mermaid/src/diagram-api/diagramAPI.ts | 8 +-- packages/mermaid/src/mermaid.ts | 6 -- packages/mermaid/src/mermaidAPI.ts | 19 +----- 7 files changed, 44 insertions(+), 79 deletions(-) diff --git a/packages/mermaid/src/__mocks__/mermaidAPI.ts b/packages/mermaid/src/__mocks__/mermaidAPI.ts index f15db139f..08c5b7eea 100644 --- a/packages/mermaid/src/__mocks__/mermaidAPI.ts +++ b/packages/mermaid/src/__mocks__/mermaidAPI.ts @@ -11,17 +11,13 @@ import Diagram from '../Diagram'; // Normally, we could just do the following to get the original `parse()` // implementation, however, requireActual returns a promise and it's not documented how to use withing mock file. -let hasLoadedDiagrams = false; /** * @param text * @param parseError */ // eslint-disable-next-line @typescript-eslint/ban-types function parse(text: string, parseError?: Function): boolean { - if (!hasLoadedDiagrams) { - addDiagrams(); - hasLoadedDiagrams = true; - } + addDiagrams(); const diagram = new Diagram(text, parseError); return diagram.parse(text, parseError); } diff --git a/packages/mermaid/src/diagram-api/detectType.ts b/packages/mermaid/src/diagram-api/detectType.ts index d7ae4ffdf..9536fded2 100644 --- a/packages/mermaid/src/diagram-api/detectType.ts +++ b/packages/mermaid/src/diagram-api/detectType.ts @@ -1,4 +1,5 @@ import { MermaidConfig } from '../config.type'; +import { log } from '../logger'; import { DetectorRecord, DiagramDetector, DiagramLoader } from './types'; const directive = @@ -47,6 +48,7 @@ export const addDetector = (key: string, detector: DiagramDetector, loader?: Dia throw new Error(`Detector with key ${key} already exists`); } detectors[key] = { detector, loader }; + log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`); }; export const getDiagramLoader = (key: string) => detectors[key].loader; diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index ec73e445d..a26edb303 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -1,12 +1,4 @@ -import { registerDiagram, registerDetector } from './diagramAPI'; -import { DiagramDefinition, DiagramDetector } from './types'; - -// // @ts-ignore: TODO Fix ts errors -// import mindmapParser from '../diagrams/mindmap/parser/mindmap'; -// import * as mindmapDb from '../diagrams/mindmap/mindmapDb'; -// import { mindmapDetector } from '../diagrams/mindmap/mindmapDetector'; -// import mindmapRenderer from '../diagrams/mindmap/mindmapRenderer'; -// import mindmapStyles from '../diagrams/mindmap/styles'; +import { registerDiagram } from './diagramAPI'; // @ts-ignore: TODO Fix ts errors import gitGraphParser from '../diagrams/git/parser/gitGraph'; @@ -102,17 +94,15 @@ import { setConfig } from '../config'; import errorRenderer from '../diagrams/error/errorRenderer'; import errorStyles from '../diagrams/error/styles'; -const registerDiagramAndDetector = ( - id: string, - diagram: DiagramDefinition, - detector: DiagramDetector -) => { - registerDiagram(id, diagram); - registerDetector(id, detector); -}; - +let hasLoadedDiagrams = false; export const addDiagrams = () => { - registerDiagramAndDetector( + if (hasLoadedDiagrams) { + return; + } + // This is added here to avoid race-conditions. + // We could optimize the loading logic somehow. + hasLoadedDiagrams = true; + registerDiagram( 'error', // Special diagram with error messages but setup as a regular diagram { @@ -136,7 +126,7 @@ export const addDiagrams = () => { (text) => text.toLowerCase().trim() === 'error' ); - registerDiagramAndDetector( + registerDiagram( 'c4', { parser: c4Parser, @@ -149,7 +139,7 @@ export const addDiagrams = () => { }, c4Detector ); - registerDiagramAndDetector( + registerDiagram( 'class', { parser: classParser, @@ -166,7 +156,7 @@ export const addDiagrams = () => { }, classDetector ); - registerDiagramAndDetector( + registerDiagram( 'classDiagram', { parser: classParser, @@ -183,7 +173,7 @@ export const addDiagrams = () => { }, classDetectorV2 ); - registerDiagramAndDetector( + registerDiagram( 'er', { parser: erParser, @@ -193,7 +183,7 @@ export const addDiagrams = () => { }, erDetector ); - registerDiagramAndDetector( + registerDiagram( 'gantt', { parser: ganttParser, @@ -203,7 +193,7 @@ export const addDiagrams = () => { }, ganttDetector ); - registerDiagramAndDetector( + registerDiagram( 'info', { parser: infoParser, @@ -213,7 +203,7 @@ export const addDiagrams = () => { }, infoDetector ); - registerDiagramAndDetector( + registerDiagram( 'pie', { parser: pieParser, @@ -223,7 +213,7 @@ export const addDiagrams = () => { }, pieDetector ); - registerDiagramAndDetector( + registerDiagram( 'requirement', { parser: requirementParser, @@ -233,7 +223,7 @@ export const addDiagrams = () => { }, requirementDetector ); - registerDiagramAndDetector( + registerDiagram( 'sequence', { parser: sequenceParser, @@ -256,7 +246,7 @@ export const addDiagrams = () => { }, sequenceDetector ); - registerDiagramAndDetector( + registerDiagram( 'state', { parser: stateParser, @@ -273,7 +263,7 @@ export const addDiagrams = () => { }, stateDetector ); - registerDiagramAndDetector( + registerDiagram( 'stateDiagram', { parser: stateParser, @@ -290,7 +280,7 @@ export const addDiagrams = () => { }, stateDetectorV2 ); - registerDiagramAndDetector( + registerDiagram( 'journey', { parser: journeyParser, @@ -305,7 +295,7 @@ export const addDiagrams = () => { journeyDetector ); - registerDiagramAndDetector( + registerDiagram( 'flowchart', { parser: flowParser, @@ -325,7 +315,7 @@ export const addDiagrams = () => { }, flowDetector ); - registerDiagramAndDetector( + registerDiagram( 'flowchart-v2', { parser: flowParser, @@ -346,14 +336,9 @@ export const addDiagrams = () => { }, flowDetectorV2 ); - registerDiagramAndDetector( + registerDiagram( 'gitGraph', { parser: gitGraphParser, db: gitGraphDb, renderer: gitGraphRenderer, styles: gitGraphStyles }, gitGraphDetector ); - // registerDiagram( - // 'mindmap', - // { parser: mindmapParser, db: mindmapDb, renderer: mindmapRenderer, styles: mindmapStyles }, - // mindmapDetector - // ); }; diff --git a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts index 98e38c5c5..ea546fbb6 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.spec.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.spec.ts @@ -1,5 +1,5 @@ import { detectType } from './detectType'; -import { getDiagram, registerDiagram, registerDetector } from './diagramAPI'; +import { getDiagram, registerDiagram } from './diagramAPI'; import { addDiagrams } from './diagram-orchestration'; import { DiagramDetector } from './types'; @@ -22,13 +22,16 @@ describe('DiagramAPI', () => { const detector: DiagramDetector = (str: string) => { return str.match('loki') !== null; }; - registerDetector('loki', detector); - registerDiagram('loki', { - db: {}, - parser: {}, - renderer: {}, - styles: {}, - }); + registerDiagram( + 'loki', + { + db: {}, + parser: {}, + renderer: {}, + styles: {}, + }, + detector + ); expect(getDiagram('loki')).not.toBeNull(); expect(detectType('loki diagram')).toBe('loki'); }); diff --git a/packages/mermaid/src/diagram-api/diagramAPI.ts b/packages/mermaid/src/diagram-api/diagramAPI.ts index 7db0ecabd..ceb098ce3 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.ts @@ -23,13 +23,10 @@ export interface Detectors { [key: string]: DiagramDetector; } -export const registerDetector = (id: string, detector: DiagramDetector) => { - addDetector(id, detector, null); -}; - export const registerDiagram = ( id: string, diagram: DiagramDefinition, + detector: DiagramDetector, callback?: ( _log: any, _setLogLevel: any, @@ -39,9 +36,10 @@ export const registerDiagram = ( ) => void ) => { if (diagrams[id]) { - log.warn(`Diagram ${id} already registered.`); + throw new Error(`Diagram ${id} already registered.`); } diagrams[id] = diagram; + addDetector(id, detector); addStylesForDiagram(id, diagram.styles); if (typeof callback !== 'undefined') { callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox); diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 875c5411a..b2f346b18 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -53,7 +53,6 @@ const init = async function ( callback?: Function ) { try { - log.info('Detectors in init', mermaid.detectors); // eslint-disable-line const conf = mermaidAPI.getConfig(); if (conf?.lazyLoadedDiagrams && conf.lazyLoadedDiagrams.length > 0) { // Load all lazy loaded diagrams in parallel @@ -64,9 +63,6 @@ const init = async function ( }) ); } - mermaid.detectors.forEach(({ id, detector, path }) => { - addDetector(id, detector, path); - }); await initThrowsErrors(config, nodes, callback); } catch (e) { log.warn('Syntax Error rendering'); @@ -251,7 +247,6 @@ const mermaid: { contentLoaded: typeof contentLoaded; setParseErrorHandler: typeof setParseErrorHandler; // Array of functions to use for detecting diagram types - detectors: Array; // eslint-disable-line @typescript-eslint/no-explicit-any connectDiagram: (id: string, diagram: DiagramDefinition, callback: (id: string) => void) => void; } = { startOnLoad: true, @@ -265,7 +260,6 @@ const mermaid: { parseError: undefined, contentLoaded, setParseErrorHandler, - detectors: [], connectDiagram: connectDiagram, }; diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index f9544ee44..7c967e5fd 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -18,7 +18,6 @@ import { compile, serialize, stringify } from 'stylis'; import pkg from '../package.json'; import * as configApi from './config'; import { addDiagrams } from './diagram-api/diagram-orchestration'; -import { addDetector } from './diagram-api/detectType'; import classDb from './diagrams/class/classDb'; import flowDb from './diagrams/flowchart/flowDb'; import flowRenderer from './diagrams/flowchart/flowRenderer'; @@ -34,18 +33,13 @@ import DOMPurify from 'dompurify'; import { MermaidConfig } from './config.type'; import { evaluate } from './diagrams/common/common'; -let hasLoadedDiagrams = false; - /** * @param text * @param parseError */ // eslint-disable-next-line @typescript-eslint/ban-types function parse(text: string, parseError?: Function): boolean { - if (!hasLoadedDiagrams) { - addDiagrams(); - hasLoadedDiagrams = true; - } + addDiagrams(); const diagram = new Diagram(text, parseError); return diagram.parse(text, parseError); } @@ -122,10 +116,7 @@ const render = async function ( cb: (svgCode: string, bindFunctions?: (element: Element) => void) => void, container?: Element ): Promise { - if (!hasLoadedDiagrams) { - addDiagrams(); - hasLoadedDiagrams = true; - } + addDiagrams(); configApi.reset(); text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;; const graphInit = utils.detectInit(text); @@ -486,11 +477,7 @@ async function initialize(options: MermaidConfig) { typeof options === 'object' ? configApi.setSiteConfig(options) : configApi.getSiteConfig(); setLogLevel(config.logLevel); - - if (!hasLoadedDiagrams) { - addDiagrams(); - hasLoadedDiagrams = true; - } + addDiagrams(); } export const mermaidAPI = Object.freeze({ From 97b39bca95bf3644943606b4d1506b95ea1466b9 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 8 Oct 2022 13:22:06 +0800 Subject: [PATCH 7/9] fix: Remove connectDiagram --- packages/mermaid/src/mermaid.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index b2f346b18..2f54f0533 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -217,22 +217,6 @@ const parse = (txt: string) => { return mermaidAPI.parse(txt, mermaid.parseError); }; -const connectDiagram = ( - id: string, - diagram: DiagramDefinition, - callback: ( - _log: any, - _setLogLevel: any, - _getConfig: any, - _sanitizeText: any, - _setupGraphViewbox: any - ) => void -) => { - registerDiagram(id, diagram, callback); - // Todo move this connect call to after the diagram is actually loaded. - callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox); -}; - const mermaid: { startOnLoad: boolean; diagrams: any; @@ -246,8 +230,6 @@ const mermaid: { initialize: typeof initialize; contentLoaded: typeof contentLoaded; setParseErrorHandler: typeof setParseErrorHandler; - // Array of functions to use for detecting diagram types - connectDiagram: (id: string, diagram: DiagramDefinition, callback: (id: string) => void) => void; } = { startOnLoad: true, diagrams: {}, @@ -260,7 +242,6 @@ const mermaid: { parseError: undefined, contentLoaded, setParseErrorHandler, - connectDiagram: connectDiagram, }; export default mermaid; From de5ad8644e8199f27c3df7c1b7a7732159c3a836 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 8 Oct 2022 13:38:40 +0800 Subject: [PATCH 8/9] fix: Optional detector --- packages/mermaid/src/Diagram.ts | 24 +++++-------------- .../mermaid/src/diagram-api/diagramAPI.ts | 6 +++-- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 179c72264..0aa741994 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -81,10 +81,8 @@ export class Diagram { } } -export default Diagram; - // eslint-disable-next-line @typescript-eslint/ban-types -export const getDiagramFromText = async (txt: string, parseError?: Function) => { +export const getDiagramFromText = async (txt: string, parseError?: Function): Promise => { const type = detectType(txt, configApi.getConfig()); try { // Trying to find the diagram @@ -94,24 +92,14 @@ export const getDiagramFromText = async (txt: string, parseError?: Function) => if (!loader) { throw new Error(`Diagram ${type} not found.`); } - // Diagram not avaiable, loading it - // const path = getPathForDiagram(type); - const { diagram } = await loader(); // eslint-disable-line @typescript-eslint/no-explicit-any - registerDiagram( - type, - { - db: diagram.db, - renderer: diagram.renderer, - parser: diagram.parser, - styles: diagram.styles, - }, - diagram.injectUtils - ); - // await loadDiagram('./packages/mermaid-mindmap/dist/mermaid-mindmap.js'); - // await loadDiagram(path + 'mermaid-' + type + '.js'); + // Diagram not available, loading it + const { diagram } = await loader(); + registerDiagram(type, diagram, undefined, diagram.injectUtils); // new diagram will try getDiagram again and if fails then it is a valid throw } // If either of the above worked, we have the diagram // logic and can continue return new Diagram(txt, parseError); }; + +export default Diagram; diff --git a/packages/mermaid/src/diagram-api/diagramAPI.ts b/packages/mermaid/src/diagram-api/diagramAPI.ts index ceb098ce3..806252554 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.ts @@ -26,7 +26,7 @@ export interface Detectors { export const registerDiagram = ( id: string, diagram: DiagramDefinition, - detector: DiagramDetector, + detector?: DiagramDetector, callback?: ( _log: any, _setLogLevel: any, @@ -39,7 +39,9 @@ export const registerDiagram = ( throw new Error(`Diagram ${id} already registered.`); } diagrams[id] = diagram; - addDetector(id, detector); + if (detector) { + addDetector(id, detector); + } addStylesForDiagram(id, diagram.styles); if (typeof callback !== 'undefined') { callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox); From 738abc894690d2f04e8020aa838f5f6d966a6ee9 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 8 Oct 2022 13:41:04 +0800 Subject: [PATCH 9/9] chore: Cleanup --- packages/mermaid/src/diagram-api/diagramAPI.ts | 17 ----------------- packages/mermaid/src/mermaid.ts | 8 -------- 2 files changed, 25 deletions(-) diff --git a/packages/mermaid/src/diagram-api/diagramAPI.ts b/packages/mermaid/src/diagram-api/diagramAPI.ts index 806252554..2bc8091ec 100644 --- a/packages/mermaid/src/diagram-api/diagramAPI.ts +++ b/packages/mermaid/src/diagram-api/diagramAPI.ts @@ -18,7 +18,6 @@ export const sanitizeText = (text: string) => _sanitizeText(text, getConfig()); export const setupGraphViewbox = _setupGraphViewbox; const diagrams: Record = {}; -const connectCallbacks: Record = {}; // TODO fix, eslint-disable-line @typescript-eslint/no-explicit-any export interface Detectors { [key: string]: DiagramDetector; } @@ -54,19 +53,3 @@ export const getDiagram = (name: string): DiagramDefinition => { } throw new Error(`Diagram ${name} not found.`); }; - -/** - * - * @param sScriptSrc - */ -export const loadDiagram = (sScriptSrc: string) => - new Promise((resolve) => { - const oHead = document.getElementsByTagName('HEAD')[0]; - const oScript = document.createElement('script'); - oScript.type = 'text/javascript'; - oScript.src = sScriptSrc; - oHead.appendChild(oScript); - oScript.onload = () => { - resolve(true); - }; - }); diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 2f54f0533..ae6c62547 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -7,15 +7,7 @@ import { log } from './logger'; import utils from './utils'; import { mermaidAPI } from './mermaidAPI'; import { addDetector } from './diagram-api/detectType'; -import { - registerDiagram, - setLogLevel, - getConfig, - setupGraphViewbox, - sanitizeText, -} from './diagram-api/diagramAPI'; import { isDetailedError } from './utils'; -import { DiagramDefinition } from './diagram-api/types'; /** * ## init