mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-22 08:50:13 +02:00
Handling of YAML blocks for sequence diagrams
This commit is contained in:
@@ -34,12 +34,17 @@ export const detectors: Record<string, DetectorRecord> = {};
|
|||||||
* @returns A graph definition key
|
* @returns A graph definition key
|
||||||
*/
|
*/
|
||||||
export const detectType = function (text: string, config?: MermaidConfig): string {
|
export const detectType = function (text: string, config?: MermaidConfig): string {
|
||||||
text = text
|
const cleanedText = text
|
||||||
.replace(frontMatterRegex, '')
|
.replace(frontMatterRegex, '')
|
||||||
.replace(directiveRegex, '')
|
.replace(directiveRegex, '')
|
||||||
.replace(anyCommentRegex, '\n');
|
.replace(anyCommentRegex, '\n');
|
||||||
|
|
||||||
for (const [key, { detector }] of Object.entries(detectors)) {
|
for (const [key, { detector }] of Object.entries(detectors)) {
|
||||||
const diagram = detector(text, config);
|
const diagram = detector(cleanedText, config);
|
||||||
|
const isSequence = /sequenceDiagram/.exec(cleanedText);
|
||||||
|
if (isSequence) {
|
||||||
|
return 'sequence';
|
||||||
|
}
|
||||||
if (diagram) {
|
if (diagram) {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ import architecture from '../diagrams/architecture/architectureDetector.js';
|
|||||||
import { registerLazyLoadedDiagrams } from './detectType.js';
|
import { registerLazyLoadedDiagrams } from './detectType.js';
|
||||||
import { registerDiagram } from './diagramAPI.js';
|
import { registerDiagram } from './diagramAPI.js';
|
||||||
import { treemap } from '../diagrams/treemap/detector.js';
|
import { treemap } from '../diagrams/treemap/detector.js';
|
||||||
|
import { frontMatterRegex } from './regexes.js';
|
||||||
import '../type.d.ts';
|
import '../type.d.ts';
|
||||||
|
|
||||||
let hasLoadedDiagrams = false;
|
let hasLoadedDiagrams = false;
|
||||||
@@ -68,7 +69,21 @@ export const addDiagrams = () => {
|
|||||||
init: () => null, // no op
|
init: () => null, // no op
|
||||||
},
|
},
|
||||||
(text) => {
|
(text) => {
|
||||||
return text.toLowerCase().trimStart().startsWith('---');
|
const trimmed = text.trimStart();
|
||||||
|
if (!trimmed.startsWith('---')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If there is a valid YAML front matter block, and the remaining text starts
|
||||||
|
// with a sequence diagram header, let the sequence diagram handle it.
|
||||||
|
const m = trimmed.match(frontMatterRegex);
|
||||||
|
if (m) {
|
||||||
|
const rest = trimmed.slice(m[0].length).trimStart();
|
||||||
|
if (/^sequencediagram\b/i.test(rest)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, treat this as an invalid diagram beginning with '---'
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
lexer grammar SequenceLexer;
|
lexer grammar SequenceLexer;
|
||||||
tokens { AS }
|
tokens { AS }
|
||||||
|
|
||||||
|
@members {
|
||||||
|
private seenSD = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Comments (skip)
|
// Comments (skip)
|
||||||
HASH_COMMENT: '#' ~[\r\n]* -> skip;
|
HASH_COMMENT: '#' ~[\r\n]* -> skip;
|
||||||
@@ -9,6 +11,10 @@ PERCENT_COMMENT1: '%%' ~[\r\n]* -> skip;
|
|||||||
PERCENT_COMMENT2: ~[}] '%%' ~[\r\n]* -> skip;
|
PERCENT_COMMENT2: ~[}] '%%' ~[\r\n]* -> skip;
|
||||||
|
|
||||||
// Whitespace and newline
|
// Whitespace and newline
|
||||||
|
|
||||||
|
// YAML front matter (allowed before the diagram header)
|
||||||
|
FRONTMATTER_START: { !this.seenSD }? '---' [ \t]* ('\r'? '\n') -> pushMode(FRONTMATTER_MODE), skip;
|
||||||
|
|
||||||
NEWLINE: ('\r'? '\n')+;
|
NEWLINE: ('\r'? '\n')+;
|
||||||
WS: [ \t]+ -> skip;
|
WS: [ \t]+ -> skip;
|
||||||
|
|
||||||
@@ -19,7 +25,7 @@ PLUS: '+';
|
|||||||
MINUS: '-';
|
MINUS: '-';
|
||||||
|
|
||||||
// Core keywords
|
// Core keywords
|
||||||
SD: 'sequenceDiagram' -> pushMode(AFTER_SD);
|
SD: 'sequenceDiagram' { this.seenSD = true; } -> pushMode(AFTER_SD);
|
||||||
PARTICIPANT: 'participant' -> pushMode(ID);
|
PARTICIPANT: 'participant' -> pushMode(ID);
|
||||||
PARTICIPANT_ACTOR: 'actor' -> pushMode(ID);
|
PARTICIPANT_ACTOR: 'actor' -> pushMode(ID);
|
||||||
CREATE: 'create';
|
CREATE: 'create';
|
||||||
@@ -105,6 +111,7 @@ mode ACC_DESCR_MODE;
|
|||||||
ACC_DESCR_VALUE: (~[\r\n;#])* -> popMode;
|
ACC_DESCR_VALUE: (~[\r\n;#])* -> popMode;
|
||||||
|
|
||||||
mode ACC_DESCR_MULTILINE_MODE;
|
mode ACC_DESCR_MULTILINE_MODE;
|
||||||
|
|
||||||
ACC_DESCR_MULTILINE_END: '}' -> popMode;
|
ACC_DESCR_MULTILINE_END: '}' -> popMode;
|
||||||
ACC_DESCR_MULTILINE_VALUE: (~['}'])*;
|
ACC_DESCR_MULTILINE_VALUE: (~['}'])*;
|
||||||
|
|
||||||
@@ -112,6 +119,11 @@ mode CONFIG_MODE;
|
|||||||
CONFIG_CONTENT: (~[}])+;
|
CONFIG_CONTENT: (~[}])+;
|
||||||
CONFIG_END: '}' -> popMode;
|
CONFIG_END: '}' -> popMode;
|
||||||
|
|
||||||
|
// YAML front matter mode: consume until closing '---' line, then pop
|
||||||
|
mode FRONTMATTER_MODE;
|
||||||
|
FM_END: [ \t]* '---' [ \t]* ('\r'? '\n') -> popMode, skip;
|
||||||
|
FM_LINE: (~[\r\n])* ('\r'? '\n') -> skip;
|
||||||
|
|
||||||
|
|
||||||
// After the diagram name keyword, consume the rest of header line then pop
|
// After the diagram name keyword, consume the rest of header line then pop
|
||||||
mode AFTER_SD;
|
mode AFTER_SD;
|
||||||
|
@@ -25,7 +25,7 @@ const getEnvVar = (name: string): string | undefined => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const USE_ANTLR_PARSER = getEnvVar('USE_ANTLR_PARSER') === 'true';
|
const USE_ANTLR_PARSER = true; //getEnvVar('USE_ANTLR_PARSER') === 'false';
|
||||||
|
|
||||||
// Force logging to window for debugging
|
// Force logging to window for debugging
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
|
@@ -4,7 +4,7 @@ import mermaidAPI from '../../mermaidAPI.js';
|
|||||||
import { Diagram } from '../../Diagram.js';
|
import { Diagram } from '../../Diagram.js';
|
||||||
import { addDiagrams } from '../../diagram-api/diagram-orchestration.js';
|
import { addDiagrams } from '../../diagram-api/diagram-orchestration.js';
|
||||||
import { SequenceDB } from './sequenceDb.js';
|
import { SequenceDB } from './sequenceDb.js';
|
||||||
|
import { preprocessDiagram } from './preprocess.js';
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Is required to load the sequence diagram
|
// Is required to load the sequence diagram
|
||||||
await Diagram.fromText('sequenceDiagram');
|
await Diagram.fromText('sequenceDiagram');
|
||||||
@@ -1820,6 +1820,51 @@ Alice->Bob: Hello Bob, how are you?`;
|
|||||||
expect(bounds.stopy).toBe(models.lastMessage().stopy + 10);
|
expect(bounds.stopy).toBe(models.lastMessage().stopy + 10);
|
||||||
expect(msgs.every((v) => v.wrap)).toBe(true);
|
expect(msgs.every((v) => v.wrap)).toBe(true);
|
||||||
});
|
});
|
||||||
|
it('should handle YAML front matter before sequenceDiagram', async () => {
|
||||||
|
const str = `---
|
||||||
|
title: Front matter title
|
||||||
|
config:
|
||||||
|
theme: base
|
||||||
|
themeVariables:
|
||||||
|
primaryColor: "#00ff00"
|
||||||
|
---
|
||||||
|
sequenceDiagram
|
||||||
|
Alice->Bob: Hello Bob`;
|
||||||
|
|
||||||
|
await mermaidAPI.parse(str);
|
||||||
|
const diagram = await Diagram.fromText(str);
|
||||||
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
|
const messages = diagram.db.getMessages();
|
||||||
|
expect(messages.length).toBe(1);
|
||||||
|
expect(messages[0].from).toBe('Alice');
|
||||||
|
expect(messages[0].to).toBe('Bob');
|
||||||
|
expect(messages[0].message).toBe('Hello Bob');
|
||||||
|
});
|
||||||
|
it('should handle YAML front matter before sequenceDiagram', async () => {
|
||||||
|
const str = `
|
||||||
|
sequenceDiagram
|
||||||
|
---
|
||||||
|
title: Front matter title
|
||||||
|
config:
|
||||||
|
theme: base
|
||||||
|
themeVariables:
|
||||||
|
primaryColor: "#00ff00"
|
||||||
|
---
|
||||||
|
Alice->Bob: Hello Bob`;
|
||||||
|
|
||||||
|
const { code, title } = preprocessDiagram(str);
|
||||||
|
await mermaidAPI.parse(str);
|
||||||
|
const diagram = await Diagram.fromText(code, { title });
|
||||||
|
await diagram.renderer.draw(code, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
|
const messages = diagram.db.getMessages();
|
||||||
|
expect(messages.length).toBe(1);
|
||||||
|
expect(messages[0].from).toBe('Alice');
|
||||||
|
expect(messages[0].to).toBe('Bob');
|
||||||
|
expect(messages[0].message).toBe('Hello Bob');
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle two actors and two centered shared notes', async () => {
|
it('should handle two actors and two centered shared notes', async () => {
|
||||||
const str = `
|
const str = `
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
|
Reference in New Issue
Block a user