mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-05 00:26:40 +02:00
Merge branch next into add-pie-langium-parser
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
import * as configApi from './config.js';
|
import * as configApi from './config.js';
|
||||||
import { log } from './logger.js';
|
|
||||||
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
|
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI.js';
|
||||||
import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
|
import { detectType, getDiagramLoader } from './diagram-api/detectType.js';
|
||||||
import { UnknownDiagramError } from './errors.js';
|
import { UnknownDiagramError } from './errors.js';
|
||||||
import { encodeEntities } from './utils.js';
|
import { encodeEntities } from './utils.js';
|
||||||
|
|
||||||
import type { DetailedError } from './utils.js';
|
import type { DetailedError } from './utils.js';
|
||||||
import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js';
|
import type { DiagramDefinition, DiagramMetadata } from './diagram-api/types.js';
|
||||||
|
|
||||||
@@ -15,51 +13,45 @@ export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?:
|
|||||||
* @privateRemarks This is exported as part of the public mermaidAPI.
|
* @privateRemarks This is exported as part of the public mermaidAPI.
|
||||||
*/
|
*/
|
||||||
export class Diagram {
|
export class Diagram {
|
||||||
type = 'graph';
|
public static async fromText(text: string, metadata: Pick<DiagramMetadata, 'title'> = {}) {
|
||||||
parser: DiagramDefinition['parser'];
|
|
||||||
renderer: DiagramDefinition['renderer'];
|
|
||||||
db: DiagramDefinition['db'];
|
|
||||||
private init?: DiagramDefinition['init'];
|
|
||||||
|
|
||||||
private detectError?: UnknownDiagramError;
|
|
||||||
constructor(public text: string, public metadata: Pick<DiagramMetadata, 'title'> = {}) {
|
|
||||||
this.text = encodeEntities(text);
|
|
||||||
this.text += '\n';
|
|
||||||
const cnf = configApi.getConfig();
|
|
||||||
try {
|
|
||||||
this.type = detectType(text, cnf);
|
|
||||||
} catch (e) {
|
|
||||||
this.type = 'error';
|
|
||||||
this.detectError = e as UnknownDiagramError;
|
|
||||||
}
|
|
||||||
const diagram = getDiagram(this.type);
|
|
||||||
log.debug('Type ' + this.type);
|
|
||||||
// Setup diagram
|
|
||||||
this.db = diagram.db;
|
|
||||||
this.renderer = diagram.renderer;
|
|
||||||
this.parser = diagram.parser;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
this.init = diagram.init;
|
|
||||||
this.parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
parse() {
|
|
||||||
if (this.detectError) {
|
|
||||||
throw this.detectError;
|
|
||||||
}
|
|
||||||
this.db.clear?.();
|
|
||||||
const config = configApi.getConfig();
|
const config = configApi.getConfig();
|
||||||
this.init?.(config);
|
const type = detectType(text, config);
|
||||||
// This block was added for legacy compatibility. Use frontmatter instead of adding more special cases.
|
text = encodeEntities(text) + '\n';
|
||||||
if (this.metadata.title) {
|
try {
|
||||||
this.db.setDiagramTitle?.(this.metadata.title);
|
getDiagram(type);
|
||||||
|
} catch (e) {
|
||||||
|
const loader = getDiagramLoader(type);
|
||||||
|
if (!loader) {
|
||||||
|
throw new UnknownDiagramError(`Diagram ${type} not found.`);
|
||||||
|
}
|
||||||
|
// Diagram not available, loading it.
|
||||||
|
// new diagram will try getDiagram again and if fails then it is a valid throw
|
||||||
|
const { id, diagram } = await loader();
|
||||||
|
registerDiagram(id, diagram);
|
||||||
}
|
}
|
||||||
this.parser.parse(this.text);
|
const { db, parser, renderer, init } = getDiagram(type);
|
||||||
|
if (parser.parser) {
|
||||||
|
// The parser.parser.yy is only present in JISON parsers. So, we'll only set if required.
|
||||||
|
parser.parser.yy = db;
|
||||||
|
}
|
||||||
|
db.clear?.();
|
||||||
|
init?.(config);
|
||||||
|
// This block was added for legacy compatibility. Use frontmatter instead of adding more special cases.
|
||||||
|
if (metadata.title) {
|
||||||
|
db.setDiagramTitle?.(metadata.title);
|
||||||
|
}
|
||||||
|
await parser.parse(text);
|
||||||
|
return new Diagram(type, text, db, parser, renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
public type: string,
|
||||||
|
public text: string,
|
||||||
|
public db: DiagramDefinition['db'],
|
||||||
|
public parser: DiagramDefinition['parser'],
|
||||||
|
public renderer: DiagramDefinition['renderer']
|
||||||
|
) {}
|
||||||
|
|
||||||
async render(id: string, version: string) {
|
async render(id: string, version: string) {
|
||||||
await this.renderer.draw(this.text, id, version, this);
|
await this.renderer.draw(this.text, id, version, this);
|
||||||
}
|
}
|
||||||
@@ -72,34 +64,3 @@ export class Diagram {
|
|||||||
return this.type;
|
return this.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the text asynchronously and generate a Diagram object asynchronously.
|
|
||||||
* **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,
|
|
||||||
metadata: Pick<DiagramMetadata, 'title'> = {}
|
|
||||||
): Promise<Diagram> => {
|
|
||||||
const type = detectType(text, configApi.getConfig());
|
|
||||||
try {
|
|
||||||
// Trying to find the diagram
|
|
||||||
getDiagram(type);
|
|
||||||
} catch (error) {
|
|
||||||
const loader = getDiagramLoader(type);
|
|
||||||
if (!loader) {
|
|
||||||
throw new UnknownDiagramError(`Diagram ${type} not found.`);
|
|
||||||
}
|
|
||||||
// Diagram not available, loading it.
|
|
||||||
// new diagram will try getDiagram again and if fails then it is a valid throw
|
|
||||||
const { id, diagram } = await loader();
|
|
||||||
registerDiagram(id, diagram);
|
|
||||||
}
|
|
||||||
return new Diagram(text, metadata);
|
|
||||||
};
|
|
||||||
|
@@ -2,12 +2,12 @@ import { detectType } from './detectType.js';
|
|||||||
import { getDiagram, registerDiagram } from './diagramAPI.js';
|
import { getDiagram, registerDiagram } from './diagramAPI.js';
|
||||||
import { addDiagrams } from './diagram-orchestration.js';
|
import { addDiagrams } from './diagram-orchestration.js';
|
||||||
import type { DiagramDetector } from './types.js';
|
import type { DiagramDetector } from './types.js';
|
||||||
import { getDiagramFromText } from '../Diagram.js';
|
import { Diagram } from '../Diagram.js';
|
||||||
import { it, describe, expect, beforeAll } from 'vitest';
|
import { it, describe, expect, beforeAll } from 'vitest';
|
||||||
|
|
||||||
addDiagrams();
|
addDiagrams();
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await getDiagramFromText('sequenceDiagram');
|
await Diagram.fromText('sequenceDiagram');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DiagramAPI', () => {
|
describe('DiagramAPI', () => {
|
||||||
|
@@ -121,7 +121,7 @@ export type DrawDefinition = (
|
|||||||
) => void | Promise<void>;
|
) => void | Promise<void>;
|
||||||
|
|
||||||
export interface ParserDefinition {
|
export interface ParserDefinition {
|
||||||
parse: (text: string) => void;
|
parse: (text: string) => void | Promise<void>;
|
||||||
parser?: { yy: DiagramDB };
|
parser?: { yy: DiagramDB };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { Diagram, getDiagramFromText } from './Diagram.js';
|
import { Diagram } from './Diagram.js';
|
||||||
import { addDetector } from './diagram-api/detectType.js';
|
import { addDetector } from './diagram-api/detectType.js';
|
||||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
||||||
import type { DiagramLoader } from './diagram-api/types.js';
|
import type { DiagramLoader } from './diagram-api/types.js';
|
||||||
@@ -30,10 +30,10 @@ const getDummyDiagram = (id: string, title?: string): Awaited<ReturnType<Diagram
|
|||||||
|
|
||||||
describe('diagram detection', () => {
|
describe('diagram detection', () => {
|
||||||
test('should detect inbuilt diagrams', async () => {
|
test('should detect inbuilt diagrams', async () => {
|
||||||
const graph = (await getDiagramFromText('graph TD; A-->B')) as Diagram;
|
const graph = (await Diagram.fromText('graph TD; A-->B')) as Diagram;
|
||||||
expect(graph).toBeInstanceOf(Diagram);
|
expect(graph).toBeInstanceOf(Diagram);
|
||||||
expect(graph.type).toBe('flowchart-v2');
|
expect(graph.type).toBe('flowchart-v2');
|
||||||
const sequence = (await getDiagramFromText(
|
const sequence = (await Diagram.fromText(
|
||||||
'sequenceDiagram; Alice->>+John: Hello John, how are you?'
|
'sequenceDiagram; Alice->>+John: Hello John, how are you?'
|
||||||
)) as Diagram;
|
)) as Diagram;
|
||||||
expect(sequence).toBeInstanceOf(Diagram);
|
expect(sequence).toBeInstanceOf(Diagram);
|
||||||
@@ -46,7 +46,7 @@ describe('diagram detection', () => {
|
|||||||
(str) => str.startsWith('loki'),
|
(str) => str.startsWith('loki'),
|
||||||
() => Promise.resolve(getDummyDiagram('loki'))
|
() => Promise.resolve(getDummyDiagram('loki'))
|
||||||
);
|
);
|
||||||
const diagram = await getDiagramFromText('loki TD; A-->B');
|
const diagram = await Diagram.fromText('loki TD; A-->B');
|
||||||
expect(diagram).toBeInstanceOf(Diagram);
|
expect(diagram).toBeInstanceOf(Diagram);
|
||||||
expect(diagram.type).toBe('loki');
|
expect(diagram.type).toBe('loki');
|
||||||
});
|
});
|
||||||
@@ -58,19 +58,19 @@ describe('diagram detection', () => {
|
|||||||
(str) => str.startsWith('flowchart-elk'),
|
(str) => str.startsWith('flowchart-elk'),
|
||||||
() => Promise.resolve(getDummyDiagram('flowchart-elk', title))
|
() => Promise.resolve(getDummyDiagram('flowchart-elk', title))
|
||||||
);
|
);
|
||||||
const diagram = await getDiagramFromText('flowchart-elk TD; A-->B');
|
const diagram = await Diagram.fromText('flowchart-elk TD; A-->B');
|
||||||
expect(diagram).toBeInstanceOf(Diagram);
|
expect(diagram).toBeInstanceOf(Diagram);
|
||||||
expect(diagram.db.getDiagramTitle?.()).toBe(title);
|
expect(diagram.db.getDiagramTitle?.()).toBe(title);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should throw the right error for incorrect diagram', async () => {
|
test('should throw the right error for incorrect diagram', async () => {
|
||||||
await expect(getDiagramFromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
|
await expect(Diagram.fromText('graph TD; A-->')).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||||
"Parse error on line 2:
|
"Parse error on line 2:
|
||||||
graph TD; A-->
|
graph TD; A-->
|
||||||
--------------^
|
--------------^
|
||||||
Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF'"
|
Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF'"
|
||||||
`);
|
`);
|
||||||
await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects
|
await expect(Diagram.fromText('sequenceDiagram; A-->B')).rejects
|
||||||
.toThrowErrorMatchingInlineSnapshot(`
|
.toThrowErrorMatchingInlineSnapshot(`
|
||||||
"Parse error on line 1:
|
"Parse error on line 1:
|
||||||
...quenceDiagram; A-->B
|
...quenceDiagram; A-->B
|
||||||
@@ -80,13 +80,13 @@ Expecting 'TXT', got 'NEWLINE'"
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should throw the right error for unregistered diagrams', async () => {
|
test('should throw the right error for unregistered diagrams', async () => {
|
||||||
await expect(getDiagramFromText('thor TD; A-->B')).rejects.toThrowErrorMatchingInlineSnapshot(
|
await expect(Diagram.fromText('thor TD; A-->B')).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||||
'"No diagram type detected matching given configuration for text: thor TD; A-->B"'
|
'"No diagram type detected matching given configuration for text: thor TD; A-->B"'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should consider entity codes when present in diagram defination', async () => {
|
test('should consider entity codes when present in diagram defination', async () => {
|
||||||
const diagram = await getDiagramFromText(`sequenceDiagram
|
const diagram = await Diagram.fromText(`sequenceDiagram
|
||||||
A->>B: I #9829; you!
|
A->>B: I #9829; you!
|
||||||
B->>A: I #9829; you #infin; times more!`);
|
B->>A: I #9829; you #infin; times more!`);
|
||||||
// @ts-ignore: we need to add types for sequenceDb which will be done in separate PR
|
// @ts-ignore: we need to add types for sequenceDb which will be done in separate PR
|
||||||
|
@@ -1,31 +1,27 @@
|
|||||||
import { parser } from './infoParser.js';
|
import { parser } from './infoParser.js';
|
||||||
|
|
||||||
describe('info', () => {
|
describe('info', () => {
|
||||||
it('should handle an info definition', () => {
|
it('should handle an info definition', async () => {
|
||||||
const str = `info`;
|
const str = `info`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle an info definition with showInfo', () => {
|
it('should handle an info definition with showInfo', async () => {
|
||||||
const str = `info showInfo`;
|
const str = `info showInfo`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw because of unsupported info grammar', () => {
|
it('should throw because of unsupported info grammar', async () => {
|
||||||
const str = `info unsupported`;
|
const str = `info unsupported`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).rejects.toThrow(
|
||||||
parser.parse(str);
|
'Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.'
|
||||||
}).toThrow('Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.');
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw because of unsupported info grammar', () => {
|
it('should throw because of unsupported info grammar', async () => {
|
||||||
const str = `info unsupported`;
|
const str = `info unsupported`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).rejects.toThrow(
|
||||||
parser.parse(str);
|
'Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.'
|
||||||
}).toThrow('Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.');
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import type { Info } from '@mermaid-js/parser';
|
import type { Info } from '@mermaid-js/parser';
|
||||||
import { parse } from '@mermaid-js/parser';
|
import { parse } from '@mermaid-js/parser';
|
||||||
|
|
||||||
import { log } from '../../logger.js';
|
|
||||||
import type { ParserDefinition } from '../../diagram-api/types.js';
|
import type { ParserDefinition } from '../../diagram-api/types.js';
|
||||||
|
import { log } from '../../logger.js';
|
||||||
|
|
||||||
export const parser: ParserDefinition = {
|
export const parser: ParserDefinition = {
|
||||||
parse: (input: string): void => {
|
parse: async (input: string): Promise<void> => {
|
||||||
const ast: Info = parse('info', input);
|
const ast: Info = await parse('info', input);
|
||||||
log.debug(ast);
|
log.debug(ast);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { it, describe, expect } from 'vitest';
|
||||||
import { db } from './db.js';
|
import { db } from './db.js';
|
||||||
import { parser } from './parser.js';
|
import { parser } from './parser.js';
|
||||||
|
|
||||||
@@ -8,24 +9,20 @@ describe('packet diagrams', () => {
|
|||||||
clear();
|
clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle a packet-beta definition', () => {
|
it('should handle a packet-beta definition', async () => {
|
||||||
const str = `packet-beta`;
|
const str = `packet-beta`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
expect(getPacket()).toMatchInlineSnapshot('[]');
|
expect(getPacket()).toMatchInlineSnapshot('[]');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle diagram with data and title', () => {
|
it('should handle diagram with data and title', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
title Packet diagram
|
title Packet diagram
|
||||||
accTitle: Packet accTitle
|
accTitle: Packet accTitle
|
||||||
accDescr: Packet accDescription
|
accDescr: Packet accDescription
|
||||||
0-10: "test"
|
0-10: "test"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
expect(getDiagramTitle()).toMatchInlineSnapshot('"Packet diagram"');
|
expect(getDiagramTitle()).toMatchInlineSnapshot('"Packet diagram"');
|
||||||
expect(getAccTitle()).toMatchInlineSnapshot('"Packet accTitle"');
|
expect(getAccTitle()).toMatchInlineSnapshot('"Packet accTitle"');
|
||||||
expect(getAccDescription()).toMatchInlineSnapshot('"Packet accDescription"');
|
expect(getAccDescription()).toMatchInlineSnapshot('"Packet accDescription"');
|
||||||
@@ -42,14 +39,12 @@ describe('packet diagrams', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle single bits', () => {
|
it('should handle single bits', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-10: "test"
|
0-10: "test"
|
||||||
11: "single"
|
11: "single"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
expect(getPacket()).toMatchInlineSnapshot(`
|
expect(getPacket()).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@@ -68,14 +63,12 @@ describe('packet diagrams', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should split into multiple rows', () => {
|
it('should split into multiple rows', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-10: "test"
|
0-10: "test"
|
||||||
11-90: "multiple"
|
11-90: "multiple"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
expect(getPacket()).toMatchInlineSnapshot(`
|
expect(getPacket()).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@@ -108,14 +101,12 @@ describe('packet diagrams', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should split into multiple rows when cut at exact length', () => {
|
it('should split into multiple rows when cut at exact length', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
0-16: "test"
|
||||||
17-63: "multiple"
|
17-63: "multiple"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||||
parser.parse(str);
|
|
||||||
}).not.toThrow();
|
|
||||||
expect(getPacket()).toMatchInlineSnapshot(`
|
expect(getPacket()).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@@ -141,51 +132,43 @@ describe('packet diagrams', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error if numbers are not continuous', () => {
|
it('should throw error if numbers are not continuous', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
0-16: "test"
|
||||||
18-20: "error"
|
18-20: "error"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||||
parser.parse(str);
|
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
|
||||||
'"Packet block 18 - 20 is not contiguous. It should start from 17."'
|
'"Packet block 18 - 20 is not contiguous. It should start from 17."'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error if numbers are not continuous for single packets', () => {
|
it('should throw error if numbers are not continuous for single packets', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
0-16: "test"
|
||||||
18: "error"
|
18: "error"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||||
parser.parse(str);
|
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
|
||||||
'"Packet block 18 - 18 is not contiguous. It should start from 17."'
|
'"Packet block 18 - 18 is not contiguous. It should start from 17."'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error if numbers are not continuous for single packets - 2', () => {
|
it('should throw error if numbers are not continuous for single packets - 2', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
0-16: "test"
|
||||||
17: "good"
|
17: "good"
|
||||||
19: "error"
|
19: "error"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||||
parser.parse(str);
|
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
|
||||||
'"Packet block 19 - 19 is not contiguous. It should start from 18."'
|
'"Packet block 19 - 19 is not contiguous. It should start from 18."'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error if end is less than start', () => {
|
it('should throw error if end is less than start', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
0-16: "test"
|
||||||
25-20: "error"
|
25-20: "error"
|
||||||
`;
|
`;
|
||||||
expect(() => {
|
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||||
parser.parse(str);
|
|
||||||
}).toThrowErrorMatchingInlineSnapshot(
|
|
||||||
'"Packet block 25 - 20 is invalid. End must be greater than start."'
|
'"Packet block 25 - 20 is invalid. End must be greater than start."'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -77,8 +77,8 @@ const getNextFittingBlock = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const parser: ParserDefinition = {
|
export const parser: ParserDefinition = {
|
||||||
parse: (input: string): void => {
|
parse: async (input: string): Promise<void> => {
|
||||||
const ast: Packet = parse('packet', input);
|
const ast: Packet = await parse('packet', input);
|
||||||
log.debug(ast);
|
log.debug(ast);
|
||||||
populate(ast);
|
populate(ast);
|
||||||
},
|
},
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
import { setSiteConfig } from '../../diagram-api/diagramAPI.js';
|
import { setSiteConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import mermaidAPI from '../../mermaidAPI.js';
|
import mermaidAPI from '../../mermaidAPI.js';
|
||||||
import { Diagram, getDiagramFromText } from '../../Diagram.js';
|
import { Diagram } from '../../Diagram.js';
|
||||||
import { addDiagrams } from '../../diagram-api/diagram-orchestration.js';
|
import { addDiagrams } from '../../diagram-api/diagram-orchestration.js';
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// Is required to load the sequence diagram
|
// Is required to load the sequence diagram
|
||||||
await getDiagramFromText('sequenceDiagram');
|
await Diagram.fromText('sequenceDiagram');
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,8 +95,8 @@ function addConf(conf, key, value) {
|
|||||||
let diagram;
|
let diagram;
|
||||||
|
|
||||||
describe('more than one sequence diagram', () => {
|
describe('more than one sequence diagram', () => {
|
||||||
it('should not have duplicated messages', () => {
|
it('should not have duplicated messages', async () => {
|
||||||
const diagram1 = new Diagram(`
|
const diagram1 = await Diagram.fromText(`
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
Alice->Bob:Hello Bob, how are you?
|
Alice->Bob:Hello Bob, how are you?
|
||||||
Bob-->Alice: I am good thanks!`);
|
Bob-->Alice: I am good thanks!`);
|
||||||
@@ -120,7 +120,7 @@ describe('more than one sequence diagram', () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
const diagram2 = new Diagram(`
|
const diagram2 = await Diagram.fromText(`
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
Alice->Bob:Hello Bob, how are you?
|
Alice->Bob:Hello Bob, how are you?
|
||||||
Bob-->Alice: I am good thanks!`);
|
Bob-->Alice: I am good thanks!`);
|
||||||
@@ -147,7 +147,7 @@ describe('more than one sequence diagram', () => {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
// Add John actor
|
// Add John actor
|
||||||
const diagram3 = new Diagram(`
|
const diagram3 = await Diagram.fromText(`
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
Alice->John:Hello John, how are you?
|
Alice->John:Hello John, how are you?
|
||||||
John-->Alice: I am good thanks!`);
|
John-->Alice: I am good thanks!`);
|
||||||
@@ -176,8 +176,8 @@ describe('more than one sequence diagram', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when parsing a sequenceDiagram', function () {
|
describe('when parsing a sequenceDiagram', function () {
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
diagram = new Diagram(`
|
diagram = await Diagram.fromText(`
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
Alice->Bob:Hello Bob, how are you?
|
Alice->Bob:Hello Bob, how are you?
|
||||||
Note right of Bob: Bob thinks
|
Note right of Bob: Bob thinks
|
||||||
@@ -1613,7 +1613,7 @@ describe('when rendering a sequenceDiagram APA', function () {
|
|||||||
setSiteConfig({ logLevel: 5, sequence: conf });
|
setSiteConfig({ logLevel: 5, sequence: conf });
|
||||||
});
|
});
|
||||||
let conf;
|
let conf;
|
||||||
beforeEach(function () {
|
beforeEach(async function () {
|
||||||
mermaidAPI.reset();
|
mermaidAPI.reset();
|
||||||
|
|
||||||
// });
|
// });
|
||||||
@@ -1632,7 +1632,7 @@ describe('when rendering a sequenceDiagram APA', function () {
|
|||||||
mirrorActors: false,
|
mirrorActors: false,
|
||||||
};
|
};
|
||||||
setSiteConfig({ logLevel: 5, sequence: conf });
|
setSiteConfig({ logLevel: 5, sequence: conf });
|
||||||
diagram = new Diagram(`
|
diagram = await Diagram.fromText(`
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
Alice->Bob:Hello Bob, how are you?
|
Alice->Bob:Hello Bob, how are you?
|
||||||
Note right of Bob: Bob thinks
|
Note right of Bob: Bob thinks
|
||||||
|
@@ -17,7 +17,7 @@ import { compile, serialize, stringify } from 'stylis';
|
|||||||
import { version } from '../package.json';
|
import { version } from '../package.json';
|
||||||
import * as configApi from './config.js';
|
import * as configApi from './config.js';
|
||||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
||||||
import { Diagram, getDiagramFromText as getDiagramFromTextInternal } from './Diagram.js';
|
import { Diagram } from './Diagram.js';
|
||||||
import errorRenderer from './diagrams/error/errorRenderer.js';
|
import errorRenderer from './diagrams/error/errorRenderer.js';
|
||||||
import { attachFunctions } from './interactionDb.js';
|
import { attachFunctions } from './interactionDb.js';
|
||||||
import { log, setLogLevel } from './logger.js';
|
import { log, setLogLevel } from './logger.js';
|
||||||
@@ -422,9 +422,9 @@ const render = async function (
|
|||||||
let parseEncounteredException;
|
let parseEncounteredException;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
diag = await getDiagramFromText(text, { title: processed.title });
|
diag = await Diagram.fromText(text, { title: processed.title });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
diag = new Diagram('error');
|
diag = await Diagram.fromText('error');
|
||||||
parseEncounteredException = error;
|
parseEncounteredException = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +536,7 @@ function initialize(options: MermaidConfig = {}) {
|
|||||||
|
|
||||||
const getDiagramFromText = (text: string, metadata: Pick<DiagramMetadata, 'title'> = {}) => {
|
const getDiagramFromText = (text: string, metadata: Pick<DiagramMetadata, 'title'> = {}) => {
|
||||||
const { code } = preprocessDiagram(text);
|
const { code } = preprocessDiagram(text);
|
||||||
return getDiagramFromTextInternal(code, metadata);
|
return Diagram.fromText(code, metadata);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,38 +1,41 @@
|
|||||||
import type { LangiumParser, ParseResult } from 'langium';
|
import type { LangiumParser, ParseResult } from 'langium';
|
||||||
|
|
||||||
import type { Info, Packet, Pie } from './index.js';
|
import type { Info, Packet, Pie } from './index.js';
|
||||||
import { createInfoServices, createPacketServices, createPieServices } from './language/index.js';
|
|
||||||
|
|
||||||
export type DiagramAST = Info | Packet | Pie;
|
export type DiagramAST = Info | Packet | Pie;
|
||||||
|
|
||||||
const parsers: Record<string, LangiumParser> = {};
|
const parsers: Record<string, LangiumParser> = {};
|
||||||
const initializers = {
|
const initializers = {
|
||||||
info: () => {
|
info: async () => {
|
||||||
// Will have to make parse async to use this. Can try later...
|
const { createInfoServices } = await import('./language/info/index.js');
|
||||||
// const { createInfoServices } = await import('./language/info/index.js');
|
const parser = createInfoServices().Info.parser.LangiumParser;
|
||||||
parsers['info'] = createInfoServices().Info.parser.LangiumParser;
|
parsers['info'] = parser;
|
||||||
},
|
},
|
||||||
packet: () => {
|
packet: async () => {
|
||||||
parsers['packet'] = createPacketServices().Packet.parser.LangiumParser;
|
const { createPacketServices } = await import('./language/packet/index.js');
|
||||||
|
const parser = createPacketServices().Packet.parser.LangiumParser;
|
||||||
|
parsers['packet'] = parser;
|
||||||
},
|
},
|
||||||
pie: () => {
|
pie: async () => {
|
||||||
parsers['pie'] = createPieServices().Pie.parser.LangiumParser;
|
const { createPieServices } = await import('./language/pie/index.js');
|
||||||
|
const parser = createPieServices().Pie.parser.LangiumParser;
|
||||||
|
parsers['pie'] = parser;
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export function parse(diagramType: 'info', text: string): Info;
|
export async function parse(diagramType: 'info', text: string): Promise<Info>;
|
||||||
export function parse(diagramType: 'packet', text: string): Packet;
|
export async function parse(diagramType: 'packet', text: string): Promise<Packet>;
|
||||||
export function parse(diagramType: 'pie', text: string): Pie;
|
export async function parse(diagramType: 'pie', text: string): Promise<Pie>;
|
||||||
export function parse<T extends DiagramAST>(
|
export async function parse<T extends DiagramAST>(
|
||||||
diagramType: keyof typeof initializers,
|
diagramType: keyof typeof initializers,
|
||||||
text: string
|
text: string
|
||||||
): T {
|
): Promise<T> {
|
||||||
const initializer = initializers[diagramType];
|
const initializer = initializers[diagramType];
|
||||||
if (!initializer) {
|
if (!initializer) {
|
||||||
throw new Error(`Unknown diagram type: ${diagramType}`);
|
throw new Error(`Unknown diagram type: ${diagramType}`);
|
||||||
}
|
}
|
||||||
if (!parsers[diagramType]) {
|
if (!parsers[diagramType]) {
|
||||||
initializer();
|
await initializer();
|
||||||
}
|
}
|
||||||
const parser: LangiumParser = parsers[diagramType];
|
const parser: LangiumParser = parsers[diagramType];
|
||||||
const result: ParseResult<T> = parser.parse<T>(text);
|
const result: ParseResult<T> = parser.parse<T>(text);
|
||||||
|
Reference in New Issue
Block a user