chore: Fix types of MermaidConfig

This commit is contained in:
Sidharth Vinod
2024-05-13 09:59:01 +05:30
parent cee1cf0ce2
commit 453802d4ce
19 changed files with 157 additions and 123 deletions

View File

@@ -83,6 +83,7 @@ NODIR
NSTR NSTR
outdir outdir
Qcontrolx Qcontrolx
regexes
reinit reinit
rels rels
reqs reqs

View File

@@ -6,6 +6,16 @@
# Module: config # Module: config
## Type Aliases
### PartialMermaidConfig
Ƭ **PartialMermaidConfig**: `PartialDeep`<`MermaidConfig`>
#### Defined in
[config.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L10)
## Variables ## Variables
### defaultConfig ### defaultConfig
@@ -14,7 +24,7 @@
#### Defined in #### Defined in
[config.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L8) [config.ts:9](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L9)
## Functions ## Functions
@@ -26,9 +36,9 @@ Pushes in a directive to the configuration
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| :---------- | :-------------- | :----------------------- | | :---------- | :--------------------------------------- | :----------------------- |
| `directive` | `MermaidConfig` | The directive to push in | | `directive` | `PartialObjectDeep`<`MermaidConfig`, {}> | The directive to push in |
#### Returns #### Returns
@@ -36,7 +46,7 @@ Pushes in a directive to the configuration
#### Defined in #### Defined in
[config.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L188) [config.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L193)
--- ---
@@ -60,7 +70,7 @@ The currentConfig
#### Defined in #### Defined in
[config.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L131) [config.ts:136](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L136)
--- ---
@@ -84,7 +94,7 @@ The siteConfig
#### Defined in #### Defined in
[config.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L96) [config.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L101)
--- ---
@@ -118,7 +128,7 @@ The siteConfig
#### Defined in #### Defined in
[config.ts:218](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L218) [config.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L223)
--- ---
@@ -147,7 +157,7 @@ options in-place
#### Defined in #### Defined in
[config.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L146) [config.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L151)
--- ---
@@ -157,9 +167,9 @@ options in-place
#### Parameters #### Parameters
| Name | Type | | Name | Type |
| :----- | :-------------- | | :----- | :--------------------------------------- |
| `conf` | `MermaidConfig` | | `conf` | `PartialObjectDeep`<`MermaidConfig`, {}> |
#### Returns #### Returns
@@ -167,7 +177,7 @@ options in-place
#### Defined in #### Defined in
[config.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L75) [config.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L80)
--- ---
@@ -187,9 +197,9 @@ corresponding siteConfig value.
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| :----- | :-------------- | :-------------------------- | | :----- | :--------------------------------------- | :-------------------------- |
| `conf` | `MermaidConfig` | The potential currentConfig | | `conf` | `PartialObjectDeep`<`MermaidConfig`, {}> | The potential currentConfig |
#### Returns #### Returns
@@ -199,7 +209,7 @@ The currentConfig merged with the sanitized conf
#### Defined in #### Defined in
[config.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L113) [config.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L118)
--- ---
@@ -220,9 +230,9 @@ function _Default value: At default, will mirror Global Config_
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| :----- | :-------------- | :------------------------------------------ | | :----- | :--------------------------------------- | :------------------------------------------ |
| `conf` | `MermaidConfig` | The base currentConfig to use as siteConfig | | `conf` | `PartialObjectDeep`<`MermaidConfig`, {}> | The base currentConfig to use as siteConfig |
#### Returns #### Returns
@@ -232,7 +242,7 @@ The new siteConfig
#### Defined in #### Defined in
[config.ts:61](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L61) [config.ts:66](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L66)
--- ---
@@ -242,10 +252,10 @@ The new siteConfig
#### Parameters #### Parameters
| Name | Type | | Name | Type |
| :------------ | :----------------- | | :------------ | :------------------------------------------ |
| `siteCfg` | `MermaidConfig` | | `siteCfg` | `MermaidConfig` |
| `_directives` | `MermaidConfig`\[] | | `_directives` | `PartialObjectDeep`<`MermaidConfig`, {}>\[] |
#### Returns #### Returns
@@ -253,7 +263,7 @@ The new siteConfig
#### Defined in #### Defined in
[config.ts:15](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L15) [config.ts:17](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L17)
--- ---
@@ -263,9 +273,9 @@ The new siteConfig
#### Parameters #### Parameters
| Name | Type | | Name | Type |
| :----- | :-------------- | | :----- | :--------------------------------------- |
| `conf` | `MermaidConfig` | | `conf` | `PartialObjectDeep`<`MermaidConfig`, {}> |
#### Returns #### Returns
@@ -273,4 +283,4 @@ The new siteConfig
#### Defined in #### Defined in
[config.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L79) [config.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L84)

