fix: Optimize diagram loading

This commit is contained in:
Sidharth Vinod 2022-10-08 13:14:16 +08:00
parent 069437842b
commit 3698b30809
No known key found for this signature in database
GPG Key ID: FB5CCD378D3907CD
7 changed files with 44 additions and 79 deletions

View File

@ -11,17 +11,13 @@ import Diagram from '../Diagram';
// Normally, we could just do the following to get the original `parse()` // 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. // implementation, however, requireActual returns a promise and it's not documented how to use withing mock file.
let hasLoadedDiagrams = false;
/** /**
* @param text * @param text
* @param parseError * @param parseError
*/ */
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
function parse(text: string, parseError?: Function): boolean { function parse(text: string, parseError?: Function): boolean {
if (!hasLoadedDiagrams) { addDiagrams();
addDiagrams();
hasLoadedDiagrams = true;
}
const diagram = new Diagram(text, parseError); const diagram = new Diagram(text, parseError);
return diagram.parse(text, parseError); return diagram.parse(text, parseError);
} }

View File

@ -1,4 +1,5 @@
import { MermaidConfig } from '../config.type'; import { MermaidConfig } from '../config.type';
import { log } from '../logger';
import { DetectorRecord, DiagramDetector, DiagramLoader } from './types'; import { DetectorRecord, DiagramDetector, DiagramLoader } from './types';
const directive = const directive =
@ -47,6 +48,7 @@ export const addDetector = (key: string, detector: DiagramDetector, loader?: Dia
throw new Error(`Detector with key ${key} already exists`); throw new Error(`Detector with key ${key} already exists`);
} }
detectors[key] = { detector, loader }; detectors[key] = { detector, loader };
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
}; };
export const getDiagramLoader = (key: string) => detectors[key].loader; export const getDiagramLoader = (key: string) => detectors[key].loader;

View File

