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
target: 'web',
entry: {
mermaid: './src/mermaid.ts',
mermaid: './src/mermaid',
},
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.ts', '.json', '.jison'],

View File

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

View File

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

View File

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

View File

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

View File

@@ -40,10 +40,10 @@ const diagramMatchers: Record<string, RegExp> = {
* class: { defaultRenderer: string } | undefined;
* state: { defaultRenderer: string } | undefined;
* flowchart: { defaultRenderer: string } | undefined;
* }} [cnf]
* }} [config]
* @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');
for (const [diagram, matcher] of Object.entries(diagramMatchers)) {
if (text.match(matcher)) {
@@ -52,16 +52,16 @@ export const detectType = function (text: string, cnf: MermaidConfig): string {
}
if (text.match(/^\s*classDiagram/)) {
if (cnf?.class?.defaultRenderer === 'dagre-wrapper') return 'classDiagram';
if (config?.class?.defaultRenderer === 'dagre-wrapper') return 'classDiagram';
return 'class';
}
if (text.match(/^\s*stateDiagram/)) {
if (cnf?.state?.defaultRenderer === 'dagre-wrapper') return 'stateDiagram';
if (config?.state?.defaultRenderer === 'dagre-wrapper') return 'stateDiagram';
return 'state';
}
if (cnf?.flowchart?.defaultRenderer === 'dagre-wrapper') {
if (config?.flowchart?.defaultRenderer === 'dagre-wrapper') {
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
* functionality and to render the diagrams to svg code.
*/
import { MermaidConfig } from 'types/config';
import { log } from './logger';
import mermaidAPI from './mermaidAPI';
import utils from './utils';
@@ -31,73 +30,73 @@ import utils from './utils';
*
* Renders the mermaid diagrams
*/
const init = function (config: any, ...nodes: any[]) {
const init = function (
config?: MermaidConfig,
nodes?: string | HTMLElement | NodeListOf<HTMLElement>,
callback?: Function
) {
try {
initThrowsErrors(config, nodes);
initThrowsErrors(config, nodes, callback);
} catch (e) {
log.warn('Syntax Error rendering');
// @ts-ignore
log.warn(e.str);
if (this.parseError) {
this.parseError(e);
if (mermaid.parseError) {
// @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();
// console.log('Starting rendering diagrams (init) - mermaid.init', conf);
if (config) {
// @ts-ignore
mermaid.sequenceConfig = config;
}
// if last argument is a function this is the callback function
let callback: (id: string) => void;
if (typeof nodes[nodes.length - 1] === 'function') {
callback = nodes[nodes.length - 1];
log.debug('Callback function found');
if (!callback && typeof conf?.mermaid?.callback === 'function') {
callback = conf.mermaid.callback;
}
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 {
if (typeof conf.mermaid !== 'undefined') {
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 });
throw new Error('Invalid argument nodes for mermaid.init');
}
if (typeof mermaid.ganttConfig !== 'undefined') {
mermaidAPI.updateSiteConfig({ gantt: mermaid.ganttConfig });
log.debug(`Found ${nodesToProcess.length} diagrams`);
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);
let txt;
for (let i = 0; i < nodes.length; i++) {
// element is the current div with mermaid class
const element = nodes[i];
// element is the current div with mermaid class
for (const element of Array.from(nodesToProcess)) {
/*! Check if previously processed */
if (!element.getAttribute('data-processed')) {
element.setAttribute('data-processed', true);
} else {
if (element.getAttribute('data-processed')) {
continue;
}
element.setAttribute('data-processed', 'true');
const id = `mermaid-${idGenerator.next()}`;
@@ -118,7 +117,7 @@ const initThrowsErrors = function (config: any, nodes: any[]) {
mermaidAPI.render(
id,
txt,
(svgCode, bindFunctions) => {
(svgCode: string, bindFunctions: (el: HTMLElement) => void) => {
element.innerHTML = svgCode;
if (typeof callback !== 'undefined') {
callback(id);
@@ -129,24 +128,15 @@ const initThrowsErrors = function (config: any, nodes: any[]) {
);
} catch (error) {
log.warn('Catching Error (bootstrap)');
// @ts-ignore
// TODO: We should be throwing an error object.
throw { error, message: error.str };
}
}
};
const initialize = function (config: any) {
// 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;
}
}
const initialize = function (config: MermaidConfig) {
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.
*/
const contentLoaded = function () {
let config;
if (mermaid.startOnLoad) {
// 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();
}
}
const { startOnLoad } = mermaidAPI.getConfig();
if (startOnLoad) {
mermaid.init();
}
};
@@ -200,13 +177,12 @@ if (typeof document !== 'undefined') {
*
* @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;
};
const mermaid = {
startOnLoad: true,
htmlLabels: true,
diagrams: {},
mermaidAPI,
parse: mermaidAPI != undefined ? mermaidAPI.parse : null,
@@ -215,7 +191,7 @@ const mermaid = {
init,
initThrowsErrors,
initialize,
parseError: undefined,
contentLoaded,
setParseErrorHandler,

View File

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