View File

@@ -32,7 +32,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
### mermaidAPI ### mermaidAPI
`Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md) & { `suppressErrors`: `true` }) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md) | `false`>(`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md)> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }> `Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`, `metadata`: `Pick`<`DiagramMetadata`, `"title"`>) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `PartialObjectDeep`<`MermaidConfig`, {}>) => `void` ; `parse`: (`text`: `string`, `parseOptions`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md) & { `suppressErrors`: `true` }) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md) | `false`>(`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<[`ParseResult`](../interfaces/mermaidAPI.ParseResult.md)> ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `PartialObjectDeep`<`MermaidConfig`, {}>) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `PartialObjectDeep`<`MermaidConfig`, {}>) => `MermaidConfig` = configApi.updateSiteConfig }>
## mermaidAPI configuration defaults ## mermaidAPI configuration defaults

View File

@@ -17,14 +17,14 @@ describe('when working with site config', () => {
expect(config_1).toEqual(config_2); expect(config_1).toEqual(config_2);
}); });
it('should respect secure keys when applying directives', () => { it('should respect secure keys when applying directives', () => {
const config_0: MermaidConfig = { const config_0 = {
fontFamily: 'foo-font', fontFamily: 'foo-font',
securityLevel: 'strict', // can't be changed securityLevel: 'strict', // can't be changed
fontSize: 12345, // can't be changed fontSize: 12345, // can't be changed
secure: [...configApi.defaultConfig.secure!, 'fontSize'], secure: [...configApi.defaultConfig.secure!, 'fontSize'],
}; } as MermaidConfig;
configApi.setSiteConfig(config_0); configApi.setSiteConfig(config_0);
const directive: MermaidConfig = { const directive: configApi.PartialMermaidConfig = {
fontFamily: 'baf', fontFamily: 'baf',
// fontSize and securityLevel shouldn't be changed // fontSize and securityLevel shouldn't be changed
fontSize: 54321, fontSize: 54321,

View File

@@ -1,24 +1,29 @@
import type { PartialDeep } from 'type-fest';
import assignWithDepth from './assignWithDepth.js'; import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfig } from './config.type.js';
import config from './defaultConfig.js';
import { log } from './logger.js'; import { log } from './logger.js';
import theme from './themes/index.js'; import theme from './themes/index.js';
import config from './defaultConfig.js';
import type { MermaidConfig } from './config.type.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js'; import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const defaultConfig: MermaidConfig = Object.freeze(config); export const defaultConfig: MermaidConfig = Object.freeze(config);
export type PartialMermaidConfig = PartialDeep<MermaidConfig>;
let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig); let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
let configFromInitialize: MermaidConfig; let configFromInitialize: PartialMermaidConfig;
let directives: MermaidConfig[] = []; let directives: PartialMermaidConfig[] = [];
let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig); let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig);
export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => { export const updateCurrentConfig = (
siteCfg: MermaidConfig,
_directives: PartialMermaidConfig[]
) => {
// start with config being the siteConfig // start with config being the siteConfig
let cfg: MermaidConfig = assignWithDepth({}, siteCfg); let cfg: MermaidConfig = assignWithDepth({}, siteCfg);
// let sCfg = assignWithDepth(defaultConfig, siteConfigDelta); // let sCfg = assignWithDepth(defaultConfig, siteConfigDelta);
// Join directives // Join directives
let sumOfDirectives: MermaidConfig = {}; let sumOfDirectives: PartialMermaidConfig = {};
for (const d of _directives) { for (const d of _directives) {
sanitize(d); sanitize(d);
// Apply the data from the directive where the overrides the themeVariables // Apply the data from the directive where the overrides the themeVariables
@@ -58,7 +63,7 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: Mermaid
* @param conf - The base currentConfig to use as siteConfig * @param conf - The base currentConfig to use as siteConfig
* @returns The new siteConfig * @returns The new siteConfig
*/ */
export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => { export const setSiteConfig = (conf: PartialMermaidConfig): MermaidConfig => {
siteConfig = assignWithDepth({}, defaultConfig); siteConfig = assignWithDepth({}, defaultConfig);
siteConfig = assignWithDepth(siteConfig, conf); siteConfig = assignWithDepth(siteConfig, conf);
@@ -72,11 +77,11 @@ export const setSiteConfig = (conf: MermaidConfig): MermaidConfig => {
return siteConfig; return siteConfig;
}; };
export const saveConfigFromInitialize = (conf: MermaidConfig): void => { export const saveConfigFromInitialize = (conf: PartialMermaidConfig): void => {
configFromInitialize = assignWithDepth({}, conf); configFromInitialize = assignWithDepth({}, conf);
}; };
export const updateSiteConfig = (conf: MermaidConfig): MermaidConfig => { export const updateSiteConfig = (conf: PartialMermaidConfig): MermaidConfig => {
siteConfig = assignWithDepth(siteConfig, conf); siteConfig = assignWithDepth(siteConfig, conf);
updateCurrentConfig(siteConfig, directives); updateCurrentConfig(siteConfig, directives);
@@ -110,7 +115,7 @@ export const getSiteConfig = (): MermaidConfig => {
* @param conf - The potential currentConfig * @param conf - The potential currentConfig
* @returns The currentConfig merged with the sanitized conf * @returns The currentConfig merged with the sanitized conf
*/ */
export const setConfig = (conf: MermaidConfig): MermaidConfig => { export const setConfig = (conf: PartialMermaidConfig): MermaidConfig => {
checkConfig(conf); checkConfig(conf);
assignWithDepth(currentConfig, conf); assignWithDepth(currentConfig, conf);
@@ -185,7 +190,7 @@ export const sanitize = (options: any) => {
* *
* @param directive - The directive to push in * @param directive - The directive to push in
*/ */
export const addDirective = (directive: MermaidConfig) => { export const addDirective = (directive: PartialMermaidConfig) => {
sanitizeDirective(directive); sanitizeDirective(directive);
// If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables // If the directive has a fontFamily, but no themeVariables, add the fontFamily to the themeVariables
@@ -236,7 +241,7 @@ const issueWarning = (warning: ConfigWarningStrings) => {
issuedWarnings[warning] = true; issuedWarnings[warning] = true;
}; };
const checkConfig = (config: MermaidConfig) => { const checkConfig = (config: PartialMermaidConfig) => {
if (!config) { if (!config) {
return; return;
} }

View File

@@ -1,13 +1,13 @@
import type { MermaidConfig } from '../config.type.js'; import type { MermaidConfig } from '../config.type.js';
import { UnknownDiagramError } from '../errors.js';
import { log } from '../logger.js'; import { log } from '../logger.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import type { import type {
DetectorRecord, DetectorRecord,
DiagramDetector, DiagramDetector,
DiagramLoader, DiagramLoader,
ExternalDiagramDefinition, ExternalDiagramDefinition,
} from './types.js'; } from './types.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import { UnknownDiagramError } from '../errors.js';
export const detectors: Record<string, DetectorRecord> = {}; export const detectors: Record<string, DetectorRecord> = {};

View File

@@ -1,4 +1,5 @@
import { it, describe, expect } from 'vitest'; import { describe, expect, it } from 'vitest';
import type { MermaidConfig } from '../config.type.js';
import { detectType } from './detectType.js'; import { detectType } from './detectType.js';
import { addDiagrams } from './diagram-orchestration.js'; import { addDiagrams } from './diagram-orchestration.js';
@@ -46,30 +47,40 @@ describe('diagram-orchestration', () => {
// graph & dagre-d3 => flowchart // graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B')).toBe('flowchart'); expect(detectType('graph TD; A-->B')).toBe('flowchart');
// graph & dagre-d3 => flowchart // graph & dagre-d3 => flowchart
expect(detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } })).toBe( expect(
'flowchart' detectType('graph TD; A-->B', {
); flowchart: { defaultRenderer: 'dagre-d3' },
} as MermaidConfig)
).toBe('flowchart');
// flowchart & dagre-d3 => error // flowchart & dagre-d3 => error
expect(() => expect(() =>
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-d3' } }) detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-d3' },
} as MermaidConfig)
).toThrowErrorMatchingInlineSnapshot( ).toThrowErrorMatchingInlineSnapshot(
`[UnknownDiagramError: No diagram type detected matching given configuration for text: flowchart TD; A-->B]` `[UnknownDiagramError: No diagram type detected matching given configuration for text: flowchart TD; A-->B]`
); );
// graph & dagre-wrapper => flowchart-v2 // graph & dagre-wrapper => flowchart-v2
expect( expect(
detectType('graph TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } }) detectType('graph TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-wrapper' },
} as MermaidConfig)
).toBe('flowchart-v2'); ).toBe('flowchart-v2');
// flowchart ==> flowchart-v2 // flowchart ==> flowchart-v2
expect(detectType('flowchart TD; A-->B')).toBe('flowchart-v2'); expect(detectType('flowchart TD; A-->B')).toBe('flowchart-v2');
// flowchart && dagre-wrapper ==> flowchart-v2 // flowchart && dagre-wrapper ==> flowchart-v2
expect( expect(
detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'dagre-wrapper' } }) detectType('flowchart TD; A-->B', {
flowchart: { defaultRenderer: 'dagre-wrapper' },
} as MermaidConfig)
).toBe('flowchart-v2'); ).toBe('flowchart-v2');
// flowchart && elk ==> flowchart-elk // flowchart && elk ==> flowchart-elk
expect(detectType('flowchart TD; A-->B', { flowchart: { defaultRenderer: 'elk' } })).toBe( expect(
'flowchart-elk' detectType('flowchart TD; A-->B', {
); flowchart: { defaultRenderer: 'elk' },
} as MermaidConfig)
).toBe('flowchart-elk');
}); });
it('should not detect flowchart if pie contains flowchart', () => { it('should not detect flowchart if pie contains flowchart', () => {

View File

@@ -1,4 +1,5 @@
import type { GanttDiagramConfig, MermaidConfig } from '../config.type.js'; import type { PartialMermaidConfig } from '../config.js';
import type { GanttDiagramConfig } from '../config.type.js';
import { frontMatterRegex } from './regexes.js'; import { frontMatterRegex } from './regexes.js';
// The "* as yaml" part is necessary for tree-shaking // The "* as yaml" part is necessary for tree-shaking
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
@@ -7,7 +8,7 @@ interface FrontMatterMetadata {
title?: string; title?: string;
// Allows custom display modes. Currently used for compact mode in gantt charts. // Allows custom display modes. Currently used for compact mode in gantt charts.
displayMode?: GanttDiagramConfig['displayMode']; displayMode?: GanttDiagramConfig['displayMode'];
config?: MermaidConfig; config?: PartialMermaidConfig;
} }
export interface FrontMatterResult { export interface FrontMatterResult {

View File

@@ -2,11 +2,12 @@
import type * as d3 from 'd3'; import type * as d3 from 'd3';
import type { SetRequired } from 'type-fest'; import type { SetRequired } from 'type-fest';
import type { Diagram } from '../Diagram.js'; import type { Diagram } from '../Diagram.js';
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js'; import type { PartialMermaidConfig } from '../config.js';
import type { BaseDiagramConfig } from '../config.type.js';
export interface DiagramMetadata { export interface DiagramMetadata {
title?: string; title?: string;
config?: MermaidConfig; config?: PartialMermaidConfig;
} }
export interface InjectUtils { export interface InjectUtils {
@@ -78,7 +79,7 @@ export interface DiagramDefinition {
renderer: DiagramRenderer; renderer: DiagramRenderer;
parser: ParserDefinition; parser: ParserDefinition;
styles?: any; styles?: any;
init?: (config: MermaidConfig) => void; init?: (config: PartialMermaidConfig) => void;
injectUtils?: ( injectUtils?: (
_log: InjectUtils['_log'], _log: InjectUtils['_log'],
_setLogLevel: InjectUtils['_setLogLevel'], _setLogLevel: InjectUtils['_setLogLevel'],
@@ -102,7 +103,7 @@ export interface ExternalDiagramDefinition {
loader: DiagramLoader; loader: DiagramLoader;
} }
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean; export type DiagramDetector = (text: string, config?: PartialMermaidConfig) => boolean;
export type DiagramLoader = () => Promise<{ id: string; diagram: DiagramDefinition }>; export type DiagramLoader = () => Promise<{ id: string; diagram: DiagramDefinition }>;
/** /**

View File

@@ -1,17 +1,16 @@
// @ts-ignore: JISON doesn't support types import type { DiagramDefinition } from '../../diagram-api/types.js';
import parser from './parser/c4Diagram.jison';
import db from './c4Db.js'; import db from './c4Db.js';
import renderer from './c4Renderer.js'; import renderer from './c4Renderer.js';
// @ts-ignore: JISON doesn't support types
import parser from './parser/c4Diagram.jison';
import styles from './styles.js'; import styles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = { export const diagram: DiagramDefinition = {
parser, parser,
db, db,
renderer, renderer,
styles, styles,
init: ({ c4, wrap }: MermaidConfig) => { init: ({ c4, wrap }) => {
renderer.setConf(c4); renderer.setConf(c4);
db.setWrap(wrap); db.setWrap(wrap);
}, },

View File

@@ -1,4 +1,5 @@
import { sanitizeText, removeScript, parseGenericTypes, countOccurrence } from './common.js'; import type { MermaidConfig } from '../../config.type.js';
import { countOccurrence, parseGenericTypes, removeScript, sanitizeText } from './common.js';
describe('when securityLevel is antiscript, all script must be removed', () => { describe('when securityLevel is antiscript, all script must be removed', () => {
/** /**
@@ -67,7 +68,7 @@ describe('Sanitize text', () => {
const result = sanitizeText(maliciousStr, { const result = sanitizeText(maliciousStr, {
securityLevel: 'strict', securityLevel: 'strict',
flowchart: { htmlLabels: true }, flowchart: { htmlLabels: true },
}); } as MermaidConfig);
expect(result).not.toContain('javascript:alert(1)'); expect(result).not.toContain('javascript:alert(1)');
}); });
}); });

View File

@@ -1,17 +1,17 @@
// @ts-ignore: JISON doesn't support types import { setConfig } from '../../diagram-api/diagramAPI.js';
import flowParser from './parser/flow.jison'; import type { DiagramDefinition } from '../../diagram-api/types.js';
import flowDb from './flowDb.js'; import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js'; import flowRendererV2 from './flowRenderer-v2.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
import flowStyles from './styles.js'; import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
export const diagram = { export const diagram: DiagramDefinition = {
parser: flowParser, parser: flowParser,
db: flowDb, db: flowDb,
renderer: flowRendererV2, renderer: flowRendererV2,
styles: flowStyles, styles: flowStyles,
init: (cnf: MermaidConfig) => { init: (cnf) => {
if (!cnf.flowchart) { if (!cnf.flowchart) {
cnf.flowchart = {}; cnf.flowchart = {};
} }

View File

@@ -1,17 +1,17 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import flowDb from './flowDb.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowRenderer from './flowRenderer.js';
// @ts-ignore: JISON doesn't support types // @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison'; import flowParser from './parser/flow.jison';
import flowDb from './flowDb.js';
import flowRenderer from './flowRenderer.js';
import flowRendererV2 from './flowRenderer-v2.js';
import flowStyles from './styles.js'; import flowStyles from './styles.js';
import type { MermaidConfig } from '../../config.type.js';
export const diagram = { export const diagram: DiagramDefinition = {
parser: flowParser, parser: flowParser,
db: flowDb, db: flowDb,
renderer: flowRendererV2, renderer: flowRendererV2,
styles: flowStyles, styles: flowStyles,
init: (cnf: MermaidConfig) => { init: (cnf) => {
if (!cnf.flowchart) { if (!cnf.flowchart) {
cnf.flowchart = {}; cnf.flowchart = {};
} }

View File

@@ -1,4 +1,4 @@
import { vi, it, expect, describe, beforeEach } from 'vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest';
// ------------------------------------- // -------------------------------------
// Mocks and mocking // Mocks and mocking
@@ -67,8 +67,8 @@ vi.mock('stylis', () => {
}); });
import { compile, serialize } from 'stylis'; import { compile, serialize } from 'stylis';
import { decodeEntities, encodeEntities } from './utils.js';
import { Diagram } from './Diagram.js'; import { Diagram } from './Diagram.js';
import { decodeEntities, encodeEntities } from './utils.js';
/** /**
* @see https://vitest.dev/guide/mocking.html Mock part of a module * @see https://vitest.dev/guide/mocking.html Mock part of a module
@@ -283,12 +283,12 @@ describe('mermaidAPI', () => {
describe('createCssStyles', () => { describe('createCssStyles', () => {
const serif = 'serif'; const serif = 'serif';
const sansSerif = 'sans-serif'; const sansSerif = 'sans-serif';
const mocked_config_with_htmlLabels: MermaidConfig = { const mocked_config_with_htmlLabels = {
themeCSS: 'default', themeCSS: 'default',
fontFamily: serif, fontFamily: serif,
altFontFamily: sansSerif, altFontFamily: sansSerif,
htmlLabels: true, htmlLabels: true,
}; } as MermaidConfig;
it('gets the cssStyles from the theme', () => { it('gets the cssStyles from the theme', () => {
const styles = createCssStyles(mocked_config_with_htmlLabels, null); const styles = createCssStyles(mocked_config_with_htmlLabels, null);
@@ -389,14 +389,14 @@ describe('mermaidAPI', () => {
}); });
it('there are flowchart.htmlLabels in the configuration', () => { it('there are flowchart.htmlLabels in the configuration', () => {
const mocked_config_flowchart_htmlLabels: MermaidConfig = { const mocked_config_flowchart_htmlLabels = {
themeCSS: 'default', themeCSS: 'default',
fontFamily: 'serif', fontFamily: 'serif',
altFontFamily: 'sans-serif', altFontFamily: 'sans-serif',
flowchart: { flowchart: {
htmlLabels: true, htmlLabels: true,
}, },
}; } as MermaidConfig;
expect_correct_styles_with_htmlElements(mocked_config_flowchart_htmlLabels); expect_correct_styles_with_htmlElements(mocked_config_flowchart_htmlLabels);
}); });
@@ -405,7 +405,7 @@ describe('mermaidAPI', () => {
themeCSS: 'default', themeCSS: 'default',
fontFamily: 'serif', fontFamily: 'serif',
altFontFamily: 'sans-serif', altFontFamily: 'sans-serif',
}; } as MermaidConfig;
describe('creates styles for shape elements "rect", "polygon", "ellipse", and "circle"', () => { describe('creates styles for shape elements "rect", "polygon", "ellipse", and "circle"', () => {
const htmlElements = ['rect', 'polygon', 'ellipse', 'circle']; const htmlElements = ['rect', 'polygon', 'ellipse', 'circle'];
@@ -430,7 +430,7 @@ describe('mermaidAPI', () => {
themeCSS: 'default', themeCSS: 'default',
htmlLabels: true, htmlLabels: true,
themeVariables: { fontFamily: 'serif' }, themeVariables: { fontFamily: 'serif' },
}; } as MermaidConfig;
const classDef1 = { id: 'classDef1', styles: ['style1-1'], textStyles: [] }; const classDef1 = { id: 'classDef1', styles: ['style1-1'], textStyles: [] };

View File

@@ -14,22 +14,22 @@
import { select } from 'd3'; import { select } from 'd3';
import { compile, serialize, stringify } from 'stylis'; import { compile, serialize, stringify } from 'stylis';
// @ts-ignore: TODO Fix ts errors // @ts-ignore: TODO Fix ts errors
import DOMPurify from 'dompurify';
import isEmpty from 'lodash-es/isEmpty.js';
import { version } from '../package.json'; import { version } from '../package.json';
import * as configApi from './config.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import { Diagram } from './Diagram.js'; import { Diagram } from './Diagram.js';
import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js';
import * as configApi from './config.js';
import type { MermaidConfig } from './config.type.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
import { evaluate } from './diagrams/common/common.js';
import errorRenderer from './diagrams/error/errorRenderer.js'; import errorRenderer from './diagrams/error/errorRenderer.js';
import { attachFunctions } from './interactionDb.js'; import { attachFunctions } from './interactionDb.js';
import { log, setLogLevel } from './logger.js'; import { log, setLogLevel } from './logger.js';
import { preprocessDiagram } from './preprocess.js';
import getStyles from './styles.js'; import getStyles from './styles.js';
import theme from './themes/index.js'; import theme from './themes/index.js';
import DOMPurify from 'dompurify';
import type { MermaidConfig } from './config.type.js';
import { evaluate } from './diagrams/common/common.js';
import isEmpty from 'lodash-es/isEmpty.js';
import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility.js';
import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js';
import { preprocessDiagram } from './preprocess.js';
import { decodeEntities } from './utils.js'; import { decodeEntities } from './utils.js';
const MAX_TEXTLENGTH = 50_000; const MAX_TEXTLENGTH = 50_000;
@@ -517,7 +517,7 @@ const render = async function (
/** /**
* @param options - Initial Mermaid options * @param options - Initial Mermaid options
*/ */
function initialize(options: MermaidConfig = {}) { function initialize(options: configApi.PartialMermaidConfig = {}) {
// Handle legacy location of font-family configuration // Handle legacy location of font-family configuration
if (options?.fontFamily && !options.themeVariables?.fontFamily) { if (options?.fontFamily && !options.themeVariables?.fontFamily) {
if (!options.themeVariables) { if (!options.themeVariables) {

View File

@@ -1,3 +1,4 @@
import type { PartialMermaidConfig } from './config.js';
import { cleanupComments } from './diagram-api/comments.js'; import { cleanupComments } from './diagram-api/comments.js';
import { extractFrontMatter } from './diagram-api/frontmatter.js'; import { extractFrontMatter } from './diagram-api/frontmatter.js';
import type { DiagramMetadata } from './diagram-api/types.js'; import type { DiagramMetadata } from './diagram-api/types.js';
@@ -30,7 +31,7 @@ const processFrontmatter = (code: string) => {
}; };
const processDirectives = (code: string) => { const processDirectives = (code: string) => {
const initDirective = utils.detectInit(code) ?? {}; const initDirective: PartialMermaidConfig = utils.detectInit(code) ?? {};
const wrapDirectives = utils.detectDirective(code, 'wrap'); const wrapDirectives = utils.detectDirective(code, 'wrap');
if (Array.isArray(wrapDirectives)) { if (Array.isArray(wrapDirectives)) {
initDirective.wrap = wrapDirectives.some(({ type }) => { initDirective.wrap = wrapDirectives.some(({ type }) => {

View File

@@ -1,14 +1,14 @@
import type { Content } from 'mdast'; import type { Content } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown'; import { fromMarkdown } from 'mdast-util-from-markdown';
import { dedent } from 'ts-dedent'; import { dedent } from 'ts-dedent';
import type { PartialMermaidConfig } from '../config.js';
import type { MarkdownLine, MarkdownWordType } from './types.js'; import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';
/** /**
* @param markdown - markdown to process * @param markdown - markdown to process
* @returns processed markdown * @returns processed markdown
*/ */
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string { function preprocessMarkdown(markdown: string, { markdownAutoWrap }: PartialMermaidConfig): string {
// Replace multiple newlines with a single newline // Replace multiple newlines with a single newline
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n'); const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line // Remove extra spaces at the beginning of each line
@@ -22,7 +22,10 @@ function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfi
/** /**
* @param markdown - markdown to split into lines * @param markdown - markdown to split into lines
*/ */
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] { export function markdownToLines(
markdown: string,
config: PartialMermaidConfig = {}
): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config); const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const { children } = fromMarkdown(preprocessedMarkdown); const { children } = fromMarkdown(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]]; const lines: MarkdownLine[] = [[]];
@@ -60,7 +63,7 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
return lines; return lines;
} }
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) { export function markdownToHTML(markdown: string, { markdownAutoWrap }: PartialMermaidConfig = {}) {
const { children } = fromMarkdown(markdown); const { children } = fromMarkdown(markdown);
function output(node: Content): string { function output(node: Content): string {

View File

@@ -7,12 +7,12 @@ import {
curveBumpX, curveBumpX,
curveBumpY, curveBumpY,
curveBundle, curveBundle,
curveCardinal,
curveCardinalClosed, curveCardinalClosed,
curveCardinalOpen, curveCardinalOpen,
curveCardinal, curveCatmullRom,
curveCatmullRomClosed, curveCatmullRomClosed,
curveCatmullRomOpen, curveCatmullRomOpen,
curveCatmullRom,
curveLinear, curveLinear,
curveLinearClosed, curveLinearClosed,
curveMonotoneX, curveMonotoneX,
@@ -23,17 +23,18 @@ import {
curveStepBefore, curveStepBefore,
select, select,
} from 'd3'; } from 'd3';
import common from './diagrams/common/common.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
import { log } from './logger.js';
import { detectType } from './diagram-api/detectType.js';
import assignWithDepth from './assignWithDepth.js';
import type { MermaidConfig } from './config.type.js';
import memoize from 'lodash-es/memoize.js'; import memoize from 'lodash-es/memoize.js';
import merge from 'lodash-es/merge.js'; import merge from 'lodash-es/merge.js';
import assignWithDepth from './assignWithDepth.js';
import type { PartialMermaidConfig } from './config.js';
import type { MermaidConfig } from './config.type.js';
import { detectType } from './diagram-api/detectType.js';
import { directiveRegex } from './diagram-api/regexes.js'; import { directiveRegex } from './diagram-api/regexes.js';
import common from './diagrams/common/common.js';
import { log } from './logger.js';
import type { D3Element } from './mermaidAPI.js'; import type { D3Element } from './mermaidAPI.js';
import type { Point, TextDimensionConfig, TextDimensions } from './types.js'; import type { Point, TextDimensionConfig, TextDimensions } from './types.js';
import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const ZERO_WIDTH_SPACE = '\u200b'; export const ZERO_WIDTH_SPACE = '\u200b';
@@ -97,26 +98,23 @@ const directiveWithoutOpen =
* @param config - Optional mermaid configuration object. * @param config - Optional mermaid configuration object.
* @returns The json object representing the init passed to mermaid.initialize() * @returns The json object representing the init passed to mermaid.initialize()
*/ */
export const detectInit = function ( export const detectInit = function (text: string): PartialMermaidConfig | undefined {
text: string,
config?: MermaidConfig
): MermaidConfig | undefined {
const inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/); const inits = detectDirective(text, /(?:init\b)|(?:initialize\b)/);
let results: MermaidConfig & { config?: unknown } = {}; let results: PartialMermaidConfig & { config?: unknown } = {};
if (Array.isArray(inits)) { if (Array.isArray(inits)) {
const args = inits.map((init) => init.args); const args = inits.map((init) => init.args);
sanitizeDirective(args); sanitizeDirective(args);
results = assignWithDepth(results, [...args]); results = assignWithDepth(results, [...args]);
} else { } else {
results = inits.args as MermaidConfig; results = inits.args as PartialMermaidConfig;
} }
if (!results) { if (!results) {
return; return;
} }
let type = detectType(text, config); let type = detectType(text);
// Move the `config` value to appropriate diagram type value // Move the `config` value to appropriate diagram type value
const prop = 'config'; const prop = 'config';
@@ -250,7 +248,10 @@ export function interpolateToCurve(
* @param config - Configuration passed to MermaidJS * @param config - Configuration passed to MermaidJS
* @returns The formatted URL or `undefined`. * @returns The formatted URL or `undefined`.
*/ */
export function formatUrl(linkStr: string, config: MermaidConfig): string | undefined { export function formatUrl(
linkStr: string,
config: Pick<MermaidConfig, 'securityLevel'>
): string | undefined {
const url = linkStr.trim(); const url = linkStr.trim();
if (!url) { if (!url) {

View File

@@ -3,7 +3,7 @@ import type { FlowchartDiagramConfig } from '../config.type.js';
export const getSubGraphTitleMargins = ({ export const getSubGraphTitleMargins = ({
flowchart, flowchart,
}: { }: {
flowchart: FlowchartDiagramConfig; flowchart: Pick<FlowchartDiagramConfig, 'subGraphTitleMargin'>;
}): { }): {
subGraphTitleTopMargin: number; subGraphTitleTopMargin: number;
subGraphTitleBottomMargin: number; subGraphTitleBottomMargin: number;