Merge branch next into add-pie-langium-parser

This commit is contained in:
Reda Al Sulais
2024-01-27 20:55:15 +03:00
11 changed files with 115 additions and 173 deletions

View File

@@ -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);
};

View File

@@ -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', () => {

View File

@@ -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 };
} }

View File

@@ -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

View File

@@ -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.'); );
}); });
}); });

View File

@@ -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);
}, },
}; };

View File

@@ -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."'
); );
}); });

View File

@@ -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);
}, },

View File

@@ -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

View File

@@ -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);
}; };
/** /**

View File

@@ -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);