diff --git a/README.md b/README.md
index 98741a689..f844e2cb9 100644
--- a/README.md
+++ b/README.md
@@ -165,13 +165,7 @@ class Class10 {
int id
size()
}
-namespace Namespace01 {
- class Class11
- class Class12 {
- int id
- size()
- }
-}
+
```
```mermaid
@@ -191,13 +185,7 @@ class Class10 {
int id
size()
}
-namespace Namespace01 {
- class Class11
- class Class12 {
- int id
- size()
- }
-}
+
```
### State diagram [docs - live editor]
diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts
index 4160f4cbd..c656f638d 100644
--- a/cypress/helpers/util.ts
+++ b/cypress/helpers/util.ts
@@ -52,29 +52,21 @@ export const imgSnapshotTest = (
api = false,
validation?: any
): void => {
- cy.log(JSON.stringify(_options));
- const options: CypressMermaidConfig = Object.assign(_options);
- if (!options.fontFamily) {
- options.fontFamily = 'courier';
- }
- if (!options.sequence) {
- options.sequence = {};
- }
- if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) {
- options.sequence.actorFontFamily = 'courier';
- }
- if (options.sequence && !options.sequence.noteFontFamily) {
- options.sequence.noteFontFamily = 'courier';
- }
- options.sequence.actorFontFamily = 'courier';
- options.sequence.noteFontFamily = 'courier';
- options.sequence.messageFontFamily = 'courier';
- if (options.sequence && !options.sequence.actorFontFamily) {
- options.sequence.actorFontFamily = 'courier';
- }
- if (!options.fontSize) {
- options.fontSize = 16;
- }
+ const options: CypressMermaidConfig = {
+ ..._options,
+ fontFamily: _options.fontFamily || 'courier',
+ // @ts-ignore TODO: Fix type of fontSize
+ fontSize: _options.fontSize || '16px',
+ sequence: {
+ ...(_options.sequence || {}),
+ actorFontFamily: 'courier',
+ noteFontFamily:
+ _options.sequence && _options.sequence.noteFontFamily
+ ? _options.sequence.noteFontFamily
+ : 'courier',
+ messageFontFamily: 'courier',
+ },
+ };
const url: string = mermaidUrl(graphStr, options, api);
openURLAndVerifyRendering(url, options, validation);
@@ -82,11 +74,10 @@ export const imgSnapshotTest = (
export const urlSnapshotTest = (
url: string,
- _options: CypressMermaidConfig,
+ options: CypressMermaidConfig,
_api = false,
validation?: any
): void => {
- const options: CypressMermaidConfig = Object.assign(_options);
openURLAndVerifyRendering(url, options, validation);
};
diff --git a/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md b/docs/config/setup/interfaces/mermaidAPI.ParseOptions.md
index 2082a081e..56f914641 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:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L78)
+[mermaidAPI.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L76)
diff --git a/docs/config/setup/interfaces/mermaidAPI.RenderResult.md b/docs/config/setup/interfaces/mermaidAPI.RenderResult.md
index f84a51b87..2c1504285 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:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98)
+[mermaidAPI.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L96)
---
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
#### Defined in
-[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88)
+[mermaidAPI.ts:86](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L86)
diff --git a/docs/config/setup/modules/mermaidAPI.md b/docs/config/setup/modules/mermaidAPI.md
index d5d4a1cbc..1ea19fac4 100644
--- a/docs/config/setup/modules/mermaidAPI.md
+++ b/docs/config/setup/modules/mermaidAPI.md
@@ -25,13 +25,13 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
#### Defined in
-[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82)
+[mermaidAPI.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L80)
## Variables
### mermaidAPI
-• `Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<`boolean`> ; `parseDirective`: (`p`: `any`, `statement`: `string`, `context`: `string`, `type`: `string`) => `void` ; `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`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<`boolean`> ; `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 }>
## mermaidAPI configuration defaults
@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in
-[mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
+[mermaidAPI.ts:662](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L662)
## Functions
@@ -127,7 +127,7 @@ Return the last node appended
#### Defined in
-[mermaidAPI.ts:310](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L310)
+[mermaidAPI.ts:318](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L318)
---
@@ -153,7 +153,7 @@ the cleaned up svgCode
#### Defined in
-[mermaidAPI.ts:256](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L256)
+[mermaidAPI.ts:264](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L264)
---
@@ -179,7 +179,7 @@ the string with all the user styles
#### Defined in
-[mermaidAPI.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L185)
+[mermaidAPI.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L193)
---
@@ -202,7 +202,7 @@ the string with all the user styles
#### Defined in
-[mermaidAPI.ts:233](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L233)
+[mermaidAPI.ts:241](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L241)
---
@@ -229,7 +229,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
-[mermaidAPI.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L169)
+[mermaidAPI.ts:177](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L177)
---
@@ -249,7 +249,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
-[mermaidAPI.ts:155](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L155)
+[mermaidAPI.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L163)
---
@@ -269,7 +269,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
-[mermaidAPI.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L126)
+[mermaidAPI.ts:134](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L134)
---
@@ -295,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in
-[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
+[mermaidAPI.ts:295](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L295)
---
@@ -320,4 +320,4 @@ Remove any existing elements from the given document
#### Defined in
-[mermaidAPI.ts:360](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L360)
+[mermaidAPI.ts:368](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L368)
diff --git a/packages/mermaid/src/Diagram.ts b/packages/mermaid/src/Diagram.ts
index 5e88be558..5ee04c03c 100644
--- a/packages/mermaid/src/Diagram.ts
+++ b/packages/mermaid/src/Diagram.ts
@@ -2,11 +2,9 @@ import * as configApi from './config.js';
import { log } from './logger.js';
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
-import { extractFrontMatter } from './diagram-api/frontmatter.js';
import { UnknownDiagramError } from './errors.js';
-import { cleanupComments } from './diagram-api/comments.js';
import type { DetailedError } from './utils.js';
-import type { DiagramDefinition } from './diagram-api/types.js';
+import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js';
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
@@ -22,7 +20,7 @@ export class Diagram {
private init?: DiagramDefinition['init'];
private detectError?: UnknownDiagramError;
- constructor(public text: string) {
+ constructor(public text: string, public metadata: Pick = {}) {
this.text += '\n';
const cnf = configApi.getConfig();
try {
@@ -37,20 +35,7 @@ export class Diagram {
this.db = diagram.db;
this.renderer = diagram.renderer;
this.parser = diagram.parser;
- const originalParse = this.parser.parse.bind(this.parser);
- // Wrap the jison parse() method to handle extracting frontmatter.
- //
- // This can't be done in this.parse() because some code
- // directly calls diagram.parser.parse(), bypassing this.parse().
- //
- // Similarly, we can't do this in getDiagramFromText() because some code
- // calls diagram.db.clear(), which would reset anything set by
- // extractFrontMatter().
-
- this.parser.parse = (text: string) =>
- originalParse(cleanupComments(extractFrontMatter(text, this.db, configApi.addDirective)));
-
- if (this.parser.parser !== undefined) {
+ if (this.parser.parser) {
// The parser.parser.yy is only present in JISON parsers. So, we'll only set if required.
this.parser.parser.yy = this.db;
}
@@ -63,7 +48,12 @@ export class Diagram {
throw this.detectError;
}
this.db.clear?.();
- this.init?.(configApi.getConfig());
+ const config = configApi.getConfig();
+ this.init?.(config);
+ // This block was added for legacy compatibility. Use frontmatter instead of adding more special cases.
+ if (this.metadata.title) {
+ this.db.setDiagramTitle?.(this.metadata.title);
+ }
this.parser.parse(this.text);
}
@@ -85,11 +75,15 @@ export class Diagram {
* **Warning:** This function may be changed in the future.
* @alpha
* @param text - The mermaid diagram definition.
+ * @param metadata - Diagram metadata, defined in YAML.
* @returns A the Promise of a Diagram object.
* @throws {@link UnknownDiagramError} if the diagram type can not be found.
* @privateRemarks This is exported as part of the public mermaidAPI.
*/
-export const getDiagramFromText = async (text: string): Promise => {
+export const getDiagramFromText = async (
+ text: string,
+ metadata: Pick = {}
+): Promise => {
const type = detectType(text, configApi.getConfig());
try {
// Trying to find the diagram
@@ -104,5 +98,5 @@ export const getDiagramFromText = async (text: string): Promise => {
const { id, diagram } = await loader();
registerDiagram(id, diagram);
}
- return new Diagram(text);
+ return new Diagram(text, metadata);
};
diff --git a/packages/mermaid/src/__mocks__/mermaidAPI.ts b/packages/mermaid/src/__mocks__/mermaidAPI.ts
index a2d19ef24..de4cb61df 100644
--- a/packages/mermaid/src/__mocks__/mermaidAPI.ts
+++ b/packages/mermaid/src/__mocks__/mermaidAPI.ts
@@ -13,7 +13,6 @@ export const mermaidAPI = {
svg: '',
}),
parse: mAPI.parse,
- parseDirective: vi.fn(),
initialize: vi.fn(),
getConfig: configApi.getConfig,
setConfig: configApi.setConfig,
diff --git a/packages/mermaid/src/config.ts b/packages/mermaid/src/config.ts
index eb24b6268..ede3a568d 100644
--- a/packages/mermaid/src/config.ts
+++ b/packages/mermaid/src/config.ts
@@ -3,7 +3,7 @@ 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';
+import { sanitizeDirective } from './utils/sanitizeDirective.js';
export const defaultConfig: MermaidConfig = Object.freeze(config);
diff --git a/packages/mermaid/src/diagram-api/comments.ts b/packages/mermaid/src/diagram-api/comments.ts
index be39b0a0f..8141deee0 100644
--- a/packages/mermaid/src/diagram-api/comments.ts
+++ b/packages/mermaid/src/diagram-api/comments.ts
@@ -4,5 +4,5 @@
* @returns cleaned text
*/
export const cleanupComments = (text: string): string => {
- return text.trimStart().replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
+ return text.replace(/^\s*%%(?!{)[^\n]+\n?/gm, '').trimStart();
};
diff --git a/packages/mermaid/src/diagram-api/diagramAPI.ts b/packages/mermaid/src/diagram-api/diagramAPI.ts
index 6f1421527..ea3c10159 100644
--- a/packages/mermaid/src/diagram-api/diagramAPI.ts
+++ b/packages/mermaid/src/diagram-api/diagramAPI.ts
@@ -6,7 +6,6 @@ import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox.js
import { addStylesForDiagram } from '../styles.js';
import type { DiagramDefinition, DiagramDetector } from './types.js';
import * as _commonDb from '../diagrams/common/commonDb.js';
-import { parseDirective as _parseDirective } from '../directiveUtils.js';
/*
Packaging and exposing resources for external diagrams so that they can import
@@ -21,8 +20,6 @@ export const setupGraphViewbox = _setupGraphViewbox;
export const getCommonDb = () => {
return _commonDb;
};
-export const parseDirective = (p: any, statement: string, context: string, type: string) =>
- _parseDirective(p, statement, context, type);
const diagrams: Record = {};
export interface Detectors {
@@ -52,17 +49,18 @@ export const registerDiagram = (
}
addStylesForDiagram(id, diagram.styles);
- if (diagram.injectUtils) {
- diagram.injectUtils(
- log,
- setLogLevel,
- getConfig,
- sanitizeText,
- setupGraphViewbox,
- getCommonDb(),
- parseDirective
- );
- }
+ diagram.injectUtils?.(
+ log,
+ setLogLevel,
+ getConfig,
+ sanitizeText,
+ setupGraphViewbox,
+ getCommonDb(),
+ () => {
+ // parseDirective is removed in https://github.com/mermaid-js/mermaid/pull/4759.
+ // This is a no-op for legacy support.
+ }
+ );
};
export const getDiagram = (name: string): DiagramDefinition => {
diff --git a/packages/mermaid/src/diagram-api/frontmatter.spec.ts b/packages/mermaid/src/diagram-api/frontmatter.spec.ts
index 03d46c300..90ef97cb6 100644
--- a/packages/mermaid/src/diagram-api/frontmatter.spec.ts
+++ b/packages/mermaid/src/diagram-api/frontmatter.spec.ts
@@ -1,84 +1,139 @@
-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');
+ expect(extractFrontMatter('diagram')).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "diagram",
+ }
+ `);
});
it('returns text unchanged if frontmatter lacks closing delimiter', () => {
const text = `---\ntitle: foo\ndiagram`;
- expect(extractFrontMatter(text, dbMock())).toEqual(text);
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "---
+ title: foo
+ diagram",
+ }
+ `);
});
it('handles empty frontmatter', () => {
- const db = dbMock();
const text = `---\n\n---\ndiagram`;
- expect(extractFrontMatter(text, db)).toEqual('diagram');
- expect(db.setDiagramTitle).not.toHaveBeenCalled();
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "diagram",
+ }
+ `);
});
it('handles frontmatter without mappings', () => {
- const db = dbMock();
- const text = `---\n1\n---\ndiagram`;
- expect(extractFrontMatter(text, db)).toEqual('diagram');
- expect(db.setDiagramTitle).not.toHaveBeenCalled();
+ expect(extractFrontMatter(`---\n1\n---\ndiagram`)).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "diagram",
+ }
+ `);
+ expect(extractFrontMatter(`---\n-1\n-2\n---\ndiagram`)).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "diagram",
+ }
+ `);
+ expect(extractFrontMatter(`---\nnull\n---\ndiagram`)).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "diagram",
+ }
+ `);
});
it('does not try to parse frontmatter at the end', () => {
- const db = dbMock();
const text = `diagram\n---\ntitle: foo\n---\n`;
- expect(extractFrontMatter(text, db)).toEqual(text);
- expect(db.setDiagramTitle).not.toHaveBeenCalled();
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {},
+ "text": "diagram
+ ---
+ title: foo
+ ---
+ ",
+ }
+ `);
});
it('handles frontmatter with multiple delimiters', () => {
- const db = dbMock();
const text = `---\ntitle: foo---bar\n---\ndiagram\n---\ntest`;
- expect(extractFrontMatter(text, db)).toEqual('diagram\n---\ntest');
- expect(db.setDiagramTitle).toHaveBeenCalledWith('foo---bar');
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {
+ "title": "foo---bar",
+ },
+ "text": "diagram
+ ---
+ test",
+ }
+ `);
});
it('handles frontmatter with multi-line string and multiple delimiters', () => {
- const db = dbMock();
const text = `---\ntitle: |\n multi-line string\n ---\n---\ndiagram`;
- expect(extractFrontMatter(text, db)).toEqual('diagram');
- expect(db.setDiagramTitle).toHaveBeenCalledWith('multi-line string\n---\n');
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {
+ "title": "multi-line string
+ ---
+ ",
+ },
+ "text": "diagram",
+ }
+ `);
});
it('handles frontmatter with title', () => {
- const db = dbMock();
const text = `---\ntitle: foo\n---\ndiagram`;
- expect(extractFrontMatter(text, db)).toEqual('diagram');
- expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {
+ "title": "foo",
+ },
+ "text": "diagram",
+ }
+ `);
});
it('handles booleans in frontmatter properly', () => {
- const db = dbMock();
const text = `---\ntitle: true\n---\ndiagram`;
- expect(extractFrontMatter(text, db)).toEqual('diagram');
- expect(db.setDiagramTitle).toHaveBeenCalledWith('true');
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {
+ "title": "true",
+ },
+ "text": "diagram",
+ }
+ `);
});
it('ignores unspecified frontmatter keys', () => {
- const db = dbMock();
const text = `---\ninvalid: true\ntitle: foo\ntest: bar\n---\ndiagram`;
- expect(extractFrontMatter(text, db)).toEqual('diagram');
- expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {
+ "title": "foo",
+ },
+ "text": "diagram",
+ }
+ `);
});
it('throws exception for invalid YAML syntax', () => {
const text = `---\n!!!\n---\ndiagram`;
- expect(() => extractFrontMatter(text, dbMock())).toThrow(
- 'tag suffix cannot contain exclamation marks'
- );
+ expect(() => extractFrontMatter(text)).toThrow('tag suffix cannot contain exclamation marks');
});
it('handles frontmatter with config', () => {
@@ -92,9 +147,25 @@ config:
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] },
- });
+ expect(extractFrontMatter(text)).toMatchInlineSnapshot(`
+ {
+ "metadata": {
+ "config": {
+ "graph": {
+ "array": [
+ 1,
+ 2,
+ 3,
+ ],
+ "boolean": false,
+ "number": 14,
+ "string": "hello",
+ },
+ },
+ "title": "hello",
+ },
+ "text": "diagram",
+ }
+ `);
});
});
diff --git a/packages/mermaid/src/diagram-api/frontmatter.ts b/packages/mermaid/src/diagram-api/frontmatter.ts
index 0fd2917ea..c95e05f2c 100644
--- a/packages/mermaid/src/diagram-api/frontmatter.ts
+++ b/packages/mermaid/src/diagram-api/frontmatter.ts
@@ -1,6 +1,5 @@
import type { MermaidConfig } from '../config.type.js';
import { frontMatterRegex } from './regexes.js';
-import type { DiagramDB } from './types.js';
// The "* as yaml" part is necessary for tree-shaking
import * as yaml from 'js-yaml';
@@ -11,43 +10,51 @@ interface FrontMatterMetadata {
config?: MermaidConfig;
}
+export interface FrontMatterResult {
+ text: string;
+ metadata: FrontMatterMetadata;
+}
+
/**
* 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,
- setDiagramConfig?: (config: MermaidConfig) => void
-): string {
+export function extractFrontMatter(text: string): FrontMatterResult {
const matches = text.match(frontMatterRegex);
if (!matches) {
- return text;
+ return {
+ text,
+ metadata: {},
+ };
}
- 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;
+ let 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,
+ }) ?? {};
- if (parsed?.title) {
- // toString() is necessary because YAML could parse the title as a number/boolean
- db.setDiagramTitle?.(parsed.title.toString());
+ // To handle runtime data type changes
+ parsed = typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
+
+ const metadata: FrontMatterMetadata = {};
+
+ // Only add properties that are explicitly supported, if they exist
+ if (parsed.displayMode) {
+ metadata.displayMode = parsed.displayMode.toString();
+ }
+ if (parsed.title) {
+ metadata.title = parsed.title.toString();
+ }
+ if (parsed.config) {
+ metadata.config = parsed.config;
}
- 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);
+ return {
+ text: text.slice(matches[0].length),
+ metadata,
+ };
}
diff --git a/packages/mermaid/src/diagram-api/types.ts b/packages/mermaid/src/diagram-api/types.ts
index 66b0f7323..7abcdf7d7 100644
--- a/packages/mermaid/src/diagram-api/types.ts
+++ b/packages/mermaid/src/diagram-api/types.ts
@@ -1,7 +1,13 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Diagram } from '../Diagram.js';
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js';
import type * as d3 from 'd3';
+export interface DiagramMetadata {
+ title?: string;
+ config?: MermaidConfig;
+}
+
export interface InjectUtils {
_log: any;
_setLogLevel: any;
@@ -9,6 +15,7 @@ export interface InjectUtils {
_sanitizeText: any;
_setupGraphViewbox: any;
_commonDb: any;
+ /** @deprecated as directives will be pre-processed since https://github.com/mermaid-js/mermaid/pull/4759 */
_parseDirective: any;
}
@@ -45,6 +52,7 @@ export interface DiagramDefinition {
_sanitizeText: InjectUtils['_sanitizeText'],
_setupGraphViewbox: InjectUtils['_setupGraphViewbox'],
_commonDb: InjectUtils['_commonDb'],
+ /** @deprecated as directives will be pre-processed since https://github.com/mermaid-js/mermaid/pull/4759 */
_parseDirective: InjectUtils['_parseDirective']
) => void;
}
@@ -83,15 +91,6 @@ export interface ParserDefinition {
parser?: { yy: DiagramDB };
}
-/**
- * Type for function parse directive from diagram code.
- *
- * @param statement -
- * @param context -
- * @param type -
- */
-export type ParseDirectiveDefinition = (statement: string, context: string, type: string) => void;
-
export type HTML = d3.Selection;
export type SVG = d3.Selection;
diff --git a/packages/mermaid/src/diagrams/c4/c4Db.js b/packages/mermaid/src/diagrams/c4/c4Db.js
index 7c450940f..71c178585 100644
--- a/packages/mermaid/src/diagrams/c4/c4Db.js
+++ b/packages/mermaid/src/diagrams/c4/c4Db.js
@@ -1,4 +1,3 @@
-import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import { sanitizeText } from '../common/common.js';
import {
@@ -38,10 +37,6 @@ export const setC4Type = function (c4TypeParam) {
c4Type = sanitizedText;
};
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
//type, from, to, label, ?techn, ?descr, ?sprite, ?tags, $link
export const addRel = function (type, from, to, label, techn, descr, sprite, tags, link) {
// Don't allow label nulling
@@ -821,7 +816,6 @@ export default {
getAccTitle,
getAccDescription,
setAccDescription,
- parseDirective,
getConfig: () => configApi.getConfig().c4,
clear,
LINETYPE,
diff --git a/packages/mermaid/src/diagrams/c4/c4Diagram.ts b/packages/mermaid/src/diagrams/c4/c4Diagram.ts
index 4c578b624..9557a0f70 100644
--- a/packages/mermaid/src/diagrams/c4/c4Diagram.ts
+++ b/packages/mermaid/src/diagrams/c4/c4Diagram.ts
@@ -1,17 +1,18 @@
// @ts-ignore: JISON doesn't support types
-import c4Parser from './parser/c4Diagram.jison';
-import c4Db from './c4Db.js';
-import c4Renderer from './c4Renderer.js';
-import c4Styles from './styles.js';
+import parser from './parser/c4Diagram.jison';
+import db from './c4Db.js';
+import renderer from './c4Renderer.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 = {
- parser: c4Parser,
- db: c4Db,
- renderer: c4Renderer,
- styles: c4Styles,
- init: (cnf: MermaidConfig) => {
- c4Renderer.setConf(cnf.c4);
+ parser,
+ db,
+ renderer,
+ styles,
+ init: ({ c4, wrap }: MermaidConfig) => {
+ renderer.setConf(c4);
+ db.setWrap(wrap);
},
};
diff --git a/packages/mermaid/src/diagrams/c4/parser/c4Diagram.jison b/packages/mermaid/src/diagrams/c4/parser/c4Diagram.jison
index 1dfa69ef1..a6bd8a6ec 100644
--- a/packages/mermaid/src/diagrams/c4/parser/c4Diagram.jison
+++ b/packages/mermaid/src/diagrams/c4/parser/c4Diagram.jison
@@ -72,25 +72,16 @@
%x string_kv_key
%x string_kv_value
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
.*direction\s+TB[^\n]* return 'direction_tb';
.*direction\s+BT[^\n]* return 'direction_bt';
.*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr';
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
"title"\s[^#\n;]+ return 'title';
@@ -207,7 +198,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
start
: mermaidDoc
| direction
- | directive start
;
direction
@@ -225,26 +215,6 @@ mermaidDoc
: graphConfig
;
-directive
- : openDirective typeDirective closeDirective NEWLINE
- | openDirective typeDirective ':' argDirective closeDirective NEWLINE
- ;
-
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'c4Context'); }
- ;
graphConfig
: C4_CONTEXT NEWLINE statements EOF {yy.setC4Type($1)}
diff --git a/packages/mermaid/src/diagrams/class/classDb.ts b/packages/mermaid/src/diagrams/class/classDb.ts
index c9a202aa4..b2485267a 100644
--- a/packages/mermaid/src/diagrams/class/classDb.ts
+++ b/packages/mermaid/src/diagrams/class/classDb.ts
@@ -4,7 +4,6 @@ import { log } from '../../logger.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
import utils from '../../utils.js';
-import mermaidAPI from '../../mermaidAPI.js';
import {
setAccTitle,
getAccTitle,
@@ -37,11 +36,6 @@ let functions: any[] = [];
const sanitizeText = (txt: string) => common.sanitizeText(txt, configApi.getConfig());
-export const parseDirective = function (statement: string, context: string, type: string) {
- // @ts-ignore Don't wanna mess it up
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
const splitClassNameAndType = function (id: string) {
let genericType = '';
let className = id;
@@ -460,7 +454,6 @@ export const addClassesToNamespace = function (id: string, classNames: string[])
};
export default {
- parseDirective,
setAccTitle,
getAccTitle,
getAccDescription,
diff --git a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison
index 9c67e306e..b1c2fad0e 100644
--- a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison
+++ b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison
@@ -13,9 +13,6 @@
%x href
%x callback_name
%x callback_args
-%x open_directive
-%x type_directive
-%x arg_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -24,15 +21,10 @@
%x namespace
%x namespace-body
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
.*direction\s+TB[^\n]* return 'direction_tb';
.*direction\s+BT[^\n]* return 'direction_bt';
.*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr';
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)*[^\n]*(\r?\n?)+ /* skip comments */
\%\%[^\n]*(\r?\n)* /* skip comments */
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
@@ -220,7 +212,6 @@ line was introduced with 'click'.
start
: mermaidDoc
- | directive start
| statements
;
@@ -232,27 +223,6 @@ graphConfig
: CLASS_DIAGRAM NEWLINE statements EOF
;
-directive
- : openDirective typeDirective closeDirective NEWLINE
- | openDirective typeDirective ':' argDirective closeDirective NEWLINE
- ;
-
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'class'); }
- ;
-
statements
: statement
| statement NEWLINE
diff --git a/packages/mermaid/src/diagrams/er/erDb.js b/packages/mermaid/src/diagrams/er/erDb.js
index 01bbb585c..9a397597e 100644
--- a/packages/mermaid/src/diagrams/er/erDb.js
+++ b/packages/mermaid/src/diagrams/er/erDb.js
@@ -1,5 +1,4 @@
import { log } from '../../logger.js';
-import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import {
@@ -28,10 +27,6 @@ const Identification = {
IDENTIFYING: 'IDENTIFYING',
};
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
const addEntity = function (name, alias = undefined) {
if (entities[name] === undefined) {
entities[name] = { attributes: [], alias: alias };
@@ -88,7 +83,6 @@ const clear = function () {
export default {
Cardinality,
Identification,
- parseDirective,
getConfig: () => configApi.getConfig().er,
addEntity,
addAttributes,
diff --git a/packages/mermaid/src/diagrams/er/parser/erDiagram.jison b/packages/mermaid/src/diagrams/er/parser/erDiagram.jison
index f39134607..135efc784 100644
--- a/packages/mermaid/src/diagrams/er/parser/erDiagram.jison
+++ b/packages/mermaid/src/diagrams/er/parser/erDiagram.jison
@@ -1,7 +1,7 @@
%lex
%options case-insensitive
-%x open_directive type_directive arg_directive block
+%x block
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -14,11 +14,6 @@ accDescr\s*":"\s* { this.begin("ac
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
[\}] { this.popState(); }
[^\}]* return "acc_descr_multiline_value";
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
[\n]+ return 'NEWLINE';
\s+ /* skip whitespace */
[\s]+ return 'SPACE';
@@ -77,7 +72,6 @@ o\{ return 'ZERO_OR_MORE';
start
: 'ER_DIAGRAM' document 'EOF' { /*console.log('finished parsing');*/ }
- | directive start
;
document
@@ -92,14 +86,9 @@ line
| EOF { $$=[];}
;
-directive
- : openDirective typeDirective closeDirective 'NEWLINE'
- | openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
- ;
statement
- : directive
- | entityName relSpec entityName ':' role
+ : entityName relSpec entityName ':' role
{
yy.addEntity($1);
yy.addEntity($3);
@@ -191,20 +180,4 @@ role
| 'ALPHANUM' { $$ = $1; }
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'er'); }
- ;
-
%%
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js
index 2bb50abee..a87bf558d 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDb.js
+++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js
@@ -2,7 +2,6 @@ import { select } from 'd3';
import utils from '../../utils.js';
import * as configApi from '../../config.js';
import common from '../common/common.js';
-import mermaidAPI from '../../mermaidAPI.js';
import { log } from '../../logger.js';
import {
setAccTitle,
@@ -34,10 +33,6 @@ let funs = [];
const sanitizeText = (txt) => common.sanitizeText(txt, config);
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
/**
* Function to lookup domId from id in the graph definition.
*
@@ -771,7 +766,6 @@ export const lex = {
firstGraph,
};
export default {
- parseDirective,
defaultConfig: () => configApi.defaultConfig.flowchart,
setAccTitle,
getAccTitle,
diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
index 8d746f808..6dad36d25 100644
--- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
+++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison
@@ -23,17 +23,8 @@
%x href
%x callbackname
%x callbackargs
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
@@ -272,35 +263,10 @@ that id.
%% /* language grammar */
start
- : mermaidDoc
- | directive start
- ;
-
-directive
- : openDirective typeDirective closeDirective separator
- | openDirective typeDirective ':' argDirective closeDirective separator
- ;
-
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($type_directive, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $arg_directive = $arg_directive.trim().replace(/'/g, '"'); yy.parseDirective($arg_directive, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'flowchart'); }
- ;
-
-mermaidDoc
: graphConfig document
;
+
document
: /* empty */
{ $$ = [];}
diff --git a/packages/mermaid/src/diagrams/gantt/ganttDb.js b/packages/mermaid/src/diagrams/gantt/ganttDb.js
index 3ff7955f8..775494e3d 100644
--- a/packages/mermaid/src/diagrams/gantt/ganttDb.js
+++ b/packages/mermaid/src/diagrams/gantt/ganttDb.js
@@ -6,7 +6,6 @@ import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat.js';
import { log } from '../../logger.js';
import * as configApi from '../../config.js';
import utils from '../../utils.js';
-import mermaidAPI from '../../mermaidAPI.js';
import {
setAccTitle,
@@ -42,10 +41,6 @@ let weekday = 'sunday';
// The serial order of the task in the script
let lastOrder = 0;
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
export const clear = function () {
sections = [];
tasks = [];
@@ -730,7 +725,6 @@ export const bindFunctions = function (element) {
};
export default {
- parseDirective,
getConfig: () => configApi.getConfig().gantt,
clear,
setDateFormat,
diff --git a/packages/mermaid/src/diagrams/gantt/parser/gantt.jison b/packages/mermaid/src/diagrams/gantt/parser/gantt.jison
index f7fd40c1b..b4daab5dc 100644
--- a/packages/mermaid/src/diagrams/gantt/parser/gantt.jison
+++ b/packages/mermaid/src/diagrams/gantt/parser/gantt.jison
@@ -11,19 +11,11 @@
%x href
%x callbackname
%x callbackargs
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
@@ -112,8 +104,7 @@ weekday\s+sunday return 'weekday_sunday'
%% /* language grammar */
start
- : directive start
- | gantt document 'EOF' { return $2; }
+ : gantt document 'EOF' { return $2; }
;
document
@@ -155,13 +146,8 @@ statement
| section { yy.addSection($1.substr(8));$$=$1.substr(8); }
| clickStatement
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
- | directive
;
-directive
- : openDirective typeDirective closeDirective 'NL'
- | openDirective typeDirective ':' argDirective closeDirective 'NL'
- ;
/*
click allows any combination of href and call.
@@ -192,20 +178,4 @@ clickStatementDebug
| click href {$$=$1 + ' ' + $2;}
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'gantt'); }
- ;
-
%%
diff --git a/packages/mermaid/src/diagrams/git/gitGraphAst.js b/packages/mermaid/src/diagrams/git/gitGraphAst.js
index dc9c32da9..abad68b22 100644
--- a/packages/mermaid/src/diagrams/git/gitGraphAst.js
+++ b/packages/mermaid/src/diagrams/git/gitGraphAst.js
@@ -1,6 +1,5 @@
import { log } from '../../logger.js';
import { random } from '../../utils.js';
-import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import { getConfig } from '../../config.js';
import common from '../common/common.js';
@@ -33,10 +32,6 @@ function getId() {
return random({ length: 7 });
}
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
// /**
// * @param currentCommit
// * @param otherCommit
@@ -507,7 +502,6 @@ export const commitType = {
};
export default {
- parseDirective,
getConfig: () => configApi.getConfig().gitGraph,
setDirection,
setOptions,
diff --git a/packages/mermaid/src/diagrams/git/gitGraphParser.spec b/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js
similarity index 62%
rename from packages/mermaid/src/diagrams/git/gitGraphParser.spec
rename to packages/mermaid/src/diagrams/git/gitGraphParser.spec.js
index 0a8256c5b..446f06739 100644
--- a/packages/mermaid/src/diagrams/git/gitGraphParser.spec
+++ b/packages/mermaid/src/diagrams/git/gitGraphParser.spec.js
@@ -1,87 +1,72 @@
+import gitGraphAst from './gitGraphAst.js';
+import { parser } from './parser/gitGraph.jison';
-// Todo reintroduce without cryptoRandomString
-import gitGraphAst from './gitGraphAst';
-import { parser } from './parser/gitGraph';
-import randomString from 'crypto-random-string';
-import cryptoRandomString from 'crypto-random-string';
-
-jest.mock('crypto-random-string');
-
-describe('when parsing a gitGraph', function() {
- let randomNumber;
- beforeEach(function() {
+describe('when parsing a gitGraph', function () {
+ beforeEach(function () {
parser.yy = gitGraphAst;
parser.yy.clear();
- randomNumber = 0;
- cryptoRandomString.mockImplementation(() => {
- randomNumber = randomNumber + 1;
- return String(randomNumber);
- });
});
- afterEach(function() {
- cryptoRandomString.mockReset();
- });
- it('should handle a gitGraph definition', function() {
+ it('should handle a gitGraph definition', function () {
const str = 'gitGraph:\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
- expect(parser.yy.getCurrentBranch()).toBe('master');
+ expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
- it('should handle a gitGraph definition with empty options', function() {
- const str = 'gitGraph:\n' + 'options\n' + 'end\n' + 'commit\n';
+ it('should handle a gitGraph definition with empty options', function () {
+ const str = 'gitGraph:\n' + 'options\n' + ' end\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(parser.yy.getOptions()).toEqual({});
expect(Object.keys(commits).length).toBe(1);
- expect(parser.yy.getCurrentBranch()).toBe('master');
+ expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
- it('should handle a gitGraph definition with valid options', function() {
+ it('should handle a gitGraph definition with valid options', function () {
const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"}\n' + 'end\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(parser.yy.getOptions()['key']).toBe('value');
expect(Object.keys(commits).length).toBe(1);
- expect(parser.yy.getCurrentBranch()).toBe('master');
+ expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
- it('should not fail on a gitGraph with malformed json', function() {
+ it('should not fail on a gitGraph with malformed json', function () {
const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"\n' + 'end\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
- expect(parser.yy.getCurrentBranch()).toBe('master');
+ expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('LR');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
- it('should handle set direction', function() {
- const str = 'gitGraph BT:\n' + 'commit\n';
+ it('should handle set direction', function () {
+ const str = 'gitGraph TB:\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
- expect(parser.yy.getCurrentBranch()).toBe('master');
- expect(parser.yy.getDirection()).toBe('BT');
+ expect(parser.yy.getCurrentBranch()).toBe('main');
+ expect(parser.yy.getDirection()).toBe('TB');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
- it('should checkout a branch', function() {
+ it('should checkout a branch', function () {
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n';
parser.parse(str);
@@ -91,7 +76,7 @@ describe('when parsing a gitGraph', function() {
expect(parser.yy.getCurrentBranch()).toBe('new');
});
- it('should add commits to checked out branch', function() {
+ it('should add commits to checked out branch', function () {
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n';
parser.parse(str);
@@ -103,7 +88,7 @@ describe('when parsing a gitGraph', function() {
expect(branchCommit).not.toBeNull();
expect(commits[branchCommit].parent).not.toBeNull();
});
- it('should handle commit with args', function() {
+ it('should handle commit with args', function () {
const str = 'gitGraph:\n' + 'commit "a commit"\n';
parser.parse(str);
@@ -112,10 +97,11 @@ describe('when parsing a gitGraph', function() {
expect(Object.keys(commits).length).toBe(1);
const key = Object.keys(commits)[0];
expect(commits[key].message).toBe('a commit');
- expect(parser.yy.getCurrentBranch()).toBe('master');
+ expect(parser.yy.getCurrentBranch()).toBe('main');
});
- it('should reset a branch', function() {
+ // Reset has been commented out in JISON
+ it.skip('should reset a branch', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -123,18 +109,18 @@ describe('when parsing a gitGraph', function() {
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
- 'reset master\n';
+ 'reset main\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(3);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
- expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
+ expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
});
- it('reset can take an argument', function() {
+ it.skip('reset can take an argument', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -142,18 +128,18 @@ describe('when parsing a gitGraph', function() {
'branch newbranch\n' +
'checkout newbranch\n' +
'commit\n' +
- 'reset master^\n';
+ 'reset main^\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(3);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
- const master = commits[parser.yy.getBranches()['master']];
- expect(parser.yy.getHead().id).toEqual(master.parent);
+ const main = commits[parser.yy.getBranches()['main']];
+ expect(parser.yy.getHead().id).toEqual(main.parent);
});
- it('should handle fast forwardable merges', function() {
+ it.skip('should handle fast forwardable merges', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -161,19 +147,19 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
- 'checkout master\n' +
+ 'checkout main\n' +
'merge newbranch\n';
parser.parse(str);
const commits = parser.yy.getCommits();
- expect(Object.keys(commits).length).toBe(3);
- expect(parser.yy.getCurrentBranch()).toBe('master');
- expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
+ expect(Object.keys(commits).length).toBe(4);
+ expect(parser.yy.getCurrentBranch()).toBe('main');
+ expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
});
- it('should handle cases when merge is a noop', function() {
+ it('should handle cases when merge is a noop', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -181,18 +167,18 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
- 'merge master\n';
+ 'merge main\n';
parser.parse(str);
const commits = parser.yy.getCommits();
- expect(Object.keys(commits).length).toBe(3);
+ expect(Object.keys(commits).length).toBe(4);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
- expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
+ expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['main']);
expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
});
- it('should handle merge with 2 parents', function() {
+ it('should handle merge with 2 parents', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -200,7 +186,7 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
- 'checkout master\n' +
+ 'checkout main\n' +
'commit\n' +
'merge newbranch\n';
@@ -208,12 +194,12 @@ describe('when parsing a gitGraph', function() {
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(5);
- expect(parser.yy.getCurrentBranch()).toBe('master');
- expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
- expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
+ expect(parser.yy.getCurrentBranch()).toBe('main');
+ expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['main']);
+ expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['main']);
});
- it('should handle ff merge when history walk has two parents (merge commit)', function() {
+ it.skip('should handle ff merge when history walk has two parents (merge commit)', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -221,53 +207,25 @@ describe('when parsing a gitGraph', function() {
'checkout newbranch\n' +
'commit\n' +
'commit\n' +
- 'checkout master\n' +
+ 'checkout main\n' +
'commit\n' +
'merge newbranch\n' +
'commit\n' +
'checkout newbranch\n' +
- 'merge master\n';
+ 'merge main\n';
parser.parse(str);
const commits = parser.yy.getCommits();
- expect(Object.keys(commits).length).toBe(6);
+ expect(Object.keys(commits).length).toBe(7);
expect(parser.yy.getCurrentBranch()).toBe('newbranch');
- expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
- expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
+ expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['main']);
+ expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['main']);
parser.yy.prettyPrint();
});
- it('should generate a secure random ID for commits', function() {
- const str = 'gitGraph:\n' + 'commit\n' + 'commit\n';
- const EXPECTED_LENGTH = 7;
- const EXPECTED_CHARACTERS = '0123456789abcdef';
-
- let idCount = 0;
- randomString.mockImplementation(options => {
- if (
- options.length === EXPECTED_LENGTH &&
- options.characters === EXPECTED_CHARACTERS &&
- Object.keys(options).length === 2
- ) {
- const id = `abcdef${idCount}`;
- idCount += 1;
- return id;
- }
- return 'unexpected-ID';
- });
-
- parser.parse(str);
- const commits = parser.yy.getCommits();
-
- expect(Object.keys(commits)).toEqual(['abcdef0', 'abcdef1']);
- Object.keys(commits).forEach(key => {
- expect(commits[key].id).toEqual(key);
- });
- });
-
- it('should generate an array of known branches', function() {
+ it('should generate an array of known branches', function () {
const str =
'gitGraph:\n' +
'commit\n' +
@@ -281,7 +239,7 @@ describe('when parsing a gitGraph', function() {
const branches = gitGraphAst.getBranchesAsObjArray();
expect(branches).toHaveLength(3);
- expect(branches[0]).toHaveProperty('name', 'master');
+ expect(branches[0]).toHaveProperty('name', 'main');
expect(branches[1]).toHaveProperty('name', 'b1');
expect(branches[2]).toHaveProperty('name', 'b2');
});
diff --git a/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js b/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js
index 764fbb214..df20a5eb5 100644
--- a/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js
+++ b/packages/mermaid/src/diagrams/git/gitGraphParserV2.spec.js
@@ -1,22 +1,11 @@
-/* eslint-env jasmine */
-// Todo reintroduce without cryptoRandomString
import gitGraphAst from './gitGraphAst.js';
import { parser } from './parser/gitGraph.jison';
-//import randomString from 'crypto-random-string';
-//import cryptoRandomString from 'crypto-random-string';
-
-//jest.mock('crypto-random-string');
describe('when parsing a gitGraph', function () {
- let randomNumber;
beforeEach(function () {
parser.yy = gitGraphAst;
parser.yy.clear();
- randomNumber = 0;
});
- // afterEach(function() {
- // cryptoRandomString.mockReset();
- // });
it('should handle a gitGraph commit with NO pararms, get auto-genrated reandom ID', function () {
const str = `gitGraph:
commit
diff --git a/packages/mermaid/src/diagrams/git/parser/gitGraph.jison b/packages/mermaid/src/diagrams/git/parser/gitGraph.jison
index 9ff5623f8..2297db17c 100644
--- a/packages/mermaid/src/diagrams/git/parser/gitGraph.jison
+++ b/packages/mermaid/src/diagrams/git/parser/gitGraph.jison
@@ -9,10 +9,6 @@
%x string
%x options
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -20,11 +16,6 @@
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
@@ -76,7 +67,6 @@ checkout(?=\s|$) return 'CHECKOUT';
start
: eol start
- | directive start
| GG document EOF{ return $3; }
| GG ':' document EOF{ return $3; }
| GG DIR ':' document EOF {yy.setDirection($2); return $4;}
@@ -240,27 +230,6 @@ commitType
| HIGHLIGHT { $$=yy.commitType.HIGHLIGHT;}
;
-directive
- : openDirective typeDirective closeDirective
- | openDirective typeDirective ':' argDirective closeDirective
- ;
-
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'gitGraph'); }
- ;
-
ref
: ID
| STR
diff --git a/packages/mermaid/src/diagrams/pie/parser/pie.jison b/packages/mermaid/src/diagrams/pie/parser/pie.jison
index e98638aa8..d1f516e75 100644
--- a/packages/mermaid/src/diagrams/pie/parser/pie.jison
+++ b/packages/mermaid/src/diagrams/pie/parser/pie.jison
@@ -8,19 +8,10 @@
%x string
%x title
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('');*/ }
[\n\r]+ return 'NEWLINE';
@@ -52,7 +43,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
start
: eol start
- | directive start
| PIE document
| PIE showData document {yy.setShowData(true);}
;
@@ -73,34 +63,12 @@ statement
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
- | directive
;
-directive
- : openDirective typeDirective closeDirective
- | openDirective typeDirective ':' argDirective closeDirective
- ;
-
eol
: NEWLINE
| ';'
| EOF
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); }
- ;
-
%%
diff --git a/packages/mermaid/src/diagrams/pie/pie.spec.ts b/packages/mermaid/src/diagrams/pie/pie.spec.ts
index 7c8e0809a..564e12f0f 100644
--- a/packages/mermaid/src/diagrams/pie/pie.spec.ts
+++ b/packages/mermaid/src/diagrams/pie/pie.spec.ts
@@ -62,17 +62,6 @@ describe('pie', () => {
expect(sections['bat']).toBe(40);
});
- it('should handle simple pie with a directive', () => {
- parser.parse(`%%{init: {'logLevel':0}}%%
- pie
- "ash" : 60
- "bat" : 40
- `);
- const sections = db.getSections();
- expect(sections['ash']).toBe(60);
- expect(sections['bat']).toBe(40);
- });
-
it('should handle simple pie with a title', () => {
parser.parse(`pie title a 60/40 pie
"ash" : 60
diff --git a/packages/mermaid/src/diagrams/pie/pieDb.ts b/packages/mermaid/src/diagrams/pie/pieDb.ts
index 7f209de46..ce82216dc 100644
--- a/packages/mermaid/src/diagrams/pie/pieDb.ts
+++ b/packages/mermaid/src/diagrams/pie/pieDb.ts
@@ -1,5 +1,4 @@
import { log } from '../../logger.js';
-import { parseDirective as _parseDirective } from '../../directiveUtils.js';
import { getConfig as commonGetConfig } from '../../config.js';
import { sanitizeText } from '../common/common.js';
import {
@@ -11,7 +10,6 @@ import {
setAccDescription,
clear as commonClear,
} from '../common/commonDb.js';
-import type { ParseDirectiveDefinition } from '../../diagram-api/types.js';
import type { PieFields, PieDB, Sections } from './pieTypes.js';
import type { RequiredDeep } from 'type-fest';
import type { PieDiagramConfig } from '../../config.type.js';
@@ -31,10 +29,6 @@ const config: Required = structuredClone(DEFAULT_PIE_CONFIG);
const getConfig = (): Required => structuredClone(config);
-const parseDirective: ParseDirectiveDefinition = (statement, context, type) => {
- _parseDirective(this, statement, context, type);
-};
-
const clear = (): void => {
sections = structuredClone(DEFAULT_PIE_DB.sections);
showData = DEFAULT_PIE_DB.showData;
@@ -67,7 +61,6 @@ const getShowData = (): boolean => showData;
export const db: PieDB = {
getConfig,
- parseDirective,
clear,
setDiagramTitle,
getDiagramTitle,
diff --git a/packages/mermaid/src/diagrams/pie/pieTypes.ts b/packages/mermaid/src/diagrams/pie/pieTypes.ts
index 67fb1dca2..6ba3ab92e 100644
--- a/packages/mermaid/src/diagrams/pie/pieTypes.ts
+++ b/packages/mermaid/src/diagrams/pie/pieTypes.ts
@@ -1,5 +1,5 @@
import type { PieDiagramConfig } from '../../config.type.js';
-import type { DiagramDB, ParseDirectiveDefinition } from '../../diagram-api/types.js';
+import type { DiagramDB } from '../../diagram-api/types.js';
export interface PieFields {
sections: Sections;
@@ -46,7 +46,6 @@ export interface PieDB extends DiagramDB {
getConfig: () => Required;
// common db
- parseDirective: ParseDirectiveDefinition;
clear: () => void;
setDiagramTitle: (title: string) => void;
getDiagramTitle: () => string;
diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison
index 00c125294..255b30a03 100644
--- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison
+++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison
@@ -5,10 +5,6 @@
%x string
%x md_string
%x title
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
@@ -16,11 +12,6 @@
%x point_x
%x point_y
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n\r]+ return 'NEWLINE';
@@ -87,7 +78,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
start
: eol start
| SPACE start
- | directive start
| QUADRANT document
;
@@ -110,7 +100,6 @@ statement
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
- | directive
;
points
@@ -133,33 +122,12 @@ quadrantDetails
| QUADRANT_4 text {yy.setQuadrant4Text($2)}
;
-directive
- : openDirective typeDirective closeDirective
- | openDirective typeDirective ':' argDirective closeDirective
- ;
-
eol
: NEWLINE
| SEMI
| EOF
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'quadrantChart'); }
- ;
-
text: alphaNumToken
{ $$={text:$1, type: 'text'};}
| text textNoTagsToken
diff --git a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts
index faa9281f0..d10cb2134 100644
--- a/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts
+++ b/packages/mermaid/src/diagrams/quadrant-chart/parser/quadrant.jison.spec.ts
@@ -19,7 +19,6 @@ const mockDB: Record> = {
setYAxisTopText: vi.fn(),
setYAxisBottomText: vi.fn(),
setDiagramTitle: vi.fn(),
- parseDirective: vi.fn(),
addPoint: vi.fn(),
};
@@ -45,23 +44,6 @@ describe('Testing quadrantChart jison file', () => {
expect(parserFnConstructor(str)).not.toThrow();
});
- it('should be able to parse directive', () => {
- const str =
- '%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%% \n quadrantChart';
- expect(parserFnConstructor(str)).not.toThrow();
- expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']);
- expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']);
- expect(mockDB.parseDirective.mock.calls[2]).toEqual([
- '{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }',
- 'arg_directive',
- ]);
- expect(mockDB.parseDirective.mock.calls[3]).toEqual([
- '}%%',
- 'close_directive',
- 'quadrantChart',
- ]);
- });
-
it('should be able to parse xAxis text', () => {
let str = 'quadrantChart\nx-axis urgent --> not urgent';
expect(parserFnConstructor(str)).not.toThrow();
@@ -243,8 +225,7 @@ describe('Testing quadrantChart jison file', () => {
});
it('should be able to parse the whole chart', () => {
- const str = `%%{init: {"quadrantChart": {"chartWidth": 600, "chartHeight": 600} } }%%
- quadrantChart
+ const str = `quadrantChart
title Analytics and Business Intelligence Platforms
x-axis "Completeness of Vision ❤" --> "x-axis-2"
y-axis Ability to Execute --> "y-axis-2"
@@ -258,17 +239,6 @@ describe('Testing quadrantChart jison file', () => {
Incorta: [0.20, 0.30]`;
expect(parserFnConstructor(str)).not.toThrow();
- expect(mockDB.parseDirective.mock.calls[0]).toEqual(['%%{', 'open_directive']);
- expect(mockDB.parseDirective.mock.calls[1]).toEqual(['init', 'type_directive']);
- expect(mockDB.parseDirective.mock.calls[2]).toEqual([
- '{"quadrantChart": {"chartWidth": 600, "chartHeight": 600} }',
- 'arg_directive',
- ]);
- expect(mockDB.parseDirective.mock.calls[3]).toEqual([
- '}%%',
- 'close_directive',
- 'quadrantChart',
- ]);
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
text: 'Completeness of Vision ❤',
type: 'text',
diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts
index 0dc87a2d4..0dad6dfdd 100644
--- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts
+++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts
@@ -1,5 +1,3 @@
-import { log } from '../../logger.js';
-import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import { sanitizeText } from '../common/common.js';
import {
@@ -94,11 +92,6 @@ function getQuadrantData() {
return quadrantBuilder.build();
}
-export const parseDirective = function (statement: string, context: string, type: string) {
- // @ts-ignore: TODO Fix ts errors
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
const clear = function () {
quadrantBuilder.clear();
commonClear();
@@ -117,7 +110,6 @@ export default {
setYAxisBottomText,
addPoint,
getQuadrantData,
- parseDirective,
clear,
setAccTitle,
getAccTitle,
diff --git a/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.jison b/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.jison
index 331310283..6d0f7b122 100644
--- a/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.jison
+++ b/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.jison
@@ -9,19 +9,10 @@
%x string
%x token
%x unqString
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
"title"\s[^#\n;]+ return 'title';
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
@@ -99,23 +90,10 @@ start
| RD NEWLINE diagram EOF;
directive
- : openDirective typeDirective closeDirective
- | openDirective typeDirective ':' argDirective closeDirective
- | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
+ : acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); };
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); };
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); };
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); };
diagram
: /* empty */ { $$ = [] }
diff --git a/packages/mermaid/src/diagrams/requirement/requirementDb.js b/packages/mermaid/src/diagrams/requirement/requirementDb.js
index c56c4ca16..325e95ee2 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDb.js
+++ b/packages/mermaid/src/diagrams/requirement/requirementDb.js
@@ -1,6 +1,5 @@
import * as configApi from '../../config.js';
import { log } from '../../logger.js';
-import mermaidAPI from '../../mermaidAPI.js';
import {
setAccTitle,
@@ -48,10 +47,6 @@ const Relationships = {
TRACES: 'traces',
};
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
const addRequirement = (name, type) => {
if (requirements[name] === undefined) {
requirements[name] = {
@@ -149,7 +144,6 @@ export default {
VerifyType,
Relationships,
- parseDirective,
getConfig: () => configApi.getConfig().req,
addRequirement,
diff --git a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison
index 4e971d989..78b0c9ed9 100644
--- a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison
+++ b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison
@@ -16,22 +16,15 @@
// A special state for grabbing text up to the first comment/newline
%x ID ALIAS LINE
-// Directive states
-%x open_directive type_directive arg_directive
%x acc_title
%x acc_descr
%x acc_descr_multiline
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
[\n]+ return 'NEWLINE';
\s+ /* skip all whitespace */
((?!\n)\s)+ /* skip same-line whitespace */
-\#[^\n]* /* skip comments */
+\#[^\n]* /* skip comments */
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[0-9]+(?=[ \n]+) return 'NUM';
@@ -106,7 +99,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
start
: SPACE start
| NEWLINE start
- | directive start
| SD document { yy.apply($2);return $2; }
;
@@ -133,11 +125,6 @@ box_line
;
-directive
- : openDirective typeDirective closeDirective 'NEWLINE'
- | openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
- ;
-
statement
: participant_statement
| 'create' participant_statement {$2.type='createParticipant'; $$=$2;}
@@ -215,7 +202,6 @@ statement
$3.unshift({type: 'breakStart', breakText:yy.parseMessage($2), signalType: yy.LINETYPE.BREAK_START});
$3.push({type: 'breakEnd', optText:yy.parseMessage($2), signalType: yy.LINETYPE.BREAK_END});
$$=$3;}
- | directive
;
option_sections
@@ -335,20 +321,4 @@ text2
: TXT {$$ = yy.parseMessage($1.trim().substring(1)) }
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'sequence'); }
- ;
-
%%
diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.js b/packages/mermaid/src/diagrams/sequence/sequenceDb.js
index 813d9e127..6c3f1f64d 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceDb.js
+++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.js
@@ -1,4 +1,3 @@
-import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import { log } from '../../logger.js';
import { sanitizeText } from '../common/common.js';
@@ -25,10 +24,6 @@ let currentBox = undefined;
let lastCreated = undefined;
let lastDestroyed = undefined;
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
export const addBox = function (data) {
boxes.push({
name: data.text,
@@ -634,7 +629,6 @@ export default {
getBoxes,
getDiagramTitle,
setDiagramTitle,
- parseDirective,
getConfig: () => configApi.getConfig().sequence,
clear,
parseMessage,
diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
index ed6f07300..77ac7c45c 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
+++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
@@ -1,5 +1,4 @@
import { vi } from 'vitest';
-
import * as configApi from '../../config.js';
import mermaidAPI from '../../mermaidAPI.js';
import { Diagram, getDiagramFromText } from '../../Diagram.js';
@@ -225,6 +224,7 @@ Bob-->Alice: I am good thanks!`;
diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
expect(diagram.db.showSequenceNumbers()).toBe(true);
});
+
it('should handle a sequenceDiagram definition with a title:', async () => {
const str = `
sequenceDiagram
@@ -2034,90 +2034,3 @@ participant Alice`;
});
});
});
-
-describe('when rendering a sequenceDiagram with directives', () => {
- beforeAll(function () {
- let conf = {
- diagramMarginX: 50,
- diagramMarginY: 10,
- actorMargin: 50,
- width: 150,
- height: 65,
- boxMargin: 10,
- messageMargin: 40,
- boxTextMargin: 15,
- noteMargin: 25,
- };
- mermaidAPI.initialize({ sequence: conf });
- });
-
- beforeEach(function () {
- mermaidAPI.reset();
- diagram.renderer.bounds.init();
- });
-
- it('should handle one actor, when theme is dark and logLevel is 1 DX1 (dfg1)', async () => {
- const str = `
-%%{init: { "theme": "dark", "logLevel": 1 } }%%
-sequenceDiagram
-%%{wrap}%%
-participant Alice
-`;
- diagram = new Diagram(str);
- diagram.renderer.bounds.init();
- diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
-
- const { bounds, models } = diagram.renderer.bounds.getBounds();
- const mermaid = mermaidAPI.getConfig();
- expect(mermaid.theme).toBe('dark');
- expect(mermaid.logLevel).toBe(1);
- expect(bounds.startx).toBe(0);
- expect(bounds.startx).toBe(0);
- expect(bounds.starty).toBe(0);
- expect(bounds.stopy).toBe(
- models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin
- );
- });
- it('should handle one actor, when logLevel is 3 (dfg0)', async () => {
- const str = `
-%%{initialize: { "logLevel": 3 }}%%
-sequenceDiagram
-participant Alice
-`;
-
- diagram = new Diagram(str);
- diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
-
- const { bounds, models } = diagram.renderer.bounds.getBounds();
- const mermaid = mermaidAPI.getConfig();
- expect(mermaid.logLevel).toBe(3);
- expect(bounds.startx).toBe(0);
- expect(bounds.startx).toBe(0);
- expect(bounds.starty).toBe(0);
- expect(bounds.stopy).toBe(
- models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin
- );
- });
- it('should hide sequence numbers when autonumber is removed when autonumber is enabled', async () => {
- const str1 = `
-sequenceDiagram
-autonumber
-Alice->Bob:Hello Bob, how are you?
-Note right of Bob: Bob thinks
-Bob-->Alice: I am good thanks!`;
-
- diagram = new Diagram(str1);
- diagram.renderer.draw(str1, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
- expect(diagram.db.showSequenceNumbers()).toBe(true);
-
- const str2 = `
-sequenceDiagram
-Alice->Bob:Hello Bob, how are you?
-Note right of Bob: Bob thinks
-Bob-->Alice: I am good thanks!`;
-
- diagram = new Diagram(str2);
- diagram.renderer.draw(str2, 'tst', '1.2.3', diagram);
- expect(diagram.db.showSequenceNumbers()).toBe(false);
- });
-});
diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts
index 8779b9cc4..f8d71c95e 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts
+++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts
@@ -10,4 +10,7 @@ export const diagram: DiagramDefinition = {
db,
renderer,
styles,
+ init: ({ wrap }) => {
+ db.setWrap(wrap);
+ },
};
diff --git a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison
index dc050b2ff..44235ecd4 100644
--- a/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison
+++ b/packages/mermaid/src/diagrams/state/parser/stateDiagram.jison
@@ -33,10 +33,6 @@
%x FLOATING_NOTE
%x FLOATING_NOTE_ID
%x struct
-%x open_directive
-%x type_directive
-%x arg_directive
-%x close_directive
// A special state for grabbing text up to the first comment/newline
%x LINE
@@ -50,18 +46,13 @@
.*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr';
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%\%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('Crap after close');*/ }
[\n]+ return 'NL';
[\s]+ /* skip all whitespace */
-((?!\n)\s)+ /* skip same-line whitespace */
-\#[^\n]* /* skip comments */
+((?!\n)\s)+ /* skip same-line whitespace */
+\#[^\n]* /* skip comments */
\%%[^\n]* /* skip comments */
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
\d+ return 'WIDTH';
@@ -155,7 +146,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
start
: SPACE start
| NL start
- | directive start
| SD document { /* console.log('--> Root document', $2); */ yy.setRootDoc($2); return $2; }
;
@@ -241,7 +231,6 @@ statement
$$={ stmt: 'state', id: $3.trim(), note:{position: $2.trim(), text: $4.trim()}};
}
| note NOTE_TEXT AS ID
- | directive
| direction
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
@@ -264,10 +253,6 @@ cssClassStatement
}
;
-directive
- : openDirective typeDirective closeDirective
- | openDirective typeDirective ':' argDirective closeDirective
- ;
direction
: direction_tb
{ yy.setDirection('TB');$$={stmt:'dir', value:'TB'};}
@@ -308,20 +293,4 @@ notePosition
| right_of
;
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'state'); }
- ;
-
%%
diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js
index f71290ec3..0253c5bcf 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.js
@@ -1,6 +1,5 @@
import { log } from '../../logger.js';
import { generateId } from '../../utils.js';
-import mermaidAPI from '../../mermaidAPI.js';
import common from '../common/common.js';
import * as configApi from '../../config.js';
import {
@@ -77,10 +76,6 @@ export const relationType = {
const clone = (o) => JSON.parse(JSON.stringify(o));
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
const setRootDoc = (o) => {
log.info('Setting root doc', o);
// rootDoc = { id: 'root', doc: o };
@@ -547,7 +542,6 @@ const setDirection = (dir) => {
const trimColon = (str) => (str && str[0] === ':' ? str.substr(1).trim() : str.trim());
export default {
- parseDirective,
getConfig: () => configApi.getConfig().state,
addState,
clear,
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js b/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
index e64ecfdf8..c387ff7b3 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
+++ b/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
@@ -55,16 +55,6 @@ describe('state diagram V2, ', function () {
const title = stateDb.getAccTitle();
expect(title).toBe('a simple title of the diagram');
});
- it('simple with directive', function () {
- const str = `%%{init: {'logLevel': 0 }}%%
- stateDiagram-v2\n
- State1 : this is another string
- [*] --> State1
- State1 --> [*]
- `;
-
- parser.parse(str);
- });
it('should handle relation definitions', function () {
const str = `stateDiagram-v2\n
[*] --> State1
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram.spec.js b/packages/mermaid/src/diagrams/state/stateDiagram.spec.js
index e6e470140..536031c81 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram.spec.js
+++ b/packages/mermaid/src/diagrams/state/stateDiagram.spec.js
@@ -66,16 +66,6 @@ describe('state diagram, ', function () {
const title = stateDb.getAccTitle();
expect(title).toBe('a simple title of the diagram');
});
- it('simple with directive', function () {
- const str = `%%{init: {'logLevel': 0 }}%%
- stateDiagram\n
- State1 : this is another string
- [*] --> State1
- State1 --> [*]
- `;
-
- parser.parse(str);
- });
it('should handle relation definitions', function () {
const str = `stateDiagram\n
[*] --> State1
diff --git a/packages/mermaid/src/diagrams/timeline/parser/timeline.jison b/packages/mermaid/src/diagrams/timeline/parser/timeline.jison
index 59b96516a..348c31fad 100644
--- a/packages/mermaid/src/diagrams/timeline/parser/timeline.jison
+++ b/packages/mermaid/src/diagrams/timeline/parser/timeline.jison
@@ -9,17 +9,8 @@
%x acc_descr
%x acc_descr_multiline
-// Directive states
-%x open_directive type_directive arg_directive
-
-
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n]+ return 'NEWLINE';
@@ -55,7 +46,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
start
: timeline document 'EOF' { return $2; }
- | directive start
;
document
@@ -70,11 +60,6 @@ line
| EOF { $$=[];}
;
-directive
- : openDirective typeDirective closeDirective 'NEWLINE'
- | openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
- ;
-
statement
: title {yy.getCommonDb().setDiagramTitle($1.substr(6));$$=$1.substr(6);}
| acc_title acc_title_value { $$=$2.trim();yy.getCommonDb().setAccTitle($$); }
@@ -83,7 +68,6 @@ statement
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
| period_statement
| event_statement
- | directive
;
period_statement
: period {yy.addTask($1,0,'');$$=$1;}
@@ -92,21 +76,4 @@ event_statement
: event {yy.addEvent($1.substr(2));$$=$1;}
;
-
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'timeline'); }
- ;
-
%%
diff --git a/packages/mermaid/src/diagrams/timeline/timeline.spec.js b/packages/mermaid/src/diagrams/timeline/timeline.spec.js
index a6a94fd0c..69b9df1ba 100644
--- a/packages/mermaid/src/diagrams/timeline/timeline.spec.js
+++ b/packages/mermaid/src/diagrams/timeline/timeline.spec.js
@@ -1,24 +1,6 @@
import { parser as timeline } from './parser/timeline.jison';
import * as timelineDB from './timelineDb.js';
-// import { injectUtils } from './mermaidUtils.js';
-import { parseDirective as _parseDirective } from '../../directiveUtils.js';
-
-import {
- log,
- setLogLevel,
- getConfig,
- sanitizeText,
- setupGraphViewBox,
-} from '../../diagram-api/diagramAPI.js';
-
-// injectUtils(
-// log,
-// setLogLevel,
-// getConfig,
-// sanitizeText,
-// setupGraphViewBox,
-// _parseDirective
-// );
+import { setLogLevel } from '../../diagram-api/diagramAPI.js';
describe('when parsing a timeline ', function () {
beforeEach(function () {
diff --git a/packages/mermaid/src/diagrams/timeline/timelineDb.js b/packages/mermaid/src/diagrams/timeline/timelineDb.js
index e5e22147d..485cbb3a5 100644
--- a/packages/mermaid/src/diagrams/timeline/timelineDb.js
+++ b/packages/mermaid/src/diagrams/timeline/timelineDb.js
@@ -1,4 +1,3 @@
-import { parseDirective as _parseDirective } from '../../directiveUtils.js';
import * as commonDb from '../common/commonDb.js';
let currentSection = '';
let currentTaskId = 0;
@@ -9,10 +8,6 @@ const rawTasks = [];
export const getCommonDb = () => commonDb;
-export const parseDirective = (statement, context, type) => {
- _parseDirective(this, statement, context, type);
-};
-
export const clear = function () {
sections.length = 0;
tasks.length = 0;
@@ -104,5 +99,4 @@ export default {
addTask,
addTaskOrg,
addEvent,
- parseDirective,
};
diff --git a/packages/mermaid/src/diagrams/user-journey/journeyDb.js b/packages/mermaid/src/diagrams/user-journey/journeyDb.js
index 509c5dc14..4d71c2e9d 100644
--- a/packages/mermaid/src/diagrams/user-journey/journeyDb.js
+++ b/packages/mermaid/src/diagrams/user-journey/journeyDb.js
@@ -1,4 +1,3 @@
-import mermaidAPI from '../../mermaidAPI.js';
import * as configApi from '../../config.js';
import {
setAccTitle,
@@ -16,10 +15,6 @@ const sections = [];
const tasks = [];
const rawTasks = [];
-export const parseDirective = function (statement, context, type) {
- mermaidAPI.parseDirective(this, statement, context, type);
-};
-
export const clear = function () {
sections.length = 0;
tasks.length = 0;
@@ -118,7 +113,6 @@ const getActors = function () {
};
export default {
- parseDirective,
getConfig: () => configApi.getConfig().journey,
clear,
setDiagramTitle,
diff --git a/packages/mermaid/src/diagrams/user-journey/parser/journey.jison b/packages/mermaid/src/diagrams/user-journey/parser/journey.jison
index 4c28d53dc..5567f1417 100644
--- a/packages/mermaid/src/diagrams/user-journey/parser/journey.jison
+++ b/packages/mermaid/src/diagrams/user-journey/parser/journey.jison
@@ -9,17 +9,8 @@
%x acc_descr
%x acc_descr_multiline
-// Directive states
-%x open_directive type_directive arg_directive
-
-
%%
-\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
-((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
-":" { this.popState(); this.begin('arg_directive'); return ':'; }
-\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
-((?:(?!\}\%\%).|\n)*) return 'arg_directive';
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[\n]+ return 'NEWLINE';
@@ -52,7 +43,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
start
: journey document 'EOF' { return $2; }
- | directive start
;
document
@@ -67,11 +57,6 @@ line
| EOF { $$=[];}
;
-directive
- : openDirective typeDirective closeDirective 'NEWLINE'
- | openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
- ;
-
statement
: title {yy.setDiagramTitle($1.substr(6));$$=$1.substr(6);}
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
@@ -79,23 +64,6 @@ statement
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
| section {yy.addSection($1.substr(8));$$=$1.substr(8);}
| taskName taskData {yy.addTask($1, $2);$$='task';}
- | directive
- ;
-
-openDirective
- : open_directive { yy.parseDirective('%%{', 'open_directive'); }
- ;
-
-typeDirective
- : type_directive { yy.parseDirective($1, 'type_directive'); }
- ;
-
-argDirective
- : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
- ;
-
-closeDirective
- : close_directive { yy.parseDirective('}%%', 'close_directive', 'journey'); }
;
%%
diff --git a/packages/mermaid/src/directiveUtils.ts b/packages/mermaid/src/directiveUtils.ts
deleted file mode 100644
index baf628e74..000000000
--- a/packages/mermaid/src/directiveUtils.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import * as configApi from './config.js';
-import { log } from './logger.js';
-
-let currentDirective: { type?: string; args?: any } | undefined = {};
-
-export const parseDirective = function (
- p: any,
- statement: string,
- context: string,
- type: string
-): void {
- log.debug('parseDirective is being called', statement, context, type);
- try {
- if (statement !== undefined) {
- statement = statement.trim();
- switch (context) {
- case 'open_directive':
- currentDirective = {};
- break;
- case 'type_directive':
- if (!currentDirective) {
- throw new Error('currentDirective is undefined');
- }
- currentDirective.type = statement.toLowerCase();
- break;
- case 'arg_directive':
- if (!currentDirective) {
- throw new Error('currentDirective is undefined');
- }
- currentDirective.args = JSON.parse(statement);
- break;
- case 'close_directive':
- handleDirective(p, currentDirective, type);
- currentDirective = undefined;
- break;
- }
- }
- } catch (error) {
- log.error(
- `Error while rendering sequenceDiagram directive: ${statement} jison context: ${context}`
- );
- // @ts-ignore: TODO Fix ts errors
- log.error(error.message);
- }
-};
-
-const handleDirective = function (p: any, directive: any, type: string): void {
- log.info(`Directive type=${directive.type} with args:`, directive.args);
- switch (directive.type) {
- case 'init':
- case 'initialize': {
- ['config'].forEach((prop) => {
- if (directive.args[prop] !== undefined) {
- if (type === 'flowchart-v2') {
- type = 'flowchart';
- }
- directive.args[type] = directive.args[prop];
- delete directive.args[prop];
- }
- });
- configApi.addDirective(directive.args);
- break;
- }
- case 'wrap':
- case 'nowrap':
- if (p && p['setWrap']) {
- p.setWrap(directive.type === 'wrap');
- }
- break;
- case 'themeCss':
- log.warn('themeCss encountered');
- break;
- default:
- log.warn(
- `Unhandled directive: source: '%%{${directive.type}: ${JSON.stringify(
- directive.args ? directive.args : {}
- )}}%%`,
- directive
- );
- break;
- }
-};
diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts
index bb7570034..3649b50f5 100644
--- a/packages/mermaid/src/mermaidAPI.ts
+++ b/packages/mermaid/src/mermaidAPI.ts
@@ -23,14 +23,12 @@ import { attachFunctions } from './interactionDb.js';
import { log, setLogLevel } from './logger.js';
import getStyles from './styles.js';
import theme from './themes/index.js';
-import utils from './utils.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 { parseDirective } from './directiveUtils.js';
-import { extractFrontMatter } from './diagram-api/frontmatter.js';
+import { preprocessDiagram } from './preprocess.js';
// diagram names that support classDef statements
const CLASSDEF_DIAGRAMS = [
@@ -98,6 +96,13 @@ export interface RenderResult {
bindFunctions?: (element: Element) => void;
}
+function processAndSetConfigs(text: string) {
+ const processed = preprocessDiagram(text);
+ configApi.reset();
+ configApi.addDirective(processed.config ?? {});
+ return processed;
+}
+
/**
* Parse the text and validate the syntax.
* @param text - The mermaid diagram definition.
@@ -108,6 +113,9 @@ export interface RenderResult {
async function parse(text: string, parseOptions?: ParseOptions): Promise {
addDiagrams();
+
+ text = processAndSetConfigs(text).code;
+
try {
await getDiagramFromText(text);
} catch (error) {
@@ -384,18 +392,8 @@ const render = async function (
): Promise {
addDiagrams();
- configApi.reset();
-
- // 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);
- }
+ const processed = processAndSetConfigs(text);
+ text = processed.code;
const config = configApi.getConfig();
log.debug(config);
@@ -405,15 +403,6 @@ const render = async function (
text = MAX_TEXTLENGTH_EXCEEDED_MSG;
}
- // clean up text CRLFs
- text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
-
- // clean up html tags so that all attributes use single quotes, parser throws error on double quotes
- text = text.replace(
- /<(\w+)([^>]*)>/g,
- (match, tag, attributes) => '<' + tag + attributes.replace(/="([^"]*)"/g, "='$1'") + '>'
- );
-
const idSelector = '#' + id;
const iFrameID = 'i' + id;
const iFrameID_selector = '#' + iFrameID;
@@ -476,7 +465,7 @@ const render = async function (
let parseEncounteredException;
try {
- diag = await getDiagramFromText(text);
+ diag = await getDiagramFromText(text, { title: processed.title });
} catch (error) {
diag = new Diagram('error');
parseEncounteredException = error;
@@ -673,7 +662,6 @@ function addA11yInfo(
export const mermaidAPI = Object.freeze({
render,
parse,
- parseDirective,
getDiagramFromText,
initialize,
getConfig: configApi.getConfig,
diff --git a/packages/mermaid/src/preprocess.ts b/packages/mermaid/src/preprocess.ts
new file mode 100644
index 000000000..6e386e744
--- /dev/null
+++ b/packages/mermaid/src/preprocess.ts
@@ -0,0 +1,65 @@
+import { cleanupComments } from './diagram-api/comments.js';
+import { extractFrontMatter } from './diagram-api/frontmatter.js';
+import type { DiagramMetadata } from './diagram-api/types.js';
+import utils, { cleanAndMerge, removeDirectives } from './utils.js';
+
+const cleanupText = (code: string) => {
+ return (
+ code
+ // parser problems on CRLF ignore all CR and leave LF;;
+ .replace(/\r\n?/g, '\n')
+ // clean up html tags so that all attributes use single quotes, parser throws error on double quotes
+ .replace(
+ /<(\w+)([^>]*)>/g,
+ (match, tag, attributes) => '<' + tag + attributes.replace(/="([^"]*)"/g, "='$1'") + '>'
+ )
+ );
+};
+
+const processFrontmatter = (code: string) => {
+ const { text, metadata } = extractFrontMatter(code);
+ const { displayMode, title, config = {} } = metadata;
+ if (displayMode) {
+ // Needs to be supported for legacy reasons
+ if (!config.gantt) {
+ config.gantt = {};
+ }
+ config.gantt.displayMode = displayMode;
+ }
+ return { title, config, text };
+};
+
+const processDirectives = (code: string) => {
+ const initDirective = utils.detectInit(code) ?? {};
+ const wrapDirectives = utils.detectDirective(code, 'wrap');
+ if (Array.isArray(wrapDirectives)) {
+ initDirective.wrap = wrapDirectives.some(({ type }) => {
+ type === 'wrap';
+ });
+ } else if (wrapDirectives?.type === 'wrap') {
+ initDirective.wrap = true;
+ }
+ return {
+ text: removeDirectives(code),
+ directive: initDirective,
+ };
+};
+
+/**
+ * Preprocess the given code by cleaning it up, extracting front matter and directives,
+ * cleaning and merging configuration, and removing comments.
+ * @param code - The code to preprocess.
+ * @returns The object containing the preprocessed code, title, and configuration.
+ */
+export function preprocessDiagram(code: string): DiagramMetadata & { code: string } {
+ const cleanedCode = cleanupText(code);
+ const frontMatterResult = processFrontmatter(cleanedCode);
+ const directiveResult = processDirectives(frontMatterResult.text);
+ const config = cleanAndMerge(frontMatterResult.config, directiveResult.directive);
+ code = cleanupComments(directiveResult.text);
+ return {
+ code,
+ title: frontMatterResult.title,
+ config,
+ };
+}
diff --git a/packages/mermaid/src/utils.spec.ts b/packages/mermaid/src/utils.spec.ts
index 3d00c444a..726529f58 100644
--- a/packages/mermaid/src/utils.spec.ts
+++ b/packages/mermaid/src/utils.spec.ts
@@ -1,10 +1,11 @@
import { vi } from 'vitest';
-import utils, { cleanAndMerge } from './utils.js';
+import utils, { cleanAndMerge, detectDirective } from './utils.js';
import assignWithDepth from './assignWithDepth.js';
import { detectType } from './diagram-api/detectType.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import memoize from 'lodash-es/memoize.js';
import { MockedD3 } from './tests/MockedD3.js';
+import { preprocessDiagram } from './preprocess.js';
addDiagrams();
@@ -158,13 +159,38 @@ describe('when detecting chart type ', function () {
const type = detectType(str);
expect(type).toBe('flowchart');
});
+ it('should handle a wrap directive', () => {
+ const wrap = { type: 'wrap', args: null };
+ expect(detectDirective('%%{wrap}%%', 'wrap')).toEqual(wrap);
+ expect(
+ detectDirective(
+ `%%{
+ wrap
+ }%%`,
+ 'wrap'
+ )
+ ).toEqual(wrap);
+ expect(
+ detectDirective(
+ `%%{
+
+ wrap
+
+ }%%`,
+ 'wrap'
+ )
+ ).toEqual(wrap);
+ expect(detectDirective('%%{wrap:}%%', 'wrap')).toEqual(wrap);
+ expect(detectDirective('%%{wrap: }%%', 'wrap')).toEqual(wrap);
+ expect(detectDirective('graph', 'wrap')).not.toEqual(wrap);
+ });
it('should handle an initialize definition', function () {
const str = `
%%{initialize: { 'logLevel': 0, 'theme': 'dark' }}%%
sequenceDiagram
Alice->Bob: hi`;
const type = detectType(str);
- const init = utils.detectInit(str);
+ const init = preprocessDiagram(str).config;
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
});
@@ -174,7 +200,7 @@ Alice->Bob: hi`;
sequenceDiagram
Alice->Bob: hi`;
const type = detectType(str);
- const init = utils.detectInit(str);
+ const init = preprocessDiagram(str).config;
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
});
@@ -184,7 +210,7 @@ Alice->Bob: hi`;
sequenceDiagram
Alice->Bob: hi`;
const type = detectType(str);
- const init = utils.detectInit(str);
+ const init = preprocessDiagram(str).config;
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark', sequence: { wrap: true } });
});
@@ -199,7 +225,7 @@ Alice->Bob: hi`;
sequenceDiagram
Alice->Bob: hi`;
const type = detectType(str);
- const init = utils.detectInit(str);
+ const init = preprocessDiagram(str).config;
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
});
@@ -214,7 +240,7 @@ Alice->Bob: hi`;
sequenceDiagram
Alice->Bob: hi`;
const type = detectType(str);
- const init = utils.detectInit(str);
+ const init = preprocessDiagram(str).config;
expect(type).toBe('sequence');
expect(init).toEqual({ logLevel: 0, theme: 'dark' });
});
diff --git a/packages/mermaid/src/utils.ts b/packages/mermaid/src/utils.ts
index 42b4ee67e..70de197da 100644
--- a/packages/mermaid/src/utils.ts
+++ b/packages/mermaid/src/utils.ts
@@ -25,7 +25,7 @@ import {
select,
} from 'd3';
import common from './diagrams/common/common.js';
-import { configKeys } from './defaultConfig.js';
+import { sanitizeDirective } from './utils/sanitizeDirective.js';
import { log } from './logger.js';
import { detectType } from './diagram-api/detectType.js';
import assignWithDepth from './assignWithDepth.js';
@@ -62,7 +62,6 @@ const d3CurveTypes = {
const directiveWithoutOpen =
/\s*(?:(\w+)(?=:):|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
-
/**
* Detects the init config object from the text
*
@@ -197,6 +196,10 @@ export const detectDirective = function (
}
};
+export const removeDirectives = function (text: string): string {
+ return text.replace(directiveRegex, '');
+};
+
/**
* Detects whether a substring in present in a given array
*
@@ -842,88 +845,6 @@ export const entityDecode = function (html: string): string {
return unescape(decoder.textContent);
};
-/**
- * Sanitizes directive objects
- *
- * @param args - Directive's JSON
- */
-export const sanitizeDirective = (args: unknown): void => {
- log.debug('sanitizeDirective called with', args);
-
- // Return if not an object
- if (typeof args !== 'object' || args == null) {
- return;
- }
-
- // Sanitize each element if an array
- if (Array.isArray(args)) {
- args.forEach((arg) => sanitizeDirective(arg));
- return;
- }
-
- // 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) ||
- args[key] == null
- ) {
- 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) {
- for (const k of Object.keys(args.themeVariables)) {
- const val = args.themeVariables[k];
- if (val?.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) {
- args.themeVariables[k] = '';
- }
- }
- }
- log.debug('After sanitization', args);
-};
-
-export const sanitizeCss = (str: string): string => {
- let startCnt = 0;
- let endCnt = 0;
-
- for (const element of str) {
- if (startCnt < endCnt) {
- return '{ /* ERROR: Unbalanced CSS */ }';
- }
- if (element === '{') {
- startCnt++;
- } else if (element === '}') {
- endCnt++;
- }
- }
- if (startCnt !== endCnt) {
- return '{ /* ERROR: Unbalanced CSS */ }';
- }
- // Todo add more checks here
- return str;
-};
-
export interface DetailedError {
str: string;
hash: any;
@@ -1021,8 +942,6 @@ export default {
runFunc,
entityDecode,
initIdGenerator,
- sanitizeDirective,
- sanitizeCss,
insertTitle,
parseFontSize,
};
diff --git a/packages/mermaid/src/utils/sanitizeDirective.ts b/packages/mermaid/src/utils/sanitizeDirective.ts
new file mode 100644
index 000000000..9b7e7da5c
--- /dev/null
+++ b/packages/mermaid/src/utils/sanitizeDirective.ts
@@ -0,0 +1,84 @@
+import { configKeys } from '../defaultConfig.js';
+import { log } from '../logger.js';
+
+/**
+ * Sanitizes directive objects
+ *
+ * @param args - Directive's JSON
+ */
+export const sanitizeDirective = (args: any): void => {
+ log.debug('sanitizeDirective called with', args);
+
+ // Return if not an object
+ if (typeof args !== 'object' || args == null) {
+ return;
+ }
+
+ // Sanitize each element if an array
+ if (Array.isArray(args)) {
+ args.forEach((arg) => sanitizeDirective(arg));
+ return;
+ }
+
+ // 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) ||
+ args[key] == null
+ ) {
+ 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) {
+ for (const k of Object.keys(args.themeVariables)) {
+ const val = args.themeVariables[k];
+ if (val?.match && !val.match(/^[\d "#%(),.;A-Za-z]+$/)) {
+ args.themeVariables[k] = '';
+ }
+ }
+ }
+ log.debug('After sanitization', args);
+};
+
+export const sanitizeCss = (str: string): string => {
+ let startCnt = 0;
+ let endCnt = 0;
+
+ for (const element of str) {
+ if (startCnt < endCnt) {
+ return '{ /* ERROR: Unbalanced CSS */ }';
+ }
+ if (element === '{') {
+ startCnt++;
+ } else if (element === '}') {
+ endCnt++;
+ }
+ }
+ if (startCnt !== endCnt) {
+ return '{ /* ERROR: Unbalanced CSS */ }';
+ }
+ // Todo add more checks here
+ return str;
+};