From fae976e994a30336878444e148ed1ac19ca331f6 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 10:13:48 +0530 Subject: [PATCH 01/12] refactor: Rename and cleanup `directiveSanitizer` --- packages/mermaid/src/defaultConfig.ts | 2 +- packages/mermaid/src/utils.ts | 97 +++++++++++++-------------- 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 0b232116da..f8bd9b0b53 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -265,5 +265,5 @@ const keyify = (obj: any, prefix = ''): string[] => return [...res, prefix + el]; }, []); -export const configKeys: string[] = keyify(config, ''); +export const configKeys: Set = new Set(keyify(config, '')); export default config; diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index 937f3f8f84..f054ef29ff 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -102,7 +102,7 @@ export const detectInit = function (text: string, config?: MermaidConfig): Merma if (Array.isArray(inits)) { const args = inits.map((init) => init.args); - directiveSanitizer(args); + sanitizeDirective(args); results = assignWithDepth(results, [...args]); } else { @@ -842,67 +842,62 @@ export const entityDecode = function (html: string): string { * * @param args - Directive's JSON */ -export const directiveSanitizer = (args: any) => { - log.debug('directiveSanitizer called with', args); - if (typeof args === 'object') { - // check for array - if (args.length) { - args.forEach((arg) => directiveSanitizer(arg)); - } else { - // This is an object - Object.keys(args).forEach((key) => { - log.debug('Checking key', key); - if (key.startsWith('__')) { - log.debug('sanitize deleting __ option', key); - delete args[key]; - } +export const sanitizeDirective = (args: unknown): void => { + log.debug('sanitizeDirective called with', args); - if (key.includes('proto')) { - log.debug('sanitize deleting proto option', key); - delete args[key]; - } + // Return if not an object + if (typeof args !== 'object') { + return; + } - if (key.includes('constr')) { - log.debug('sanitize deleting constr option', key); - delete args[key]; - } + // Sanitize each element if an array + if (Array.isArray(args)) { + args.forEach((arg) => sanitizeDirective(arg)); + return; + } - if (key.includes('themeCSS')) { - log.debug('sanitizing themeCss option'); - args[key] = sanitizeCss(args[key]); - } - if (key.includes('fontFamily')) { - log.debug('sanitizing fontFamily option'); - args[key] = sanitizeCss(args[key]); - } - if (key.includes('altFontFamily')) { - log.debug('sanitizing altFontFamily option'); - args[key] = sanitizeCss(args[key]); - } - if (!configKeys.includes(key)) { - log.debug('sanitize deleting option', key); - delete args[key]; - } else { - if (typeof args[key] === 'object') { - log.debug('sanitize deleting object', key); - directiveSanitizer(args[key]); - } - } - }); + // Sanitize each key if an object + for (const key of Object.keys(args)) { + log.debug('Checking key', key); + if ( + key.startsWith('__') || + key.includes('proto') || + key.includes('constr') || + !configKeys.has(key) + ) { + log.debug('sanitize deleting key: ', key); + delete args[key]; + continue; + } + + // Recurse if an object + if (typeof args[key] === 'object') { + log.debug('sanitizing object', key); + sanitizeDirective(args[key]); + continue; + } + + const cssMatchers = ['themeCSS', 'fontFamily', 'altFontFamily']; + for (const cssKey of cssMatchers) { + if (key.includes(cssKey)) { + log.debug('sanitizing css option', key); + args[key] = sanitizeCss(args[key]); + } } } + if (args.themeVariables) { - const kArr = Object.keys(args.themeVariables); - for (const k of kArr) { + for (const k of Object.keys(args.themeVariables)) { const val = args.themeVariables[k]; - if (val && val.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) { + if (val?.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) { args.themeVariables[k] = ''; } } } log.debug('After sanitization', args); }; -export const sanitizeCss = (str) => { + +export const sanitizeCss = (str: string): string => { let startCnt = 0; let endCnt = 0; @@ -1019,8 +1014,8 @@ export default { random, runFunc, entityDecode, - initIdGenerator: initIdGenerator, - directiveSanitizer, + initIdGenerator, + sanitizeDirective, sanitizeCss, insertTitle, parseFontSize, From f422a66dde10ccbfd4716c4e62801d17a9679ea2 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 10:17:55 +0530 Subject: [PATCH 02/12] refactor: Move `sanitizeDirective` into `addDirective` --- packages/mermaid/src/config.spec.ts | 27 ++++++++++++++++--------- packages/mermaid/src/config.ts | 28 +++++++++----------------- packages/mermaid/src/directiveUtils.ts | 5 ----- packages/mermaid/src/mermaidAPI.ts | 3 +-- packages/mermaid/src/utils.ts | 2 -- 5 files changed, 29 insertions(+), 36 deletions(-) diff --git a/packages/mermaid/src/config.spec.ts b/packages/mermaid/src/config.spec.ts index 457cb82443..8dd4ff33c0 100644 --- a/packages/mermaid/src/config.spec.ts +++ b/packages/mermaid/src/config.spec.ts @@ -1,11 +1,13 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import * as configApi from './config.js'; +import type { MermaidConfig } from './config.type.js'; -describe('when working with site config', function () { +describe('when working with site config', () => { beforeEach(() => { // Resets the site config to default config configApi.setSiteConfig({}); }); - it('should set site config and config properly', function () { + it('should set site config and config properly', () => { const config_0 = { fontFamily: 'foo-font', fontSize: 150 }; configApi.setSiteConfig(config_0); const config_1 = configApi.getSiteConfig(); @@ -14,19 +16,26 @@ describe('when working with site config', function () { expect(config_1.fontSize).toEqual(config_0.fontSize); expect(config_1).toEqual(config_2); }); - it('should respect secure keys when applying directives', function () { - const config_0 = { + it('should respect secure keys when applying directives', () => { + const config_0: MermaidConfig = { fontFamily: 'foo-font', + securityLevel: 'strict', // can't be changed fontSize: 12345, // can't be changed secure: [...configApi.defaultConfig.secure!, 'fontSize'], }; configApi.setSiteConfig(config_0); - const directive = { fontFamily: 'baf', fontSize: 54321 /* fontSize shouldn't be changed */ }; - const cfg = configApi.updateCurrentConfig(config_0, [directive]); + const directive: MermaidConfig = { + fontFamily: 'baf', + // fontSize and securityLevel shouldn't be changed + fontSize: 54321, + securityLevel: 'loose', + }; + const cfg: MermaidConfig = configApi.updateCurrentConfig(config_0, [directive]); expect(cfg.fontFamily).toEqual(directive.fontFamily); expect(cfg.fontSize).toBe(config_0.fontSize); + expect(cfg.securityLevel).toBe(config_0.securityLevel); }); - it('should allow setting partial options', function () { + it('should allow setting partial options', () => { const defaultConfig = configApi.getConfig(); configApi.setConfig({ @@ -42,7 +51,7 @@ describe('when working with site config', function () { updatedConfig.quadrantChart!.chartWidth ); }); - it('should set reset config properly', function () { + it('should set reset config properly', () => { const config_0 = { fontFamily: 'foo-font', fontSize: 150 }; configApi.setSiteConfig(config_0); const config_1 = { fontFamily: 'baf' }; @@ -55,7 +64,7 @@ describe('when working with site config', function () { const config_4 = configApi.getSiteConfig(); expect(config_4.fontFamily).toEqual(config_0.fontFamily); }); - it('should set global reset config properly', function () { + it('should set global reset config properly', () => { const config_0 = { fontFamily: 'foo-font', fontSize: 150 }; configApi.setSiteConfig(config_0); const config_1 = configApi.getSiteConfig(); diff --git a/packages/mermaid/src/config.ts b/packages/mermaid/src/config.ts index e006638275..c426e9122a 100644 --- a/packages/mermaid/src/config.ts +++ b/packages/mermaid/src/config.ts @@ -3,15 +3,16 @@ import { log } from './logger.js'; import theme from './themes/index.js'; import config from './defaultConfig.js'; import type { MermaidConfig } from './config.type.js'; +import { sanitizeDirective } from './utils.js'; export const defaultConfig: MermaidConfig = Object.freeze(config); let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig); let configFromInitialize: MermaidConfig; -let directives: any[] = []; +let directives: MermaidConfig[] = []; let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig); -export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[]) => { +export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => { // start with config being the siteConfig let cfg: MermaidConfig = assignWithDepth({}, siteCfg); // let sCfg = assignWithDepth(defaultConfig, siteConfigDelta); @@ -20,7 +21,6 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[]) let sumOfDirectives: MermaidConfig = {}; for (const d of _directives) { sanitize(d); - // Apply the data from the directive where the the overrides the themeVariables sumOfDirectives = assignWithDepth(sumOfDirectives, d); } @@ -111,12 +111,6 @@ export const getSiteConfig = (): MermaidConfig => { * @returns The currentConfig merged with the sanitized conf */ export const setConfig = (conf: MermaidConfig): MermaidConfig => { - // sanitize(conf); - // Object.keys(conf).forEach(key => { - // const manipulator = manipulators[key]; - // conf[key] = manipulator ? manipulator(conf[key]) : conf[key]; - // }); - checkConfig(conf); assignWithDepth(currentConfig, conf); @@ -188,16 +182,14 @@ export const sanitize = (options: any) => { * * @param directive - The directive to push in */ -export const addDirective = (directive: any) => { - if (directive.fontFamily) { - if (!directive.themeVariables) { - directive.themeVariables = { fontFamily: directive.fontFamily }; - } else { - if (!directive.themeVariables.fontFamily) { - directive.themeVariables = { fontFamily: directive.fontFamily }; - } - } +export const addDirective = (directive: MermaidConfig) => { + sanitizeDirective(directive); + + // If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables + if (directive.fontFamily && (!directive.themeVariables || !directive.themeVariables.fontFamily)) { + directive.themeVariables = { fontFamily: directive.fontFamily }; } + directives.push(directive); updateCurrentConfig(siteConfig, directives); }; diff --git a/packages/mermaid/src/directiveUtils.ts b/packages/mermaid/src/directiveUtils.ts index 563856631a..baf628e74c 100644 --- a/packages/mermaid/src/directiveUtils.ts +++ b/packages/mermaid/src/directiveUtils.ts @@ -1,7 +1,5 @@ import * as configApi from './config.js'; - import { log } from './logger.js'; -import { directiveSanitizer } from './utils.js'; let currentDirective: { type?: string; args?: any } | undefined = {}; @@ -60,9 +58,6 @@ const handleDirective = function (p: any, directive: any, type: string): void { delete directive.args[prop]; } }); - log.info('sanitize in handleDirective', directive.args); - directiveSanitizer(directive.args); - log.info('sanitize in handleDirective (done)', directive.args); configApi.addDirective(directive.args); break; } diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index f8a36f88e6..35b251bc7f 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -23,7 +23,7 @@ import { attachFunctions } from './interactionDb.js'; import { log, setLogLevel } from './logger.js'; import getStyles from './styles.js'; import theme from './themes/index.js'; -import utils, { directiveSanitizer } from './utils.js'; +import utils from './utils.js'; import DOMPurify from 'dompurify'; import { MermaidConfig } from './config.type.js'; import { evaluate } from './diagrams/common/common.js'; @@ -388,7 +388,6 @@ const render = async function ( // Add Directives. Must do this before getting the config and before creating the diagram. const graphInit = utils.detectInit(text); if (graphInit) { - directiveSanitizer(graphInit); configApi.addDirective(graphInit); } diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index f054ef29ff..dd574f86a8 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -102,8 +102,6 @@ export const detectInit = function (text: string, config?: MermaidConfig): Merma if (Array.isArray(inits)) { const args = inits.map((init) => init.args); - sanitizeDirective(args); - results = assignWithDepth(results, [...args]); } else { results = inits.args; From 767baa4ec6bd5d478df500b1c77b0e68a1e21794 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 10:18:23 +0530 Subject: [PATCH 03/12] chore: Fix type in assignWithDepth --- packages/mermaid/src/assignWithDepth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/assignWithDepth.ts b/packages/mermaid/src/assignWithDepth.ts index 831825779d..125b6f434a 100644 --- a/packages/mermaid/src/assignWithDepth.ts +++ b/packages/mermaid/src/assignWithDepth.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /** - * assignWithDepth Extends the functionality of {@link ObjectConstructor.assign} with the + * assignWithDepth Extends the functionality of {@link Object.assign} with the * ability to merge arbitrary-depth objects For each key in src with path `k` (recursively) * performs an Object.assign(dst[`k`], src[`k`]) with a slight change from the typical handling of * undefined for dst[`k`]: instead of raising an error, dst[`k`] is auto-initialized to `{}` and From a6e6c3fb1848bf37a001022324d7035a9f4e885d Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 12:09:38 +0530 Subject: [PATCH 04/12] feat: Add support for config in frontmatter --- .../rendering/conf-and-directives.spec.js | 70 ++++++++++++++++--- packages/mermaid/src/Diagram.ts | 2 +- packages/mermaid/src/config.ts | 7 +- .../src/diagram-api/frontmatter.spec.ts | 22 ++++++ .../mermaid/src/diagram-api/frontmatter.ts | 53 ++++++++------ packages/mermaid/src/mermaidAPI.ts | 8 ++- packages/mermaid/src/utils.ts | 36 ++++++---- 7 files changed, 150 insertions(+), 48 deletions(-) diff --git a/cypress/integration/rendering/conf-and-directives.spec.js b/cypress/integration/rendering/conf-and-directives.spec.js index bc17f62361..ca1fccf495 100644 --- a/cypress/integration/rendering/conf-and-directives.spec.js +++ b/cypress/integration/rendering/conf-and-directives.spec.js @@ -14,7 +14,6 @@ describe('Configuration and directives - nodes should be light blue', () => { `, {} ); - cy.get('svg'); }); it('Settings from initialize - nodes should be green', () => { imgSnapshotTest( @@ -28,7 +27,6 @@ graph TD end `, { theme: 'forest' } ); - cy.get('svg'); }); it('Settings from initialize overriding themeVariable - nodes should be red', () => { imgSnapshotTest( @@ -46,7 +44,6 @@ graph TD `, { theme: 'base', themeVariables: { primaryColor: '#ff0000' }, logLevel: 0 } ); - cy.get('svg'); }); it('Settings from directive - nodes should be grey', () => { imgSnapshotTest( @@ -62,7 +59,24 @@ graph TD `, {} ); - cy.get('svg'); + }); + it('Settings from frontmatter - nodes should be grey', () => { + imgSnapshotTest( + ` +--- +config: + theme: neutral +--- +graph TD + A(Start) --> B[/Another/] + A[/Another/] --> C[End] + subgraph section + B + C + end + `, + {} + ); }); it('Settings from directive overriding theme variable - nodes should be red', () => { @@ -79,7 +93,6 @@ graph TD `, {} ); - cy.get('svg'); }); it('Settings from initialize and directive - nodes should be grey', () => { imgSnapshotTest( @@ -95,7 +108,6 @@ graph TD `, { theme: 'forest' } ); - cy.get('svg'); }); it('Theme from initialize, directive overriding theme variable - nodes should be red', () => { imgSnapshotTest( @@ -111,8 +123,50 @@ graph TD `, { theme: 'base' } ); - cy.get('svg'); }); + it('Theme from initialize, frontmatter overriding theme variable - nodes should be red', () => { + imgSnapshotTest( + ` +--- +config: + theme: base + themeVariables: + primaryColor: '#ff0000' +--- +graph TD + A(Start) --> B[/Another/] + A[/Another/] --> C[End] + subgraph section + B + C + end + `, + { theme: 'forest' } + ); + }); + + it('should render if values are not quoted properly', () => { + // #ff0000 is not quoted properly, and will evaluate to null. + // This test ensures that the rendering still works. + imgSnapshotTest( + `--- +config: + theme: base + themeVariables: + primaryColor: #ff0000 +--- +graph TD + A(Start) --> B[/Another/] + A[/Another/] --> C[End] + subgraph section + B + C + end + `, + { theme: 'forest' } + ); + }); + it('Theme variable from initialize, theme from directive - nodes should be red', () => { imgSnapshotTest( ` @@ -127,13 +181,11 @@ graph TD `, { themeVariables: { primaryColor: '#ff0000' } } ); - cy.get('svg'); }); describe('when rendering several diagrams', () => { it('diagrams should not taint later diagrams', () => { const url = 'http://localhost:9000/theme-directives.html'; cy.visit(url); - cy.get('svg'); cy.matchImageSnapshot('conf-and-directives.spec-when-rendering-several-diagrams-diagram-1'); }); }); diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts index 13fd3232b5..308e141d03 100644 --- a/packages/mermaid/src/Diagram.ts +++ b/packages/mermaid/src/Diagram.ts @@ -48,7 +48,7 @@ export class Diagram { // extractFrontMatter(). this.parser.parse = (text: string) => - originalParse(cleanupComments(extractFrontMatter(text, this.db))); + originalParse(cleanupComments(extractFrontMatter(text, this.db, configApi.addDirective))); this.parser.parser.yy = this.db; this.init = diagram.init; diff --git a/packages/mermaid/src/config.ts b/packages/mermaid/src/config.ts index c426e9122a..eb24b6268f 100644 --- a/packages/mermaid/src/config.ts +++ b/packages/mermaid/src/config.ts @@ -144,9 +144,12 @@ export const getConfig = (): MermaidConfig => { * @param options - The potential setConfig parameter */ export const sanitize = (options: any) => { + if (!options) { + return; + } // Checking that options are not in the list of excluded options ['secure', ...(siteConfig.secure ?? [])].forEach((key) => { - if (options[key] !== undefined) { + if (Object.hasOwn(options, key)) { // 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 ${key}`, options[key]); @@ -156,7 +159,7 @@ export const sanitize = (options: any) => { // Check that there no attempts of prototype pollution Object.keys(options).forEach((key) => { - if (key.indexOf('__') === 0) { + if (key.startsWith('__')) { delete options[key]; } }); diff --git a/packages/mermaid/src/diagram-api/frontmatter.spec.ts b/packages/mermaid/src/diagram-api/frontmatter.spec.ts index ef05c8f149..03d46c300c 100644 --- a/packages/mermaid/src/diagram-api/frontmatter.spec.ts +++ b/packages/mermaid/src/diagram-api/frontmatter.spec.ts @@ -2,8 +2,13 @@ import { vi } from 'vitest'; import { extractFrontMatter } from './frontmatter.js'; const dbMock = () => ({ setDiagramTitle: vi.fn() }); +const setConfigMock = vi.fn(); describe('extractFrontmatter', () => { + beforeEach(() => { + setConfigMock.mockClear(); + }); + it('returns text unchanged if no frontmatter', () => { expect(extractFrontMatter('diagram', dbMock())).toEqual('diagram'); }); @@ -75,4 +80,21 @@ describe('extractFrontmatter', () => { 'tag suffix cannot contain exclamation marks' ); }); + + it('handles frontmatter with config', () => { + const text = `--- +title: hello +config: + graph: + string: hello + number: 14 + boolean: false + array: [1, 2, 3] +--- +diagram`; + expect(extractFrontMatter(text, {}, setConfigMock)).toEqual('diagram'); + expect(setConfigMock).toHaveBeenCalledWith({ + graph: { string: 'hello', number: 14, boolean: false, array: [1, 2, 3] }, + }); + }); }); diff --git a/packages/mermaid/src/diagram-api/frontmatter.ts b/packages/mermaid/src/diagram-api/frontmatter.ts index f8d2e9c415..1ce673d4fd 100644 --- a/packages/mermaid/src/diagram-api/frontmatter.ts +++ b/packages/mermaid/src/diagram-api/frontmatter.ts @@ -1,3 +1,4 @@ +import { MermaidConfig } from '../config.type.js'; import { DiagramDB } from './types.js'; // The "* as yaml" part is necessary for tree-shaking import * as yaml from 'js-yaml'; @@ -9,38 +10,50 @@ import * as yaml from 'js-yaml'; // Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s; -type FrontMatterMetadata = { +interface FrontMatterMetadata { title?: string; // Allows custom display modes. Currently used for compact mode in gantt charts. displayMode?: string; -}; + config?: MermaidConfig; +} /** * Extract and parse frontmatter from text, if present, and sets appropriate * properties in the provided db. * @param text - The text that may have a YAML frontmatter. * @param db - Diagram database, could be of any diagram. + * @param setDiagramConfig - Optional function to set diagram config. * @returns text with frontmatter stripped out */ -export function extractFrontMatter(text: string, db: DiagramDB): string { +export function extractFrontMatter( + text: string, + db: DiagramDB, + setDiagramConfig?: (config: MermaidConfig) => void +): string { const matches = text.match(frontMatterRegex); - if (matches) { - const parsed: FrontMatterMetadata = yaml.load(matches[1], { - // To keep things simple, only allow strings, arrays, and plain objects. - // https://www.yaml.org/spec/1.2/spec.html#id2802346 - schema: yaml.FAILSAFE_SCHEMA, - }) as FrontMatterMetadata; - - if (parsed?.title) { - db.setDiagramTitle?.(parsed.title); - } - - if (parsed?.displayMode) { - db.setDisplayMode?.(parsed.displayMode); - } - - return text.slice(matches[0].length); - } else { + if (!matches) { return text; } + + const parsed: FrontMatterMetadata = yaml.load(matches[1], { + // To support config, we need JSON schema. + // https://www.yaml.org/spec/1.2/spec.html#id2803231 + schema: yaml.JSON_SCHEMA, + }) as FrontMatterMetadata; + + if (parsed?.title) { + // toString() is necessary because YAML could parse the title as a number/boolean + db.setDiagramTitle?.(parsed.title.toString()); + } + + if (parsed?.displayMode) { + // toString() is necessary because YAML could parse the title as a number/boolean + db.setDisplayMode?.(parsed.displayMode.toString()); + } + + if (parsed?.config) { + setDiagramConfig?.(parsed.config); + } + + return text.slice(matches[0].length); } diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 35b251bc7f..0a5fae5758 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -30,6 +30,7 @@ import { evaluate } from './diagrams/common/common.js'; import isEmpty from 'lodash-es/isEmpty.js'; import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.js'; import { parseDirective } from './directiveUtils.js'; +import { extractFrontMatter } from './diagram-api/frontmatter.js'; // diagram names that support classDef statements const CLASSDEF_DIAGRAMS = [ @@ -385,7 +386,12 @@ const render = async function ( configApi.reset(); - // Add Directives. Must do this before getting the config and before creating the diagram. + // We need to add the directives before creating the diagram. + // So extractFrontMatter is called twice. Once here and once in the diagram parser. + // This can be fixed in a future refactor. + extractFrontMatter(text, {}, configApi.addDirective); + + // Add Directives. const graphInit = utils.detectInit(text); if (graphInit) { configApi.addDirective(graphInit); diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index dd574f86a8..cae3d5bfe8 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -96,7 +96,10 @@ const directiveWithoutOpen = * @param config - Optional mermaid configuration object. * @returns The json object representing the init passed to mermaid.initialize() */ -export const detectInit = function (text: string, config?: MermaidConfig): MermaidConfig { +export const detectInit = function ( + text: string, + config?: MermaidConfig +): MermaidConfig | undefined { const inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/); let results = {}; @@ -106,20 +109,22 @@ export const detectInit = function (text: string, config?: MermaidConfig): Merma } else { results = inits.args; } - if (results) { - let type = detectType(text, config); - ['config'].forEach((prop) => { - if (results[prop] !== undefined) { - if (type === 'flowchart-v2') { - type = 'flowchart'; - } - results[type] = results[prop]; - delete results[prop]; - } - }); + + if (!results) { + return; } - // Todo: refactor this, these results are never used + let type = detectType(text, config); + ['config'].forEach((prop) => { + if (results[prop] !== undefined) { + if (type === 'flowchart-v2') { + type = 'flowchart'; + } + results[type] = results[prop]; + delete results[prop]; + } + }); + return results; }; @@ -844,7 +849,7 @@ export const sanitizeDirective = (args: unknown): void => { log.debug('sanitizeDirective called with', args); // Return if not an object - if (typeof args !== 'object') { + if (typeof args !== 'object' || args == null) { return; } @@ -861,7 +866,8 @@ export const sanitizeDirective = (args: unknown): void => { key.startsWith('__') || key.includes('proto') || key.includes('constr') || - !configKeys.has(key) + !configKeys.has(key) || + args[key] == null ) { log.debug('sanitize deleting key: ', key); delete args[key]; From fd35a5473568be0aad9fb1db6510a4b249daf0fc Mon Sep 17 00:00:00 2001 From: sidharthv96 Date: Mon, 21 Aug 2023 06:43:15 +0000 Subject: [PATCH 05/12] Update docs --- .../interfaces/mermaidAPI.ParseOptions.md | 2 +- .../interfaces/mermaidAPI.RenderResult.md | 4 +-- docs/config/setup/modules/config.md | 26 +++++++++---------- docs/config/setup/modules/defaultConfig.md | 2 +- docs/config/setup/modules/mermaidAPI.md | 22 ++++++++-------- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md b/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md index 8ab2598855..2082a081ea 100644 --- a/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md +++ b/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md @@ -16,4 +16,4 @@ #### Defined in -[mermaidAPI.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L77) +[mermaidAPI.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L78) diff --git a/docs/config/setup/interfaces/mermaidAPI.RenderResult.md b/docs/config/setup/interfaces/mermaidAPI.RenderResult.md index 527b46d092..f84a51b87b 100644 --- a/docs/config/setup/interfaces/mermaidAPI.RenderResult.md +++ b/docs/config/setup/interfaces/mermaidAPI.RenderResult.md @@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present. #### Defined in -[mermaidAPI.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L97) +[mermaidAPI.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98) --- @@ -51,4 +51,4 @@ The svg code for the rendered graph. #### Defined in -[mermaidAPI.ts:87](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L87) +[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88) diff --git a/docs/config/setup/modules/config.md b/docs/config/setup/modules/config.md index 8381dc8c73..f1de64e2df 100644 --- a/docs/config/setup/modules/config.md +++ b/docs/config/setup/modules/config.md @@ -14,7 +14,7 @@ #### Defined in -[config.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L7) +[config.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L8) ## Functions @@ -26,9 +26,9 @@ Pushes in a directive to the configuration #### Parameters -| Name | Type | Description | -| :---------- | :---- | :----------------------- | -| `directive` | `any` | The directive to push in | +| Name | Type | Description | +| :---------- | :-------------- | :----------------------- | +| `directive` | `MermaidConfig` | The directive to push in | #### Returns @@ -36,7 +36,7 @@ Pushes in a directive to the configuration #### Defined in -[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191) +[config.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L188) --- @@ -60,7 +60,7 @@ The currentConfig #### Defined in -[config.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L137) +[config.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L131) --- @@ -118,7 +118,7 @@ The siteConfig #### Defined in -[config.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L223) +[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218) --- @@ -147,7 +147,7 @@ options in-place #### Defined in -[config.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L152) +[config.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L146) --- @@ -242,10 +242,10 @@ The new siteConfig #### Parameters -| Name | Type | -| :------------ | :-------------- | -| `siteCfg` | `MermaidConfig` | -| `_directives` | `any`\[] | +| Name | Type | +| :------------ | :----------------- | +| `siteCfg` | `MermaidConfig` | +| `_directives` | `MermaidConfig`\[] | #### Returns @@ -253,7 +253,7 @@ The new siteConfig #### Defined in -[config.ts:14](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L14) +[config.ts:15](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L15) --- diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index 2d23977fed..effaec7b13 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -10,7 +10,7 @@ ### configKeys -• `Const` **configKeys**: `string`\[] +• `Const` **configKeys**: `Set`<`string`> #### Defined in diff --git a/docs/config/setup/modules/mermaidAPI.md b/docs/config/setup/modules/mermaidAPI.md index 1160a5dda5..d5d4a1cbc1 100644 --- a/docs/config/setup/modules/mermaidAPI.md +++ b/docs/config/setup/modules/mermaidAPI.md @@ -25,7 +25,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi) #### Defined in -[mermaidAPI.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L81) +[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82) ## Variables @@ -96,7 +96,7 @@ mermaid.initialize(config); #### Defined in -[mermaidAPI.ts:668](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L668) +[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673) ## Functions @@ -127,7 +127,7 @@ Return the last node appended #### Defined in -[mermaidAPI.ts:309](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L309) +[mermaidAPI.ts:310](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L310) --- @@ -153,7 +153,7 @@ the cleaned up svgCode #### Defined in -[mermaidAPI.ts:255](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L255) +[mermaidAPI.ts:256](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L256) --- @@ -179,7 +179,7 @@ the string with all the user styles #### Defined in -[mermaidAPI.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L184) +[mermaidAPI.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L185) --- @@ -202,7 +202,7 @@ the string with all the user styles #### Defined in -[mermaidAPI.ts:232](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L232) +[mermaidAPI.ts:233](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L233) --- @@ -229,7 +229,7 @@ with an enclosing block that has each of the cssClasses followed by !important; #### Defined in -[mermaidAPI.ts:168](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L168) +[mermaidAPI.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L169) --- @@ -249,7 +249,7 @@ with an enclosing block that has each of the cssClasses followed by !important; #### Defined in -[mermaidAPI.ts:154](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L154) +[mermaidAPI.ts:155](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L155) --- @@ -269,7 +269,7 @@ with an enclosing block that has each of the cssClasses followed by !important; #### Defined in -[mermaidAPI.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L125) +[mermaidAPI.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L126) --- @@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code #### Defined in -[mermaidAPI.ts:286](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L286) +[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287) --- @@ -320,4 +320,4 @@ Remove any existing elements from the given document #### Defined in -[mermaidAPI.ts:359](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L359) +[mermaidAPI.ts:360](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L360) From 844a175039f5ba563cad9eac2d16cd6e247c2235 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 12:30:04 +0530 Subject: [PATCH 06/12] chore: Add test with both frontmatter and directive --- .../rendering/conf-and-directives.spec.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cypress/integration/rendering/conf-and-directives.spec.js b/cypress/integration/rendering/conf-and-directives.spec.js index ca1fccf495..401a24894b 100644 --- a/cypress/integration/rendering/conf-and-directives.spec.js +++ b/cypress/integration/rendering/conf-and-directives.spec.js @@ -133,6 +133,27 @@ config: themeVariables: primaryColor: '#ff0000' --- +graph TD + A(Start) --> B[/Another/] + A[/Another/] --> C[End] + subgraph section + B + C + end + `, + { theme: 'forest' } + ); + }); + it('Theme from initialize, frontmatter overriding theme variable, directive overriding primaryColor - nodes should be red', () => { + imgSnapshotTest( + ` +--- +config: + theme: base + themeVariables: + primaryColor: '#00ff00' +--- +%%{init: {'theme': 'base', 'themeVariables':{ 'primaryColor': '#ff0000'}}}%% graph TD A(Start) --> B[/Another/] A[/Another/] --> C[End] From 534fd85339ffd283b0f6b89725ff00185df95c5f Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 12:53:29 +0530 Subject: [PATCH 07/12] chore: Minor typo fixes --- packages/mermaid/src/schemas/config.schema.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 6e5f48d95e..29e7455629 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -13,10 +13,10 @@ # Non-JSON values, like functions or `undefined`, still need to be manually # set in `src/defaultConfig.ts`) # - `src/docs.mts` -# Used to genereate Markdown documentation for this JSON Schema by using +# Used to generate Markdown documentation for this JSON Schema by using # the `@adobe/jsonschema2md` NPM package. -# Useful things to know when editting this file +# Useful things to know when editing this file # - Use the `|` character for multi-line strings # - Use `meta:enum` to document enum values (from jsonschema2md) # - Use `tsType` to override the TypeScript type (from json-schema-to-typescript) @@ -1851,7 +1851,7 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file) The color of the links in the sankey diagram. anyOf: - $ref: '#/$defs/SankeyLinkColor' - - description: An arbtirary [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) + - description: An arbitrary [CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) type: string default: gradient nodeAlignment: From 2967b3c1bb5608b04bfcbe72f942094fe3877116 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 14:25:25 +0530 Subject: [PATCH 08/12] fix: XSS vulnerability --- packages/mermaid/src/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts index cae3d5bfe8..4f9cedebd7 100644 --- a/packages/mermaid/src/utils.ts +++ b/packages/mermaid/src/utils.ts @@ -105,6 +105,7 @@ export const detectInit = function ( if (Array.isArray(inits)) { const args = inits.map((init) => init.args); + sanitizeDirective(args); results = assignWithDepth(results, [...args]); } else { results = inits.args; From 8ac7dc81e08deb8b2659fa8af6275d0646a5f15f Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 15:02:05 +0530 Subject: [PATCH 09/12] docs: Add frontmatter config docs --- cSpell.json | 1 + docs/config/configuration.md | 33 ++++++++++++++++++- docs/config/directives.md | 3 ++ docs/config/setup/modules/config.md | 2 +- .../mermaid/src/docs/config/configuration.md | 20 ++++++++++- .../mermaid/src/docs/config/directives.md | 4 +++ 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/cSpell.json b/cSpell.json index af7a9ca46f..52012a3375 100644 --- a/cSpell.json +++ b/cSpell.json @@ -44,6 +44,7 @@ "faber", "flatmap", "foswiki", + "frontmatter", "ftplugin", "gantt", "gitea", diff --git a/docs/config/configuration.md b/docs/config/configuration.md index c7b780143d..1e85427ea7 100644 --- a/docs/config/configuration.md +++ b/docs/config/configuration.md @@ -10,10 +10,41 @@ When mermaid starts, configuration is extracted to determine a configuration to - The default configuration - Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**. -- Directives - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config. +- Frontmatter (v\+) - diagram authors can update select configuration parameters in the frontmatter of the diagram. These are applied to the render config. +- Directives (Deprecated by Frontmatter) - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config. **The render config** is configuration that is used when rendering by applying these configurations. +## Frontmatter config + +The entire mermaid configuration (except the secure configs) can be overridden by the diagram author in the frontmatter of the diagram. The frontmatter is a YAML block at the top of the diagram. + +```mermaid-example +--- +title: Hello Title +config: + theme: base + themeVariables: + primaryColor: "#00ff00" +--- +flowchart + Hello --> World + +``` + +```mermaid +--- +title: Hello Title +config: + theme: base + themeVariables: + primaryColor: "#00ff00" +--- +flowchart + Hello --> World + +``` + ## Theme configuration ## Starting mermaid diff --git a/docs/config/directives.md b/docs/config/directives.md index 27fd767c75..414565d53e 100644 --- a/docs/config/directives.md +++ b/docs/config/directives.md @@ -6,6 +6,9 @@ # Directives +> **Warning** +> Directives are deprecated from v\. Please use the `config` key in frontmatter to pass configuration. See [Configuration](./configuration.md) for more details. + ## Directives Directives give a diagram author the capability to alter the appearance of a diagram before rendering by changing the applied configuration. diff --git a/docs/config/setup/modules/config.md b/docs/config/setup/modules/config.md index f1de64e2df..e779224c15 100644 --- a/docs/config/setup/modules/config.md +++ b/docs/config/setup/modules/config.md @@ -118,7 +118,7 @@ The siteConfig #### Defined in -[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218) +[config.ts:217](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L217) --- diff --git a/packages/mermaid/src/docs/config/configuration.md b/packages/mermaid/src/docs/config/configuration.md index d248944ddd..e52f2c6d5d 100644 --- a/packages/mermaid/src/docs/config/configuration.md +++ b/packages/mermaid/src/docs/config/configuration.md @@ -4,10 +4,28 @@ When mermaid starts, configuration is extracted to determine a configuration to - The default configuration - Overrides at the site level are set by the initialize call, and will be applied to all diagrams in the site/app. The term for this is the **siteConfig**. -- Directives - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config. +- Frontmatter (v+) - diagram authors can update select configuration parameters in the frontmatter of the diagram. These are applied to the render config. +- Directives (Deprecated by Frontmatter) - diagram authors can update select configuration parameters directly in the diagram code via directives. These are applied to the render config. **The render config** is configuration that is used when rendering by applying these configurations. +## Frontmatter config + +The entire mermaid configuration (except the secure configs) can be overridden by the diagram author in the frontmatter of the diagram. The frontmatter is a YAML block at the top of the diagram. + +```mermaid-example +--- +title: Hello Title +config: + theme: base + themeVariables: + primaryColor: "#00ff00" +--- +flowchart + Hello --> World + +``` + ## Theme configuration ## Starting mermaid diff --git a/packages/mermaid/src/docs/config/directives.md b/packages/mermaid/src/docs/config/directives.md index c85d1d245e..5ce9fba6da 100644 --- a/packages/mermaid/src/docs/config/directives.md +++ b/packages/mermaid/src/docs/config/directives.md @@ -1,5 +1,9 @@ # Directives +```warning +Directives are deprecated from v. Please use the `config` key in frontmatter to pass configuration. See [Configuration](./configuration.md) for more details. +``` + ## Directives Directives give a diagram author the capability to alter the appearance of a diagram before rendering by changing the applied configuration. From 2b9dc0ea80ce1ef3df06a44e53a8ac6b3b650b85 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 21 Aug 2023 15:02:29 +0530 Subject: [PATCH 10/12] docs: Add frontmatter config demos --- demos/er.html | 2 ++ demos/flowchart.html | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/demos/er.html b/demos/er.html index 49f0a683f0..65e3049c8c 100644 --- a/demos/er.html +++ b/demos/er.html @@ -21,6 +21,8 @@
       ---
       title: This is a title
+      config:
+        theme: forest
       ---
       erDiagram
         %% title This is a title
diff --git a/demos/flowchart.html b/demos/flowchart.html
index 92c5bbd6e4..8389510b28 100644
--- a/demos/flowchart.html
+++ b/demos/flowchart.html
@@ -123,6 +123,13 @@ 

graph

flowchart

+    ---
+    title: This is another complicated flow
+    config:
+      theme: base
+      flowchart:
+        curve: cardinal
+    ---
     flowchart LR
     sid-B3655226-6C29-4D00-B685-3D5C734DC7E1["
 

From 5c9eafabae396813511e3cd0efd3ba599189e147 Mon Sep 17 00:00:00 2001
From: sidharthv96 
Date: Mon, 21 Aug 2023 09:36:24 +0000
Subject: [PATCH 11/12] Update docs

---
 docs/config/setup/modules/config.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/config/setup/modules/config.md b/docs/config/setup/modules/config.md
index e779224c15..f1de64e2df 100644
--- a/docs/config/setup/modules/config.md
+++ b/docs/config/setup/modules/config.md
@@ -118,7 +118,7 @@ The siteConfig
 
 #### Defined in
 
-[config.ts:217](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L217)
+[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218)
 
 ---
 

From 3944e9ac009c64176ab91e53f726cf17f8072711 Mon Sep 17 00:00:00 2001
From: Sidharth Vinod 
Date: Tue, 22 Aug 2023 13:36:32 +0530
Subject: [PATCH 12/12] chore: Remove circular dependency

---
 packages/mermaid/src/diagram-api/frontmatter.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/mermaid/src/diagram-api/frontmatter.ts b/packages/mermaid/src/diagram-api/frontmatter.ts
index 65e290f930..0fd2917eae 100644
--- a/packages/mermaid/src/diagram-api/frontmatter.ts
+++ b/packages/mermaid/src/diagram-api/frontmatter.ts
@@ -1,6 +1,6 @@
-import { MermaidConfig } from '../config.type.js';
+import type { MermaidConfig } from '../config.type.js';
 import { frontMatterRegex } from './regexes.js';
-import { DiagramDB } from './types.js';
+import type { DiagramDB } from './types.js';
 // The "* as yaml" part is necessary for tree-shaking
 import * as yaml from 'js-yaml';