tsConversion: mermaid main

This commit is contained in:
Sidharth Vinod
2022-08-21 18:37:27 +05:30
parent cd4b1ea245
commit c68ec54fdd
9 changed files with 92 additions and 108 deletions

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
demos/*.html

View File

@@ -6,7 +6,7 @@ export default {
amd: false, // https://github.com/lodash/lodash/issues/3052 amd: false, // https://github.com/lodash/lodash/issues/3052
target: 'web', target: 'web',
entry: { entry: {
mermaid: './src/mermaid.ts', mermaid: './src/mermaid',
}, },
resolve: { resolve: {
extensions: ['.wasm', '.mjs', '.js', '.ts', '.json', '.jison'], extensions: ['.wasm', '.mjs', '.js', '.ts', '.json', '.jison'],

View File

@@ -4,7 +4,7 @@ import { merge } from 'webpack-merge';
export default merge(baseConfig, { export default merge(baseConfig, {
mode: 'development', mode: 'development',
entry: { entry: {
mermaid: './src/mermaid.ts', mermaid: './src/mermaid',
e2e: './cypress/platform/viewer.js', e2e: './cypress/platform/viewer.js',
'bundle-test': './cypress/platform/bundle-test.js', 'bundle-test': './cypress/platform/bundle-test.js',
}, },

View File

@@ -31,6 +31,7 @@
"lint:fix": "yarn lint --fix", "lint:fix": "yarn lint --fix",
"e2e:depr": "yarn lint && jest e2e --config e2e/jest.config.js", "e2e:depr": "yarn lint && jest e2e --config e2e/jest.config.js",
"cypress": "cypress run", "cypress": "cypress run",
"cypress:open": "cypress open",
"e2e": "start-server-and-test dev http://localhost:9000/ cypress", "e2e": "start-server-and-test dev http://localhost:9000/ cypress",
"e2e-upd": "yarn lint && jest e2e -u --config e2e/jest.config.js", "e2e-upd": "yarn lint && jest e2e -u --config e2e/jest.config.js",
"dev": "webpack serve --config ./.webpack/webpack.config.e2e.babel.js", "dev": "webpack serve --config ./.webpack/webpack.config.e2e.babel.js",
@@ -129,4 +130,4 @@
"**/*.css", "**/*.css",
"**/*.scss" "**/*.scss"
] ]
} }

View File

@@ -8,6 +8,7 @@ export interface MermaidConfig {
themeCSS?: string; themeCSS?: string;
maxTextSize?: number; maxTextSize?: number;
darkMode?: boolean; darkMode?: boolean;
htmlLabels?: boolean;
fontFamily?: string; fontFamily?: string;
logLevel?: number; logLevel?: number;
securityLevel?: string; securityLevel?: string;

View File

@@ -2,16 +2,17 @@ import assignWithDepth from './assignWithDepth';
import { log } from './logger'; import { log } from './logger';
import theme from './themes'; import theme from './themes';
import config from './defaultConfig'; import config from './defaultConfig';
import { MermaidConfig } from 'types/config';
export const defaultConfig = Object.freeze(config); export const defaultConfig: MermaidConfig = Object.freeze(config);
let siteConfig = assignWithDepth({}, defaultConfig); let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let configFromInitialize; let configFromInitialize: MermaidConfig;
let directives = []; let directives: any[] = [];
let currentConfig = assignWithDepth({}, defaultConfig); let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
export const updateCurrentConfig = (siteCfg, _directives) => { export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[]) => {
// start with config beeing the siteConfig // start with config being the siteConfig
let cfg = assignWithDepth({}, siteCfg); let cfg = assignWithDepth({}, siteCfg);
// let sCfg = assignWithDepth(defaultConfig, siteConfigDelta); // let sCfg = assignWithDepth(defaultConfig, siteConfigDelta);
@@ -27,12 +28,15 @@ export const updateCurrentConfig = (siteCfg, _directives) => {
cfg = assignWithDepth(cfg, sumOfDirectives); cfg = assignWithDepth(cfg, sumOfDirectives);
// @ts-ignore
if (sumOfDirectives.theme && theme[sumOfDirectives.theme]) { if (sumOfDirectives.theme && theme[sumOfDirectives.theme]) {
const tmpConfigFromInitialize = assignWithDepth({}, configFromInitialize); const tmpConfigFromInitialize = assignWithDepth({}, configFromInitialize);
const themeVariables = assignWithDepth( const themeVariables = assignWithDepth(
tmpConfigFromInitialize.themeVariables || {}, tmpConfigFromInitialize.themeVariables || {},
// @ts-ignore
sumOfDirectives.themeVariables sumOfDirectives.themeVariables
); );
// @ts-ignore
cfg.themeVariables = theme[cfg.theme].getThemeVariables(themeVariables); cfg.themeVariables = theme[cfg.theme].getThemeVariables(themeVariables);
} }
@@ -55,11 +59,13 @@ export const updateCurrentConfig = (siteCfg, _directives) => {
* @param conf - The base currentConfig to use as siteConfig * @param conf - The base currentConfig to use as siteConfig
* @returns {object} - The siteConfig * @returns {object} - The siteConfig
*/ */
export const setSiteConfig = (conf) => { export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => {
siteConfig = assignWithDepth({}, defaultConfig); siteConfig = assignWithDepth({}, defaultConfig);
siteConfig = assignWithDepth(siteConfig, conf); siteConfig = assignWithDepth(siteConfig, conf);
// @ts-ignore
if (conf.theme && theme[conf.theme]) { if (conf.theme && theme[conf.theme]) {
// @ts-ignore
siteConfig.themeVariables = theme[conf.theme].getThemeVariables(conf.themeVariables); siteConfig.themeVariables = theme[conf.theme].getThemeVariables(conf.themeVariables);
} }
@@ -67,11 +73,11 @@ export const setSiteConfig = (conf) => {
return siteConfig; return siteConfig;
}; };
export const saveConfigFromInitialize = (conf) => { export const saveConfigFromInitialize = (conf: MermaidConfig): void => {
configFromInitialize = assignWithDepth({}, conf); configFromInitialize = assignWithDepth({}, conf);
}; };
export const updateSiteConfig = (conf) => { export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => {
siteConfig = assignWithDepth(siteConfig, conf); siteConfig = assignWithDepth(siteConfig, conf);
updateCurrentConfig(siteConfig, directives); updateCurrentConfig(siteConfig, directives);
@@ -88,7 +94,7 @@ export const updateSiteConfig = (conf) => {
* *
* @returns {object} - The siteConfig * @returns {object} - The siteConfig
*/ */
export const getSiteConfig = () => { export const getSiteConfig = (): MermaidConfig => {
return assignWithDepth({}, siteConfig); return assignWithDepth({}, siteConfig);
}; };
/** /**
@@ -105,7 +111,7 @@ export const getSiteConfig = () => {
* @param {any} conf - The potential currentConfig * @param {any} conf - The potential currentConfig
* @returns {any} - The currentConfig merged with the sanitized conf * @returns {any} - The currentConfig merged with the sanitized conf
*/ */
export const setConfig = (conf) => { export const setConfig = (conf: MermaidConfig): MermaidConfig => {
// sanitize(conf); // sanitize(conf);
// Object.keys(conf).forEach(key => { // Object.keys(conf).forEach(key => {
// const manipulator = manipulators[key]; // const manipulator = manipulators[key];
@@ -143,17 +149,14 @@ export const getConfig = () => {
* *
* @param {any} options - The potential setConfig parameter * @param {any} options - The potential setConfig parameter
*/ */
export const sanitize = (options) => { export const sanitize = (options: any) => {
// Checking that options are not in the list of excluded options // Checking that options are not in the list of excluded options
Object.keys(siteConfig.secure).forEach((key) => { siteConfig.secure?.forEach((key) => {
if (typeof options[siteConfig.secure[key]] !== 'undefined') { if (typeof options[key] !== 'undefined') {
// DO NOT attempt to print options[siteConfig.secure[key]] within `${}` as a malicious script // DO NOT attempt to print options[key] within `${}` as a malicious script
// can exploit the logger's attempt to stringify the value and execute arbitrary code // can exploit the logger's attempt to stringify the value and execute arbitrary code
log.debug( log.debug(`Denied attempt to modify a secure key ${key}`, options[key]);
`Denied attempt to modify a secure key ${siteConfig.secure[key]}`, delete options[key];
options[siteConfig.secure[key]]
);
delete options[siteConfig.secure[key]];
} }
}); });
@@ -186,7 +189,7 @@ export const sanitize = (options) => {
* *
* @param {object} directive The directive to push in * @param {object} directive The directive to push in
*/ */
export const addDirective = (directive) => { export const addDirective = (directive: any) => {
if (directive.fontFamily) { if (directive.fontFamily) {
if (!directive.themeVariables) { if (!directive.themeVariables) {
directive.themeVariables = { fontFamily: directive.fontFamily }; directive.themeVariables = { fontFamily: directive.fontFamily };
@@ -215,7 +218,7 @@ export const addDirective = (directive) => {
* *
* **Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`) * **Notes**: (default: current siteConfig ) (optional, default `getSiteConfig()`)
*/ */
export const reset = () => { export const reset = (): void => {
// Replace current config with siteConfig // Replace current config with siteConfig
directives = []; directives = [];
updateCurrentConfig(siteConfig, directives); updateCurrentConfig(siteConfig, directives);

View File

@@ -40,10 +40,10 @@ const diagramMatchers: Record<string, RegExp> = {
* class: { defaultRenderer: string } | undefined; * class: { defaultRenderer: string } | undefined;
* state: { defaultRenderer: string } | undefined; * state: { defaultRenderer: string } | undefined;
* flowchart: { defaultRenderer: string } | undefined; * flowchart: { defaultRenderer: string } | undefined;
* }} [cnf] * }} [config]
* @returns {string} A graph definition key * @returns {string} A graph definition key
*/ */
export const detectType = function (text: string, cnf: MermaidConfig): string { export const detectType = function (text: string, config?: MermaidConfig): string {
text = text.replace(directive, '').replace(anyComment, '\n'); text = text.replace(directive, '').replace(anyComment, '\n');
for (const [diagram, matcher] of Object.entries(diagramMatchers)) { for (const [diagram, matcher] of Object.entries(diagramMatchers)) {
if (text.match(matcher)) { if (text.match(matcher)) {
@@ -52,16 +52,16 @@ export const detectType = function (text: string, cnf: MermaidConfig): string {
} }
if (text.match(/^\s*classDiagram/)) { if (text.match(/^\s*classDiagram/)) {
if (cnf?.class?.defaultRenderer === 'dagre-wrapper') return 'classDiagram'; if (config?.class?.defaultRenderer === 'dagre-wrapper') return 'classDiagram';
return 'class'; return 'class';
} }
if (text.match(/^\s*stateDiagram/)) { if (text.match(/^\s*stateDiagram/)) {
if (cnf?.state?.defaultRenderer === 'dagre-wrapper') return 'stateDiagram'; if (config?.state?.defaultRenderer === 'dagre-wrapper') return 'stateDiagram';
return 'state'; return 'state';
} }
if (cnf?.flowchart?.defaultRenderer === 'dagre-wrapper') { if (config?.flowchart?.defaultRenderer === 'dagre-wrapper') {
return 'flowchart-v2'; return 'flowchart-v2';
} }

View File

@@ -1,9 +1,8 @@
// TODO: Remove
// @ts-nocheck
/** /**
* Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid * Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid
* functionality and to render the diagrams to svg code. * functionality and to render the diagrams to svg code.
*/ */
import { MermaidConfig } from 'types/config';
import { log } from './logger'; import { log } from './logger';
import mermaidAPI from './mermaidAPI'; import mermaidAPI from './mermaidAPI';
import utils from './utils'; import utils from './utils';
@@ -31,73 +30,73 @@ import utils from './utils';
* *
* Renders the mermaid diagrams * Renders the mermaid diagrams
*/ */
const init = function (config: any, ...nodes: any[]) { const init = function (
config?: MermaidConfig,
nodes?: string | HTMLElement | NodeListOf<HTMLElement>,
callback?: Function
) {
try { try {
initThrowsErrors(config, nodes); initThrowsErrors(config, nodes, callback);
} catch (e) { } catch (e) {
log.warn('Syntax Error rendering'); log.warn('Syntax Error rendering');
// @ts-ignore
log.warn(e.str); log.warn(e.str);
if (this.parseError) { if (mermaid.parseError) {
this.parseError(e); // @ts-ignore
mermaid.parseError(e);
} }
} }
}; };
const initThrowsErrors = function (config: any, nodes: any[]) { const initThrowsErrors = function (
config?: MermaidConfig,
nodes?: string | HTMLElement | NodeListOf<HTMLElement>,
callback?: Function
) {
const conf = mermaidAPI.getConfig(); const conf = mermaidAPI.getConfig();
// console.log('Starting rendering diagrams (init) - mermaid.init', conf); // console.log('Starting rendering diagrams (init) - mermaid.init', conf);
if (config) { if (config) {
// @ts-ignore
mermaid.sequenceConfig = config; mermaid.sequenceConfig = config;
} }
// if last argument is a function this is the callback function // if last argument is a function this is the callback function
let callback: (id: string) => void;
if (typeof nodes[nodes.length - 1] === 'function') { if (!callback && typeof conf?.mermaid?.callback === 'function') {
callback = nodes[nodes.length - 1]; callback = conf.mermaid.callback;
log.debug('Callback function found'); }
log.debug(`${!callback ? 'No ' : ''}Callback function found`);
let nodesToProcess: NodeListOf<HTMLElement>;
if (typeof nodes === 'undefined') {
nodesToProcess = document.querySelectorAll('.mermaid');
} else if (typeof nodes === 'string') {
nodesToProcess = document.querySelectorAll(nodes);
} else if (nodes instanceof HTMLElement) {
nodesToProcess = new NodeList() as NodeListOf<HTMLElement>;
nodesToProcess[0] = nodes;
} else if (nodes instanceof NodeList) {
nodesToProcess = nodes;
} else { } else {
if (typeof conf.mermaid !== 'undefined') { throw new Error('Invalid argument nodes for mermaid.init');
if (typeof conf.mermaid.callback === 'function') {
callback = conf.mermaid.callback;
log.debug('Callback function found');
} else {
log.debug('No Callback function found');
}
}
}
nodes =
nodes === undefined
? document.querySelectorAll('.mermaid')
: typeof nodes === 'string'
? document.querySelectorAll(nodes)
: nodes instanceof window.Node
? [nodes]
: nodes; // Last case - sequence config was passed pick next
log.debug('Start On Load before: ' + mermaid.startOnLoad);
if (typeof mermaid.startOnLoad !== 'undefined') {
log.debug('Start On Load inner: ' + mermaid.startOnLoad);
mermaidAPI.updateSiteConfig({ startOnLoad: mermaid.startOnLoad });
} }
if (typeof mermaid.ganttConfig !== 'undefined') { log.debug(`Found ${nodesToProcess.length} diagrams`);
mermaidAPI.updateSiteConfig({ gantt: mermaid.ganttConfig }); if (typeof config?.startOnLoad !== 'undefined') {
log.debug('Start On Load: ' + config?.startOnLoad);
mermaidAPI.updateSiteConfig({ startOnLoad: config?.startOnLoad });
} }
const idGenerator = new utils.initIdGenerator(conf.deterministicIds, conf.deterministicIDSeed); const idGenerator = new utils.initIdGenerator(conf.deterministicIds, conf.deterministicIDSeed);
let txt; let txt;
for (let i = 0; i < nodes.length; i++) { // element is the current div with mermaid class
// element is the current div with mermaid class for (const element of Array.from(nodesToProcess)) {
const element = nodes[i];
/*! Check if previously processed */ /*! Check if previously processed */
if (!element.getAttribute('data-processed')) { if (element.getAttribute('data-processed')) {
element.setAttribute('data-processed', true);
} else {
continue; continue;
} }
element.setAttribute('data-processed', 'true');
const id = `mermaid-${idGenerator.next()}`; const id = `mermaid-${idGenerator.next()}`;
@@ -118,7 +117,7 @@ const initThrowsErrors = function (config: any, nodes: any[]) {
mermaidAPI.render( mermaidAPI.render(
id, id,
txt, txt,
(svgCode, bindFunctions) => { (svgCode: string, bindFunctions: (el: HTMLElement) => void) => {
element.innerHTML = svgCode; element.innerHTML = svgCode;
if (typeof callback !== 'undefined') { if (typeof callback !== 'undefined') {
callback(id); callback(id);
@@ -129,24 +128,15 @@ const initThrowsErrors = function (config: any, nodes: any[]) {
); );
} catch (error) { } catch (error) {
log.warn('Catching Error (bootstrap)'); log.warn('Catching Error (bootstrap)');
// @ts-ignore
// TODO: We should be throwing an error object.
throw { error, message: error.str }; throw { error, message: error.str };
} }
} }
}; };
const initialize = function (config: any) { const initialize = function (config: MermaidConfig) {
// mermaidAPI.reset();
if (typeof config.mermaid !== 'undefined') {
if (typeof config.mermaid.startOnLoad !== 'undefined') {
mermaid.startOnLoad = config.mermaid.startOnLoad;
}
if (typeof config.mermaid.htmlLabels !== 'undefined') {
mermaid.htmlLabels =
config.mermaid.htmlLabels === 'false' || config.mermaid.htmlLabels === false ? false : true;
}
}
mermaidAPI.initialize(config); mermaidAPI.initialize(config);
// mermaidAPI.reset();
}; };
/** /**
@@ -154,22 +144,9 @@ const initialize = function (config: any) {
* configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the page. * configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the page.
*/ */
const contentLoaded = function () { const contentLoaded = function () {
let config; const { startOnLoad } = mermaidAPI.getConfig();
if (startOnLoad) {
if (mermaid.startOnLoad) { mermaid.init();
// No config found, do check API config
config = mermaidAPI.getConfig();
if (config.startOnLoad) {
mermaid.init();
}
} else {
if (typeof mermaid.startOnLoad === 'undefined') {
log.debug('In start, no config');
config = mermaidAPI.getConfig();
if (config.startOnLoad) {
mermaid.init();
}
}
} }
}; };
@@ -200,13 +177,12 @@ if (typeof document !== 'undefined') {
* *
* @param {function (err, hash)} newParseErrorHandler New parseError() callback. * @param {function (err, hash)} newParseErrorHandler New parseError() callback.
*/ */
const setParseErrorHandler = function (newParseErrorHandler) { const setParseErrorHandler = function (newParseErrorHandler: (err: any, hash: any) => void) {
// @ts-ignore
mermaid.parseError = newParseErrorHandler; mermaid.parseError = newParseErrorHandler;
}; };
const mermaid = { const mermaid = {
startOnLoad: true,
htmlLabels: true,
diagrams: {}, diagrams: {},
mermaidAPI, mermaidAPI,
parse: mermaidAPI != undefined ? mermaidAPI.parse : null, parse: mermaidAPI != undefined ? mermaidAPI.parse : null,
@@ -215,7 +191,7 @@ const mermaid = {
init, init,
initThrowsErrors, initThrowsErrors,
initialize, initialize,
parseError: undefined,
contentLoaded, contentLoaded,
setParseErrorHandler, setParseErrorHandler,

View File

@@ -1,3 +1,4 @@
// @ts-nocheck
import { sanitizeUrl } from '@braintree/sanitize-url'; import { sanitizeUrl } from '@braintree/sanitize-url';
import { import {
curveBasis, curveBasis,
@@ -18,6 +19,7 @@ import { configKeys } from './defaultConfig';
import { log } from './logger'; import { log } from './logger';
import { detectType } from './diagram-api/detectType'; import { detectType } from './diagram-api/detectType';
import assignWithDepth from './assignWithDepth'; import assignWithDepth from './assignWithDepth';
import { MermaidConfig } from 'types/config';
// Effectively an enum of the supported curve types, accessible by name // Effectively an enum of the supported curve types, accessible by name
const d3CurveTypes = { const d3CurveTypes = {
@@ -71,7 +73,7 @@ const anyComment = /\s*%%.*\n/gm;
* @param {any} cnf * @param {any} cnf
* @returns {object} The json object representing the init passed to mermaid.initialize() * @returns {object} The json object representing the init passed to mermaid.initialize()
*/ */
export const detectInit = function (text, cnf) { export const detectInit = function (text: string, config?: MermaidConfig): MermaidConfig {
let inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/); let inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/);
let results = {}; let results = {};
@@ -84,7 +86,7 @@ export const detectInit = function (text, cnf) {
results = inits.args; results = inits.args;
} }
if (results) { if (results) {
let type = detectType(text, cnf); let type = detectType(text, config);
['config'].forEach((prop) => { ['config'].forEach((prop) => {
if (typeof results[prop] !== 'undefined') { if (typeof results[prop] !== 'undefined') {
if (type === 'flowchart-v2') { if (type === 'flowchart-v2') {