@ -1,12 +1,4 @@
import { registerDiagram, registerDetector } from './diagramAPI'; import { registerDiagram } 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';
// @ts-ignore: TODO Fix ts errors // @ts-ignore: TODO Fix ts errors
import gitGraphParser from '../diagrams/git/parser/gitGraph'; import gitGraphParser from '../diagrams/git/parser/gitGraph';
@ -102,17 +94,15 @@ import { setConfig } from '../config';
import errorRenderer from '../diagrams/error/errorRenderer'; import errorRenderer from '../diagrams/error/errorRenderer';
import errorStyles from '../diagrams/error/styles'; import errorStyles from '../diagrams/error/styles';
const registerDiagramAndDetector = ( let hasLoadedDiagrams = false;
id: string,
diagram: DiagramDefinition,
detector: DiagramDetector
) => {
registerDiagram(id, diagram);
registerDetector(id, detector);
};
export const addDiagrams = () => { 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', 'error',
// Special diagram with error messages but setup as a regular diagram // Special diagram with error messages but setup as a regular diagram
{ {
@ -136,7 +126,7 @@ export const addDiagrams = () => {
(text) => text.toLowerCase().trim() === 'error' (text) => text.toLowerCase().trim() === 'error'
); );
registerDiagramAndDetector( registerDiagram(
'c4', 'c4',
{ {
parser: c4Parser, parser: c4Parser,
@ -149,7 +139,7 @@ export const addDiagrams = () => {
}, },
c4Detector c4Detector
); );
registerDiagramAndDetector( registerDiagram(
'class', 'class',
{ {
parser: classParser, parser: classParser,
@ -166,7 +156,7 @@ export const addDiagrams = () => {
}, },
classDetector classDetector
); );
registerDiagramAndDetector( registerDiagram(
'classDiagram', 'classDiagram',
{ {
parser: classParser, parser: classParser,
@ -183,7 +173,7 @@ export const addDiagrams = () => {
}, },
classDetectorV2 classDetectorV2
); );
registerDiagramAndDetector( registerDiagram(
'er', 'er',
{ {
parser: erParser, parser: erParser,
@ -193,7 +183,7 @@ export const addDiagrams = () => {
}, },
erDetector erDetector
); );
registerDiagramAndDetector( registerDiagram(
'gantt', 'gantt',
{ {
parser: ganttParser, parser: ganttParser,
@ -203,7 +193,7 @@ export const addDiagrams = () => {
}, },
ganttDetector ganttDetector
); );
registerDiagramAndDetector( registerDiagram(
'info', 'info',
{ {
parser: infoParser, parser: infoParser,
@ -213,7 +203,7 @@ export const addDiagrams = () => {
}, },
infoDetector infoDetector
); );
registerDiagramAndDetector( registerDiagram(
'pie', 'pie',
{ {
parser: pieParser, parser: pieParser,
@ -223,7 +213,7 @@ export const addDiagrams = () => {
}, },
pieDetector pieDetector
); );
registerDiagramAndDetector( registerDiagram(
'requirement', 'requirement',
{ {
parser: requirementParser, parser: requirementParser,
@ -233,7 +223,7 @@ export const addDiagrams = () => {
}, },
requirementDetector requirementDetector
); );
registerDiagramAndDetector( registerDiagram(
'sequence', 'sequence',
{ {
parser: sequenceParser, parser: sequenceParser,
@ -256,7 +246,7 @@ export const addDiagrams = () => {
}, },
sequenceDetector sequenceDetector
); );
registerDiagramAndDetector( registerDiagram(
'state', 'state',
{ {
parser: stateParser, parser: stateParser,
@ -273,7 +263,7 @@ export const addDiagrams = () => {
}, },
stateDetector stateDetector
); );
registerDiagramAndDetector( registerDiagram(
'stateDiagram', 'stateDiagram',
{ {
parser: stateParser, parser: stateParser,
@ -290,7 +280,7 @@ export const addDiagrams = () => {
}, },
stateDetectorV2 stateDetectorV2
); );
registerDiagramAndDetector( registerDiagram(
'journey', 'journey',
{ {
parser: journeyParser, parser: journeyParser,
@ -305,7 +295,7 @@ export const addDiagrams = () => {
journeyDetector journeyDetector
); );
registerDiagramAndDetector( registerDiagram(
'flowchart', 'flowchart',
{ {
parser: flowParser, parser: flowParser,
@ -325,7 +315,7 @@ export const addDiagrams = () => {
}, },
flowDetector flowDetector
); );
registerDiagramAndDetector( registerDiagram(
'flowchart-v2', 'flowchart-v2',
{ {
parser: flowParser, parser: flowParser,
@ -346,14 +336,9 @@ export const addDiagrams = () => {
}, },
flowDetectorV2 flowDetectorV2
); );
registerDiagramAndDetector( registerDiagram(
'gitGraph', 'gitGraph',
{ parser: gitGraphParser, db: gitGraphDb, renderer: gitGraphRenderer, styles: gitGraphStyles }, { parser: gitGraphParser, db: gitGraphDb, renderer: gitGraphRenderer, styles: gitGraphStyles },
gitGraphDetector gitGraphDetector
); );
// registerDiagram(
// 'mindmap',
// { parser: mindmapParser, db: mindmapDb, renderer: mindmapRenderer, styles: mindmapStyles },
// mindmapDetector
// );
}; };

View File

@ -1,5 +1,5 @@
import { detectType } from './detectType'; import { detectType } from './detectType';
import { getDiagram, registerDiagram, registerDetector } from './diagramAPI'; import { getDiagram, registerDiagram } from './diagramAPI';
import { addDiagrams } from './diagram-orchestration'; import { addDiagrams } from './diagram-orchestration';
import { DiagramDetector } from './types'; import { DiagramDetector } from './types';
@ -22,13 +22,16 @@ describe('DiagramAPI', () => {
const detector: DiagramDetector = (str: string) => { const detector: DiagramDetector = (str: string) => {
return str.match('loki') !== null; return str.match('loki') !== null;
}; };
registerDetector('loki', detector); registerDiagram(
registerDiagram('loki', { 'loki',
db: {}, {
parser: {}, db: {},
renderer: {}, parser: {},
styles: {}, renderer: {},
}); styles: {},
},
detector
);
expect(getDiagram('loki')).not.toBeNull(); expect(getDiagram('loki')).not.toBeNull();
expect(detectType('loki diagram')).toBe('loki'); expect(detectType('loki diagram')).toBe('loki');
}); });

View File

@ -23,13 +23,10 @@ export interface Detectors {
[key: string]: DiagramDetector; [key: string]: DiagramDetector;
} }
export const registerDetector = (id: string, detector: DiagramDetector) => {
addDetector(id, detector, null);
};
export const registerDiagram = ( export const registerDiagram = (
id: string, id: string,
diagram: DiagramDefinition, diagram: DiagramDefinition,
detector: DiagramDetector,
callback?: ( callback?: (
_log: any, _log: any,
_setLogLevel: any, _setLogLevel: any,
@ -39,9 +36,10 @@ export const registerDiagram = (
) => void ) => void
) => { ) => {
if (diagrams[id]) { if (diagrams[id]) {
log.warn(`Diagram ${id} already registered.`); throw new Error(`Diagram ${id} already registered.`);
} }
diagrams[id] = diagram; diagrams[id] = diagram;
addDetector(id, detector);
addStylesForDiagram(id, diagram.styles); addStylesForDiagram(id, diagram.styles);
if (typeof callback !== 'undefined') { if (typeof callback !== 'undefined') {
callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox); callback(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);

View File

@ -53,7 +53,6 @@ const init = async function (
callback?: Function callback?: Function
) { ) {
try { try {
log.info('Detectors in init', mermaid.detectors); // eslint-disable-line
const conf = mermaidAPI.getConfig(); const conf = mermaidAPI.getConfig();
if (conf?.lazyLoadedDiagrams && conf.lazyLoadedDiagrams.length > 0) { if (conf?.lazyLoadedDiagrams && conf.lazyLoadedDiagrams.length > 0) {
// Load all lazy loaded diagrams in parallel // 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); await initThrowsErrors(config, nodes, callback);
} catch (e) { } catch (e) {
log.warn('Syntax Error rendering'); log.warn('Syntax Error rendering');
@ -251,7 +247,6 @@ const mermaid: {
contentLoaded: typeof contentLoaded; contentLoaded: typeof contentLoaded;
setParseErrorHandler: typeof setParseErrorHandler; setParseErrorHandler: typeof setParseErrorHandler;
// Array of functions to use for detecting diagram types // Array of functions to use for detecting diagram types
detectors: Array<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
connectDiagram: (id: string, diagram: DiagramDefinition, callback: (id: string) => void) => void; connectDiagram: (id: string, diagram: DiagramDefinition, callback: (id: string) => void) => void;
} = { } = {
startOnLoad: true, startOnLoad: true,
@ -265,7 +260,6 @@ const mermaid: {
parseError: undefined, parseError: undefined,
contentLoaded, contentLoaded,
setParseErrorHandler, setParseErrorHandler,
detectors: [],
connectDiagram: connectDiagram, connectDiagram: connectDiagram,
}; };

View File

@ -18,7 +18,6 @@ import { compile, serialize, stringify } from 'stylis';
import pkg from '../package.json'; import pkg from '../package.json';
import * as configApi from './config'; import * as configApi from './config';
import { addDiagrams } from './diagram-api/diagram-orchestration'; import { addDiagrams } from './diagram-api/diagram-orchestration';
import { addDetector } from './diagram-api/detectType';
import classDb from './diagrams/class/classDb'; import classDb from './diagrams/class/classDb';
import flowDb from './diagrams/flowchart/flowDb'; import flowDb from './diagrams/flowchart/flowDb';
import flowRenderer from './diagrams/flowchart/flowRenderer'; import flowRenderer from './diagrams/flowchart/flowRenderer';
@ -34,18 +33,13 @@ import DOMPurify from 'dompurify';
import { MermaidConfig } from './config.type'; import { MermaidConfig } from './config.type';
import { evaluate } from './diagrams/common/common'; import { evaluate } from './diagrams/common/common';
let hasLoadedDiagrams = false;
/** /**
* @param text * @param text
* @param parseError * @param parseError
*/ */
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
function parse(text: string, parseError?: Function): boolean { function parse(text: string, parseError?: Function): boolean {
if (!hasLoadedDiagrams) { addDiagrams();
addDiagrams();
hasLoadedDiagrams = true;
}
const diagram = new Diagram(text, parseError); const diagram = new Diagram(text, parseError);
return diagram.parse(text, parseError); return diagram.parse(text, parseError);
} }
@ -122,10 +116,7 @@ const render = async function (
cb: (svgCode: string, bindFunctions?: (element: Element) => void) => void, cb: (svgCode: string, bindFunctions?: (element: Element) => void) => void,
container?: Element container?: Element
): Promise<void> { ): Promise<void> {
if (!hasLoadedDiagrams) { addDiagrams();
addDiagrams();
hasLoadedDiagrams = true;
}
configApi.reset(); configApi.reset();
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;; text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
const graphInit = utils.detectInit(text); const graphInit = utils.detectInit(text);
@ -486,11 +477,7 @@ async function initialize(options: MermaidConfig) {
typeof options === 'object' ? configApi.setSiteConfig(options) : configApi.getSiteConfig(); typeof options === 'object' ? configApi.setSiteConfig(options) : configApi.getSiteConfig();
setLogLevel(config.logLevel); setLogLevel(config.logLevel);
addDiagrams();
if (!hasLoadedDiagrams) {
addDiagrams();
hasLoadedDiagrams = true;
}
} }
export const mermaidAPI = Object.freeze({ export const mermaidAPI = Object.freeze({