mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-11 15:04:06 +01:00
Merge branch 'next' into sidv/tinyMermaid
* next: (27 commits) chore: Fix type refactor!: remove MermaidConfig type enum fallback test: rewrite some `config` vals to tighten types chore: Add comment for `yy`. chore: Increase heap size when building chore: increase `test-util.ts` converage by returning `undefined` chore: add `vitest` imports to `test-util.ts` chore: run `pnpm lint:fix` create `noErrorsOrAlternatives` parser helper function chore: export `InfoModule` from `infoModule.ts` docs(parser): create `packages/parser` README.md file build: build `.langium` file using `generate` from `langium-cli` build: update `langium` and `langium-cli` to `v2.0.1` fix: fix if statment logic checks if `parser` is not `undefined` chore: add a comment illustrate why we build packages sequentially chore: refactore `&&` into `if` in `populateCommonDb` chore: remove `./*` part from `exports` in `parser/package.json` fix: use `execFileSync` instead of `execSync` in `generateLangium` fix(mermaid): mark `mermaid-parser` dependecy with `^` reorder `packages/parser` after `packages/mermaid/src/vitepress` ...
This commit is contained in:
@@ -2,6 +2,11 @@
|
|||||||
* Shared common options for both ESBuild and Vite
|
* Shared common options for both ESBuild and Vite
|
||||||
*/
|
*/
|
||||||
export const packageOptions = {
|
export const packageOptions = {
|
||||||
|
parser: {
|
||||||
|
name: 'mermaid-parser',
|
||||||
|
packageName: 'parser',
|
||||||
|
file: 'index.ts',
|
||||||
|
},
|
||||||
mermaid: {
|
mermaid: {
|
||||||
name: 'mermaid',
|
name: 'mermaid',
|
||||||
packageName: 'mermaid',
|
packageName: 'mermaid',
|
||||||
|
|||||||
5
.build/generateLangium.ts
Normal file
5
.build/generateLangium.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { generate } from 'langium-cli';
|
||||||
|
|
||||||
|
export async function generateLangium() {
|
||||||
|
await generate({ file: `./packages/parser/langium-config.json` });
|
||||||
|
}
|
||||||
9
.build/langium-cli.d.ts
vendored
Normal file
9
.build/langium-cli.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
declare module 'langium-cli' {
|
||||||
|
export interface GenerateOptions {
|
||||||
|
file?: string;
|
||||||
|
mode?: 'development' | 'production';
|
||||||
|
watch?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generate(options: GenerateOptions): Promise<boolean>;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { build } from 'esbuild';
|
|||||||
import { mkdir, writeFile } from 'node:fs/promises';
|
import { mkdir, writeFile } from 'node:fs/promises';
|
||||||
import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
|
import { MermaidBuildOptions, defaultOptions, getBuildConfig } from './util.js';
|
||||||
import { packageOptions } from '../.build/common.js';
|
import { packageOptions } from '../.build/common.js';
|
||||||
|
import { generateLangium } from '../.build/generateLangium.js';
|
||||||
|
|
||||||
const shouldVisualize = process.argv.includes('--visualize');
|
const shouldVisualize = process.argv.includes('--visualize');
|
||||||
|
|
||||||
@@ -59,9 +60,13 @@ const handler = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
await generateLangium();
|
||||||
await mkdir('stats').catch(() => {});
|
await mkdir('stats').catch(() => {});
|
||||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||||
await Promise.allSettled(packageNames.map((pkg) => buildPackage(pkg).catch(handler)));
|
// it should build `parser` before `mermaid` because it's a dependecy
|
||||||
|
for (const pkg of packageNames) {
|
||||||
|
await buildPackage(pkg).catch(handler);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void main();
|
void main();
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import cors from 'cors';
|
|||||||
import { getBuildConfig, defaultOptions } from './util.js';
|
import { getBuildConfig, defaultOptions } from './util.js';
|
||||||
import { context } from 'esbuild';
|
import { context } from 'esbuild';
|
||||||
import chokidar from 'chokidar';
|
import chokidar from 'chokidar';
|
||||||
|
import { generateLangium } from '../.build/generateLangium.js';
|
||||||
|
|
||||||
|
const parserCtx = await context(
|
||||||
|
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'parser' })
|
||||||
|
);
|
||||||
const mermaidCtx = await context(
|
const mermaidCtx = await context(
|
||||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' })
|
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' })
|
||||||
);
|
);
|
||||||
@@ -28,7 +32,7 @@ const externalCtx = await context(
|
|||||||
const zenumlCtx = await context(
|
const zenumlCtx = await context(
|
||||||
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' })
|
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' })
|
||||||
);
|
);
|
||||||
const contexts = [mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
|
const contexts = [parserCtx, mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
|
||||||
|
|
||||||
const rebuildAll = async () => {
|
const rebuildAll = async () => {
|
||||||
console.time('Rebuild time');
|
console.time('Rebuild time');
|
||||||
@@ -75,10 +79,11 @@ function sendEventsToAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createServer() {
|
async function createServer() {
|
||||||
|
await generateLangium();
|
||||||
handleFileChange();
|
handleFileChange();
|
||||||
const app = express();
|
const app = express();
|
||||||
chokidar
|
chokidar
|
||||||
.watch('**/src/**/*.{js,ts,yaml,json}', {
|
.watch('**/src/**/*.{js,ts,langium,yaml,json}', {
|
||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
|
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
|
||||||
})
|
})
|
||||||
@@ -87,12 +92,16 @@ async function createServer() {
|
|||||||
if (!['add', 'change'].includes(event)) {
|
if (!['add', 'change'].includes(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (/\.langium$/.test(path)) {
|
||||||
|
await generateLangium();
|
||||||
|
}
|
||||||
console.log(`${path} changed. Rebuilding...`);
|
console.log(`${path} changed. Rebuilding...`);
|
||||||
handleFileChange();
|
handleFileChange();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.get('/events', eventsHandler);
|
app.get('/events', eventsHandler);
|
||||||
|
app.use(express.static('./packages/parser/dist'));
|
||||||
app.use(express.static('./packages/mermaid/dist'));
|
app.use(express.static('./packages/mermaid/dist'));
|
||||||
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
||||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||||
|
|||||||
@@ -6,3 +6,6 @@ cypress/plugins/index.js
|
|||||||
coverage
|
coverage
|
||||||
*.json
|
*.json
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
# autogenereated by langium-cli
|
||||||
|
generated/
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -47,3 +47,6 @@ stats/
|
|||||||
demos/dev/**
|
demos/dev/**
|
||||||
!/demos/dev/example.html
|
!/demos/dev/example.html
|
||||||
!/demos/dev/reload.js
|
!/demos/dev/reload.js
|
||||||
|
|
||||||
|
# autogenereated by langium-cli
|
||||||
|
generated/
|
||||||
|
|||||||
@@ -10,3 +10,6 @@ stats
|
|||||||
.nyc_output
|
.nyc_output
|
||||||
# Autogenerated by `pnpm run --filter mermaid types:build-config`
|
# Autogenerated by `pnpm run --filter mermaid types:build-config`
|
||||||
packages/mermaid/src/config.type.ts
|
packages/mermaid/src/config.type.ts
|
||||||
|
|
||||||
|
# autogenereated by langium-cli
|
||||||
|
generated/
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { visualizer } from 'rollup-plugin-visualizer';
|
|||||||
import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js';
|
import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js';
|
||||||
import istanbul from 'vite-plugin-istanbul';
|
import istanbul from 'vite-plugin-istanbul';
|
||||||
import { packageOptions } from '../.build/common.js';
|
import { packageOptions } from '../.build/common.js';
|
||||||
|
import { generateLangium } from '../.build/generateLangium.js';
|
||||||
|
|
||||||
const visualize = process.argv.includes('--visualize');
|
const visualize = process.argv.includes('--visualize');
|
||||||
const watch = process.argv.includes('--watch');
|
const watch = process.argv.includes('--watch');
|
||||||
@@ -82,7 +83,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
|||||||
// @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
|
// @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
|
||||||
typescript({ compilerOptions: { declaration: false } }),
|
typescript({ compilerOptions: { declaration: false } }),
|
||||||
istanbul({
|
istanbul({
|
||||||
exclude: ['node_modules', 'test/', '__mocks__'],
|
exclude: ['node_modules', 'test/', '__mocks__', 'generated'],
|
||||||
extension: ['.js', '.ts'],
|
extension: ['.js', '.ts'],
|
||||||
requireEnv: true,
|
requireEnv: true,
|
||||||
forceBuildInstrument: coverage,
|
forceBuildInstrument: coverage,
|
||||||
@@ -110,18 +111,24 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
|||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||||
for (const pkg of packageNames.filter((pkg) => !mermaidOnly || pkg === 'mermaid')) {
|
for (const pkg of packageNames.filter(
|
||||||
|
(pkg) => !mermaidOnly || pkg === 'mermaid' || pkg === 'parser'
|
||||||
|
)) {
|
||||||
await buildPackage(pkg);
|
await buildPackage(pkg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
await generateLangium();
|
||||||
|
|
||||||
if (watch) {
|
if (watch) {
|
||||||
|
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
||||||
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
||||||
if (!mermaidOnly) {
|
if (!mermaidOnly) {
|
||||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
||||||
}
|
}
|
||||||
} else if (visualize) {
|
} else if (visualize) {
|
||||||
|
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
||||||
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
|
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));
|
||||||
await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' }));
|
await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' }));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ async function createServer() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
app.use(express.static('./packages/parser/dist'));
|
||||||
app.use(express.static('./packages/mermaid/dist'));
|
app.use(express.static('./packages/mermaid/dist'));
|
||||||
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
app.use(express.static('./packages/mermaid-zenuml/dist'));
|
||||||
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
app.use(express.static('./packages/mermaid-example-diagram/dist'));
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
"knut",
|
"knut",
|
||||||
"knutsveidqvist",
|
"knutsveidqvist",
|
||||||
"laganeckas",
|
"laganeckas",
|
||||||
|
"langium",
|
||||||
"linetype",
|
"linetype",
|
||||||
"lintstagedrc",
|
"lintstagedrc",
|
||||||
"logmsg",
|
"logmsg",
|
||||||
|
|||||||
@@ -15,11 +15,11 @@
|
|||||||
"git graph"
|
"git graph"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run -r clean && pnpm build:esbuild && pnpm build:types",
|
"build": "pnpm build:esbuild && pnpm build:types",
|
||||||
"build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts",
|
"build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts",
|
||||||
"build:mermaid": "pnpm build:esbuild --mermaid",
|
"build:mermaid": "pnpm build:esbuild --mermaid",
|
||||||
"build:viz": "pnpm build:esbuild --visualize",
|
"build:viz": "pnpm build:esbuild --visualize",
|
||||||
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
|
"build:types": "tsc -p ./packages/parser/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-zenuml/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
|
||||||
"dev": "ts-node-esm --transpileOnly .esbuild/server.ts",
|
"dev": "ts-node-esm --transpileOnly .esbuild/server.ts",
|
||||||
"dev:vite": "ts-node-esm --transpileOnly .vite/server.ts",
|
"dev:vite": "ts-node-esm --transpileOnly .vite/server.ts",
|
||||||
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
|
"dev:coverage": "pnpm coverage:cypress:clean && VITE_COVERAGE=true pnpm dev:vite",
|
||||||
@@ -107,6 +107,7 @@
|
|||||||
"jison": "^0.4.18",
|
"jison": "^0.4.18",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsdom": "^22.0.0",
|
"jsdom": "^22.0.0",
|
||||||
|
"langium-cli": "2.0.1",
|
||||||
"lint-staged": "^13.2.1",
|
"lint-staged": "^13.2.1",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
* This is a dummy parser that satisfies the mermaid API logic.
|
* This is a dummy parser that satisfies the mermaid API logic.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
parser: { yy: {} },
|
|
||||||
parse: () => {
|
parse: () => {
|
||||||
// no op
|
// no op
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,18 +72,18 @@
|
|||||||
"khroma": "^2.0.0",
|
"khroma": "^2.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mdast-util-from-markdown": "^1.3.0",
|
"mdast-util-from-markdown": "^1.3.0",
|
||||||
|
"mermaid-parser": "workspace:^",
|
||||||
"stylis": "^4.1.3",
|
"stylis": "^4.1.3",
|
||||||
"ts-dedent": "^2.2.0",
|
"ts-dedent": "^2.2.0",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@adobe/jsonschema2md": "^7.1.4",
|
"@adobe/jsonschema2md": "^7.1.4",
|
||||||
"@types/d3-scale": "^4.0.3",
|
|
||||||
"@types/d3-scale-chromatic": "^3.0.0",
|
|
||||||
"@types/cytoscape": "^3.19.9",
|
"@types/cytoscape": "^3.19.9",
|
||||||
"@types/d3": "^7.4.0",
|
"@types/d3": "^7.4.0",
|
||||||
"@types/d3-sankey": "^0.12.1",
|
"@types/d3-sankey": "^0.12.1",
|
||||||
"@types/d3-scale": "^4.0.3",
|
"@types/d3-scale": "^4.0.3",
|
||||||
|
"@types/d3-scale-chromatic": "^3.0.0",
|
||||||
"@types/d3-selection": "^3.0.5",
|
"@types/d3-selection": "^3.0.5",
|
||||||
"@types/d3-shape": "^3.1.1",
|
"@types/d3-shape": "^3.1.1",
|
||||||
"@types/dompurify": "^3.0.2",
|
"@types/dompurify": "^3.0.2",
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ export class Diagram {
|
|||||||
this.db = diagram.db;
|
this.db = diagram.db;
|
||||||
this.renderer = diagram.renderer;
|
this.renderer = diagram.renderer;
|
||||||
this.parser = diagram.parser;
|
this.parser = diagram.parser;
|
||||||
this.parser.parser.yy = this.db;
|
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.init = diagram.init;
|
||||||
this.parse();
|
this.parse();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export interface MermaidConfig {
|
|||||||
* You may also use `themeCSS` to override this value.
|
* You may also use `themeCSS` to override this value.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
theme?: string | 'default' | 'forest' | 'dark' | 'neutral' | 'null';
|
theme?: 'default' | 'forest' | 'dark' | 'neutral' | 'null';
|
||||||
themeVariables?: any;
|
themeVariables?: any;
|
||||||
themeCSS?: string;
|
themeCSS?: string;
|
||||||
/**
|
/**
|
||||||
@@ -82,26 +82,11 @@ export interface MermaidConfig {
|
|||||||
* This option decides the amount of logging to be used by mermaid.
|
* This option decides the amount of logging to be used by mermaid.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
logLevel?:
|
logLevel?: 'trace' | 0 | 'debug' | 1 | 'info' | 2 | 'warn' | 3 | 'error' | 4 | 'fatal' | 5;
|
||||||
| number
|
|
||||||
| string
|
|
||||||
| 0
|
|
||||||
| 2
|
|
||||||
| 1
|
|
||||||
| 'trace'
|
|
||||||
| 'debug'
|
|
||||||
| 'info'
|
|
||||||
| 'warn'
|
|
||||||
| 'error'
|
|
||||||
| 'fatal'
|
|
||||||
| 3
|
|
||||||
| 4
|
|
||||||
| 5
|
|
||||||
| undefined;
|
|
||||||
/**
|
/**
|
||||||
* Level of trust for parsed diagram
|
* Level of trust for parsed diagram
|
||||||
*/
|
*/
|
||||||
securityLevel?: string | 'strict' | 'loose' | 'antiscript' | 'sandbox' | undefined;
|
securityLevel?: 'strict' | 'loose' | 'antiscript' | 'sandbox';
|
||||||
/**
|
/**
|
||||||
* Dictates whether mermaid starts on Page load
|
* Dictates whether mermaid starts on Page load
|
||||||
*/
|
*/
|
||||||
@@ -723,7 +708,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
|
|||||||
/**
|
/**
|
||||||
* Directional bias for layout of entities
|
* Directional bias for layout of entities
|
||||||
*/
|
*/
|
||||||
layoutDirection?: string | 'TB' | 'BT' | 'LR' | 'RL';
|
layoutDirection?: 'TB' | 'BT' | 'LR' | 'RL';
|
||||||
/**
|
/**
|
||||||
* The minimum width of an entity box. Expressed in pixels.
|
* The minimum width of an entity box. Expressed in pixels.
|
||||||
*/
|
*/
|
||||||
@@ -788,7 +773,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig {
|
|||||||
* Decides which rendering engine that is to be used for the rendering.
|
* Decides which rendering engine that is to be used for the rendering.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `MermaidConfig`'s JSON-Schema
|
* This interface was referenced by `MermaidConfig`'s JSON-Schema
|
||||||
@@ -812,7 +797,7 @@ export interface ClassDiagramConfig extends BaseDiagramConfig {
|
|||||||
* Decides which rendering engine that is to be used for the rendering.
|
* Decides which rendering engine that is to be used for the rendering.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
||||||
nodeSpacing?: number;
|
nodeSpacing?: number;
|
||||||
rankSpacing?: number;
|
rankSpacing?: number;
|
||||||
/**
|
/**
|
||||||
@@ -872,7 +857,7 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
|
|||||||
/**
|
/**
|
||||||
* Multiline message alignment
|
* Multiline message alignment
|
||||||
*/
|
*/
|
||||||
messageAlign?: string | 'left' | 'center' | 'right';
|
messageAlign?: 'left' | 'center' | 'right';
|
||||||
/**
|
/**
|
||||||
* Prolongs the edge of the diagram downwards.
|
* Prolongs the edge of the diagram downwards.
|
||||||
*
|
*
|
||||||
@@ -951,7 +936,7 @@ export interface TimelineDiagramConfig extends BaseDiagramConfig {
|
|||||||
/**
|
/**
|
||||||
* Multiline message alignment
|
* Multiline message alignment
|
||||||
*/
|
*/
|
||||||
messageAlign?: string | 'left' | 'center' | 'right';
|
messageAlign?: 'left' | 'center' | 'right';
|
||||||
/**
|
/**
|
||||||
* Prolongs the edge of the diagram downwards.
|
* Prolongs the edge of the diagram downwards.
|
||||||
*
|
*
|
||||||
@@ -1062,7 +1047,7 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
|
|||||||
* Controls the display mode.
|
* Controls the display mode.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
displayMode?: string | 'compact';
|
displayMode?: '' | 'compact';
|
||||||
/**
|
/**
|
||||||
* On which day a week-based interval should start
|
* On which day a week-based interval should start
|
||||||
*
|
*
|
||||||
@@ -1121,7 +1106,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
|||||||
/**
|
/**
|
||||||
* Multiline message alignment
|
* Multiline message alignment
|
||||||
*/
|
*/
|
||||||
messageAlign?: string | 'left' | 'center' | 'right';
|
messageAlign?: 'left' | 'center' | 'right';
|
||||||
/**
|
/**
|
||||||
* Mirror actors under diagram
|
* Mirror actors under diagram
|
||||||
*
|
*
|
||||||
@@ -1178,7 +1163,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
|||||||
/**
|
/**
|
||||||
* This sets the text alignment of actor-attached notes
|
* This sets the text alignment of actor-attached notes
|
||||||
*/
|
*/
|
||||||
noteAlign?: string | 'left' | 'center' | 'right';
|
noteAlign?: 'left' | 'center' | 'right';
|
||||||
/**
|
/**
|
||||||
* This sets the font size of actor messages
|
* This sets the font size of actor messages
|
||||||
*/
|
*/
|
||||||
@@ -1254,7 +1239,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
|||||||
* Defines how mermaid renders curves for flowcharts.
|
* Defines how mermaid renders curves for flowcharts.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
curve?: string | 'basis' | 'linear' | 'cardinal';
|
curve?: 'basis' | 'linear' | 'cardinal';
|
||||||
/**
|
/**
|
||||||
* Represents the padding between the labels and the shape
|
* Represents the padding between the labels and the shape
|
||||||
*
|
*
|
||||||
@@ -1266,7 +1251,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
|||||||
* Decides which rendering engine that is to be used for the rendering.
|
* Decides which rendering engine that is to be used for the rendering.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
defaultRenderer?: string | 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk';
|
||||||
/**
|
/**
|
||||||
* Width of nodes where text is wrapped.
|
* Width of nodes where text is wrapped.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ export const addDiagrams = () => {
|
|||||||
styles: {}, // should never be used
|
styles: {}, // should never be used
|
||||||
renderer: {}, // should never be used
|
renderer: {}, // should never be used
|
||||||
parser: {
|
parser: {
|
||||||
parser: { yy: {} },
|
|
||||||
parse: () => {
|
parse: () => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Diagrams beginning with --- are not valid. ' +
|
'Diagrams beginning with --- are not valid. ' +
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ describe('DiagramAPI', () => {
|
|||||||
parse: (_text) => {
|
parse: (_text) => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
parser: { yy: {} },
|
|
||||||
},
|
},
|
||||||
renderer: {},
|
renderer: {},
|
||||||
styles: {},
|
styles: {},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { MermaidConfig } from '../config.type.js';
|
import type { GanttDiagramConfig, MermaidConfig } from '../config.type.js';
|
||||||
import { frontMatterRegex } from './regexes.js';
|
import { frontMatterRegex } from './regexes.js';
|
||||||
// The "* as yaml" part is necessary for tree-shaking
|
// The "* as yaml" part is necessary for tree-shaking
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
@@ -6,7 +6,7 @@ import * as yaml from 'js-yaml';
|
|||||||
interface FrontMatterMetadata {
|
interface FrontMatterMetadata {
|
||||||
title?: string;
|
title?: string;
|
||||||
// Allows custom display modes. Currently used for compact mode in gantt charts.
|
// Allows custom display modes. Currently used for compact mode in gantt charts.
|
||||||
displayMode?: string;
|
displayMode?: GanttDiagramConfig['displayMode'];
|
||||||
config?: MermaidConfig;
|
config?: MermaidConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ export function extractFrontMatter(text: string): FrontMatterResult {
|
|||||||
|
|
||||||
// Only add properties that are explicitly supported, if they exist
|
// Only add properties that are explicitly supported, if they exist
|
||||||
if (parsed.displayMode) {
|
if (parsed.displayMode) {
|
||||||
metadata.displayMode = parsed.displayMode.toString();
|
metadata.displayMode = parsed.displayMode.toString() as GanttDiagramConfig['displayMode'];
|
||||||
}
|
}
|
||||||
if (parsed.title) {
|
if (parsed.title) {
|
||||||
metadata.title = parsed.title.toString();
|
metadata.title = parsed.title.toString();
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export type DrawDefinition = (
|
|||||||
|
|
||||||
export interface ParserDefinition {
|
export interface ParserDefinition {
|
||||||
parse: (text: string) => void;
|
parse: (text: string) => void;
|
||||||
parser: { yy: DiagramDB };
|
parser?: { yy: DiagramDB };
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>;
|
export type HTML = d3.Selection<HTMLIFrameElement, unknown, Element | null, unknown>;
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ describe('diagram detection', () => {
|
|||||||
parse: () => {
|
parse: () => {
|
||||||
// no-op
|
// no-op
|
||||||
},
|
},
|
||||||
parser: {
|
|
||||||
yy: {},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
renderer: {},
|
renderer: {},
|
||||||
styles: {},
|
styles: {},
|
||||||
|
|||||||
15
packages/mermaid/src/diagrams/common/populateCommonDb.ts
Normal file
15
packages/mermaid/src/diagrams/common/populateCommonDb.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { DiagramAST } from 'mermaid-parser';
|
||||||
|
|
||||||
|
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||||
|
|
||||||
|
export function populateCommonDb(ast: DiagramAST, db: DiagramDB) {
|
||||||
|
if (ast.accDescr) {
|
||||||
|
db.setAccDescription?.(ast.accDescr);
|
||||||
|
}
|
||||||
|
if (ast.accTitle) {
|
||||||
|
db.setAccTitle?.(ast.accTitle);
|
||||||
|
}
|
||||||
|
if (ast.title) {
|
||||||
|
db.setDiagramTitle?.(ast.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ export const diagram: DiagramDefinition = {
|
|||||||
db: {},
|
db: {},
|
||||||
renderer,
|
renderer,
|
||||||
parser: {
|
parser: {
|
||||||
parser: { yy: {} },
|
|
||||||
parse: (): void => {
|
parse: (): void => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,24 +1,31 @@
|
|||||||
// @ts-ignore - jison doesn't export types
|
import { parser } from './infoParser.js';
|
||||||
import { parser } from './parser/info.jison';
|
|
||||||
import { db } from './infoDb.js';
|
|
||||||
|
|
||||||
describe('info diagram', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
parser.yy = db;
|
|
||||||
parser.yy.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
describe('info', () => {
|
||||||
it('should handle an info definition', () => {
|
it('should handle an info definition', () => {
|
||||||
const str = `info`;
|
const str = `info`;
|
||||||
parser.parse(str);
|
expect(() => {
|
||||||
|
parser.parse(str);
|
||||||
expect(db.getInfo()).toBeFalsy();
|
}).not.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle an info definition with showInfo', () => {
|
it('should handle an info definition with showInfo', () => {
|
||||||
const str = `info showInfo`;
|
const str = `info showInfo`;
|
||||||
parser.parse(str);
|
expect(() => {
|
||||||
|
parser.parse(str);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
expect(db.getInfo()).toBeTruthy();
|
it('should throw because of unsupported info grammar', () => {
|
||||||
|
const str = `info unsupported`;
|
||||||
|
expect(() => {
|
||||||
|
parser.parse(str);
|
||||||
|
}).toThrow('Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw because of unsupported info grammar', () => {
|
||||||
|
const str = `info unsupported`;
|
||||||
|
expect(() => {
|
||||||
|
parser.parse(str);
|
||||||
|
}).toThrow('Parsing failed: unexpected character: ->u<- at offset: 5, skipped 11 characters.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
import type { InfoFields, InfoDB } from './infoTypes.js';
|
import type { InfoFields, InfoDB } from './infoTypes.js';
|
||||||
|
import { version } from '../../../package.json';
|
||||||
|
|
||||||
export const DEFAULT_INFO_DB: InfoFields = {
|
export const DEFAULT_INFO_DB: InfoFields = { version } as const;
|
||||||
info: false,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
let info: boolean = DEFAULT_INFO_DB.info;
|
export const getVersion = (): string => DEFAULT_INFO_DB.version;
|
||||||
|
|
||||||
export const setInfo = (toggle: boolean): void => {
|
|
||||||
info = toggle;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getInfo = (): boolean => info;
|
|
||||||
|
|
||||||
const clear = (): void => {
|
|
||||||
info = DEFAULT_INFO_DB.info;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const db: InfoDB = {
|
export const db: InfoDB = {
|
||||||
clear,
|
getVersion,
|
||||||
setInfo,
|
|
||||||
getInfo,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||||
// @ts-ignore - jison doesn't export types
|
import { parser } from './infoParser.js';
|
||||||
import parser from './parser/info.jison';
|
|
||||||
import { db } from './infoDb.js';
|
import { db } from './infoDb.js';
|
||||||
import { renderer } from './infoRenderer.js';
|
import { renderer } from './infoRenderer.js';
|
||||||
|
|
||||||
|
|||||||
12
packages/mermaid/src/diagrams/info/infoParser.ts
Normal file
12
packages/mermaid/src/diagrams/info/infoParser.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import type { Info } from 'mermaid-parser';
|
||||||
|
import { parse } from 'mermaid-parser';
|
||||||
|
|
||||||
|
import { log } from '../../logger.js';
|
||||||
|
import type { ParserDefinition } from '../../diagram-api/types.js';
|
||||||
|
|
||||||
|
export const parser: ParserDefinition = {
|
||||||
|
parse: (input: string): void => {
|
||||||
|
const ast: Info = parse('info', input);
|
||||||
|
log.debug(ast);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import type { DiagramDB } from '../../diagram-api/types.js';
|
import type { DiagramDB } from '../../diagram-api/types.js';
|
||||||
|
|
||||||
export interface InfoFields {
|
export interface InfoFields {
|
||||||
info: boolean;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InfoDB extends DiagramDB {
|
export interface InfoDB extends DiagramDB {
|
||||||
clear: () => void;
|
getVersion: () => string;
|
||||||
setInfo: (info: boolean) => void;
|
|
||||||
getInfo: () => boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
/** mermaid
|
|
||||||
* https://knsv.github.io/mermaid
|
|
||||||
* (c) 2015 Knut Sveidqvist
|
|
||||||
* MIT license.
|
|
||||||
*/
|
|
||||||
%lex
|
|
||||||
|
|
||||||
%options case-insensitive
|
|
||||||
|
|
||||||
%{
|
|
||||||
// Pre-lexer code can go here
|
|
||||||
%}
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
"info" return 'info' ;
|
|
||||||
[\s\n\r]+ return 'NL' ;
|
|
||||||
[\s]+ return 'space';
|
|
||||||
"showInfo" return 'showInfo';
|
|
||||||
<<EOF>> return 'EOF' ;
|
|
||||||
. return 'TXT' ;
|
|
||||||
|
|
||||||
/lex
|
|
||||||
|
|
||||||
%start start
|
|
||||||
|
|
||||||
%% /* language grammar */
|
|
||||||
|
|
||||||
start
|
|
||||||
// %{ : info document 'EOF' { return yy; } }
|
|
||||||
: info document 'EOF' { return yy; }
|
|
||||||
;
|
|
||||||
|
|
||||||
document
|
|
||||||
: /* empty */
|
|
||||||
| document line
|
|
||||||
;
|
|
||||||
|
|
||||||
line
|
|
||||||
: statement { }
|
|
||||||
| 'NL'
|
|
||||||
;
|
|
||||||
|
|
||||||
statement
|
|
||||||
: showInfo { yy.setInfo(true); }
|
|
||||||
;
|
|
||||||
|
|
||||||
%%
|
|
||||||
@@ -102,7 +102,6 @@ describe('when using mermaid and ', () => {
|
|||||||
parse: (_text) => {
|
parse: (_text) => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
parser: { yy: {} },
|
|
||||||
},
|
},
|
||||||
styles: () => {
|
styles: () => {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ describe('mermaidAPI', () => {
|
|||||||
const config = {
|
const config = {
|
||||||
logLevel: 0,
|
logLevel: 0,
|
||||||
securityLevel: 'loose',
|
securityLevel: 'loose',
|
||||||
};
|
} as const;
|
||||||
mermaidAPI.initialize(config);
|
mermaidAPI.initialize(config);
|
||||||
mermaidAPI.setConfig({ securityLevel: 'strict', logLevel: 1 });
|
mermaidAPI.setConfig({ securityLevel: 'strict', logLevel: 1 });
|
||||||
expect(mermaidAPI.getConfig().logLevel).toBe(1);
|
expect(mermaidAPI.getConfig().logLevel).toBe(1);
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ properties:
|
|||||||
meta:enum:
|
meta:enum:
|
||||||
'null': Can be set to disable any pre-defined mermaid theme
|
'null': Can be set to disable any pre-defined mermaid theme
|
||||||
default: 'default'
|
default: 'default'
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "default" | "forest" | "dark" | "neutral" | "null"'
|
|
||||||
themeVariables:
|
themeVariables:
|
||||||
tsType: any
|
tsType: any
|
||||||
themeCSS:
|
themeCSS:
|
||||||
@@ -115,8 +113,6 @@ properties:
|
|||||||
error: Equivalent to 4
|
error: Equivalent to 4
|
||||||
fatal: Equivalent to 5 (default)
|
fatal: Equivalent to 5 (default)
|
||||||
default: 5
|
default: 5
|
||||||
# Allow any number or string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'number | string | 0 | 2 | 1 | "trace" | "debug" | "info" | "warn" | "error" | "fatal" | 3 | 4 | 5 | undefined'
|
|
||||||
securityLevel:
|
securityLevel:
|
||||||
description: Level of trust for parsed diagram
|
description: Level of trust for parsed diagram
|
||||||
type: string
|
type: string
|
||||||
@@ -134,8 +130,6 @@ properties:
|
|||||||
This prevent any JavaScript from running in the context.
|
This prevent any JavaScript from running in the context.
|
||||||
This may hinder interactive functionality of the diagram, like scripts, popups in the sequence diagram, or links to other tabs or targets, etc.
|
This may hinder interactive functionality of the diagram, like scripts, popups in the sequence diagram, or links to other tabs or targets, etc.
|
||||||
default: strict
|
default: strict
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "strict" | "loose" | "antiscript" | "sandbox" | undefined'
|
|
||||||
startOnLoad:
|
startOnLoad:
|
||||||
description: Dictates whether mermaid starts on Page load
|
description: Dictates whether mermaid starts on Page load
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -1021,8 +1015,6 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
LR: Left-Right
|
LR: Left-Right
|
||||||
RL: Right to Left
|
RL: Right to Left
|
||||||
default: TB
|
default: TB
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "TB" | "BT" | "LR" | "RL"'
|
|
||||||
minEntityWidth:
|
minEntityWidth:
|
||||||
description: The minimum width of an entity box. Expressed in pixels.
|
description: The minimum width of an entity box. Expressed in pixels.
|
||||||
type: integer
|
type: integer
|
||||||
@@ -1135,8 +1127,6 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
dagre-d3: The [dagre-d3-es](https://www.npmjs.com/package/dagre-d3-es) library.
|
dagre-d3: The [dagre-d3-es](https://www.npmjs.com/package/dagre-d3-es) library.
|
||||||
dagre-wrapper: wrapper for dagre implemented in mermaid
|
dagre-wrapper: wrapper for dagre implemented in mermaid
|
||||||
elk: Layout using [elkjs](https://github.com/kieler/elkjs)
|
elk: Layout using [elkjs](https://github.com/kieler/elkjs)
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "dagre-d3" | "dagre-wrapper" | "elk"'
|
|
||||||
|
|
||||||
ClassDiagramConfig:
|
ClassDiagramConfig:
|
||||||
title: Class Diagram Config
|
title: Class Diagram Config
|
||||||
@@ -1252,8 +1242,6 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
- center
|
- center
|
||||||
- right
|
- right
|
||||||
default: center
|
default: center
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "left" | "center" | "right"'
|
|
||||||
bottomMarginAdj:
|
bottomMarginAdj:
|
||||||
description: |
|
description: |
|
||||||
Prolongs the edge of the diagram downwards.
|
Prolongs the edge of the diagram downwards.
|
||||||
@@ -1378,8 +1366,6 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
- center
|
- center
|
||||||
- right
|
- right
|
||||||
default: center
|
default: center
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "left" | "center" | "right"'
|
|
||||||
bottomMarginAdj:
|
bottomMarginAdj:
|
||||||
description: |
|
description: |
|
||||||
Prolongs the edge of the diagram downwards.
|
Prolongs the edge of the diagram downwards.
|
||||||
@@ -1543,13 +1529,10 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
meta:enum:
|
meta:enum:
|
||||||
compact: Enables displaying multiple tasks on the same row.
|
compact: Enables displaying multiple tasks on the same row.
|
||||||
default: ''
|
default: ''
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "compact"'
|
|
||||||
weekday:
|
weekday:
|
||||||
description: |
|
description: |
|
||||||
On which day a week-based interval should start
|
On which day a week-based interval should start
|
||||||
type: string
|
type: string
|
||||||
tsType: '"monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"'
|
|
||||||
enum:
|
enum:
|
||||||
- monday
|
- monday
|
||||||
- tuesday
|
- tuesday
|
||||||
@@ -1691,8 +1674,6 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
type: string
|
type: string
|
||||||
enum: ['left', 'center', 'right']
|
enum: ['left', 'center', 'right']
|
||||||
default: 'center'
|
default: 'center'
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "left" | "center" | "right"'
|
|
||||||
|
|
||||||
messageFontSize:
|
messageFontSize:
|
||||||
description: This sets the font size of actor messages
|
description: This sets the font size of actor messages
|
||||||
@@ -1780,8 +1761,6 @@ $defs: # JSON Schema definition (maybe we should move these to a seperate file)
|
|||||||
type: string
|
type: string
|
||||||
enum: ['basis', 'linear', 'cardinal']
|
enum: ['basis', 'linear', 'cardinal']
|
||||||
default: 'basis'
|
default: 'basis'
|
||||||
# Allow any string for typescript backwards compatibility (fix in Mermaid v10)
|
|
||||||
tsType: 'string | "basis" | "linear" | "cardinal"'
|
|
||||||
padding:
|
padding:
|
||||||
description: |
|
description: |
|
||||||
Represents the padding between the labels and the shape
|
Represents the padding between the labels and the shape
|
||||||
|
|||||||
@@ -286,56 +286,46 @@ describe('when formatting urls', function () {
|
|||||||
it('should handle links', function () {
|
it('should handle links', function () {
|
||||||
const url = 'https://mermaid-js.github.io/mermaid/#/';
|
const url = 'https://mermaid-js.github.io/mermaid/#/';
|
||||||
|
|
||||||
const config = { securityLevel: 'loose' };
|
let result = utils.formatUrl(url, { securityLevel: 'loose' });
|
||||||
let result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
config.securityLevel = 'strict';
|
result = utils.formatUrl(url, { securityLevel: 'strict' });
|
||||||
result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
});
|
});
|
||||||
it('should handle anchors', function () {
|
it('should handle anchors', function () {
|
||||||
const url = '#interaction';
|
const url = '#interaction';
|
||||||
|
|
||||||
const config = { securityLevel: 'loose' };
|
let result = utils.formatUrl(url, { securityLevel: 'loose' });
|
||||||
let result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
config.securityLevel = 'strict';
|
result = utils.formatUrl(url, { securityLevel: 'strict' });
|
||||||
result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
});
|
});
|
||||||
it('should handle mailto', function () {
|
it('should handle mailto', function () {
|
||||||
const url = 'mailto:user@user.user';
|
const url = 'mailto:user@user.user';
|
||||||
|
|
||||||
const config = { securityLevel: 'loose' };
|
let result = utils.formatUrl(url, { securityLevel: 'loose' });
|
||||||
let result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
config.securityLevel = 'strict';
|
result = utils.formatUrl(url, { securityLevel: 'strict' });
|
||||||
result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
});
|
});
|
||||||
it('should handle other protocols', function () {
|
it('should handle other protocols', function () {
|
||||||
const url = 'notes://do-your-thing/id';
|
const url = 'notes://do-your-thing/id';
|
||||||
|
|
||||||
const config = { securityLevel: 'loose' };
|
let result = utils.formatUrl(url, { securityLevel: 'loose' });
|
||||||
let result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
config.securityLevel = 'strict';
|
result = utils.formatUrl(url, { securityLevel: 'strict' });
|
||||||
result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
});
|
});
|
||||||
it('should handle scripts', function () {
|
it('should handle scripts', function () {
|
||||||
const url = 'javascript:alert("test")';
|
const url = 'javascript:alert("test")';
|
||||||
|
|
||||||
const config = { securityLevel: 'loose' };
|
let result = utils.formatUrl(url, { securityLevel: 'loose' });
|
||||||
let result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual(url);
|
expect(result).toEqual(url);
|
||||||
|
|
||||||
config.securityLevel = 'strict';
|
result = utils.formatUrl(url, { securityLevel: 'strict' });
|
||||||
result = utils.formatUrl(url, config);
|
|
||||||
expect(result).toEqual('about:blank');
|
expect(result).toEqual('about:blank');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
21
packages/parser/LICENSE
Normal file
21
packages/parser/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2023 Yokozuna59
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
63
packages/parser/README.md
Normal file
63
packages/parser/README.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/mermaid-js/mermaid/develop/docs/public/favicon.svg" height="150">
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<h1 align="center">
|
||||||
|
Mermaid Parser
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
Mermaid parser package
|
||||||
|
<p>
|
||||||
|
|
||||||
|
[](https://www.npmjs.com/package/mermaid-parser)
|
||||||
|
|
||||||
|
## How the package works
|
||||||
|
|
||||||
|
The package exports a `parse` function that has two parameters:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
declare function parse<T extends DiagramAST>(
|
||||||
|
diagramType: keyof typeof initializers,
|
||||||
|
text: string
|
||||||
|
): T;
|
||||||
|
```
|
||||||
|
|
||||||
|
## How does a Langium-based parser work?
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
actor Package
|
||||||
|
participant Module
|
||||||
|
participant TokenBuilder
|
||||||
|
participant Lexer
|
||||||
|
participant Parser
|
||||||
|
participant ValueConverter
|
||||||
|
|
||||||
|
|
||||||
|
Package ->> Module: Create services
|
||||||
|
Module ->> TokenBuilder: Override or/and<br>reorder rules
|
||||||
|
TokenBuilder ->> Lexer: Read the string and transform<br>it into a token stream
|
||||||
|
Lexer ->> Parser: Parse token<br>stream into AST
|
||||||
|
Parser ->> ValueConverter: Clean/modify tokenized<br>rules returned value
|
||||||
|
ValueConverter -->> Package: Return AST
|
||||||
|
```
|
||||||
|
|
||||||
|
- When to override `TokenBuilder`?
|
||||||
|
|
||||||
|
- To override keyword rules.
|
||||||
|
- To override terminal rules that need a custom function.
|
||||||
|
- To manually reorder the list of rules.
|
||||||
|
|
||||||
|
- When to override `Lexer`?
|
||||||
|
|
||||||
|
- To modify input before tokenizing.
|
||||||
|
- To insert/modify tokens that cannot or have not been parsed.
|
||||||
|
|
||||||
|
- When to override `LangiumParser`?
|
||||||
|
|
||||||
|
- To insert or modify attributes that can't be parsed.
|
||||||
|
|
||||||
|
- When to override `ValueConverter`?
|
||||||
|
|
||||||
|
- To modify the returned value from the parser.
|
||||||
13
packages/parser/langium-config.json
Normal file
13
packages/parser/langium-config.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"projectName": "Mermaid",
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"id": "info",
|
||||||
|
"grammar": "src/language/info/info.langium",
|
||||||
|
"fileExtensions": [".mmd", ".mermaid"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mode": "production",
|
||||||
|
"importExtension": ".js",
|
||||||
|
"out": "src/language/generated"
|
||||||
|
}
|
||||||
48
packages/parser/package.json
Normal file
48
packages/parser/package.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "mermaid-parser",
|
||||||
|
"version": "0.2.0",
|
||||||
|
"description": "MermaidJS parser",
|
||||||
|
"author": "Yokozuna59",
|
||||||
|
"contributors": [
|
||||||
|
"Yokozuna59",
|
||||||
|
"Sidharth Vinod (https://sidharth.dev)"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/mermaid-js/mermaid/tree/develop/packages/mermaid/parser/#readme",
|
||||||
|
"types": "dist/src/index.d.ts",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/mermaid-parser.esm.mjs",
|
||||||
|
"types": "./dist/src/index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rimraf dist src/language/generated",
|
||||||
|
"langium:generate": "langium generate",
|
||||||
|
"langium:watch": "langium generate --watch",
|
||||||
|
"prepublishOnly": "pnpm -w run build"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mermaid-js/mermaid.git",
|
||||||
|
"directory": "packages/parser"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"mermaid",
|
||||||
|
"parser",
|
||||||
|
"ast"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"langium": "2.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"langium-cli": "2.0.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
packages/parser/src/index.ts
Normal file
3
packages/parser/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export type { Info } from './language/index.js';
|
||||||
|
export type { DiagramAST } from './parse.js';
|
||||||
|
export { parse, MermaidParseError } from './parse.js';
|
||||||
51
packages/parser/src/language/chevrotainWrapper.ts
Normal file
51
packages/parser/src/language/chevrotainWrapper.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
type CustomPatternMatcherReturn = [string] & { payload?: any };
|
||||||
|
|
||||||
|
export type CustomPatternMatcherFunc = (
|
||||||
|
text: string,
|
||||||
|
offset: number,
|
||||||
|
tokens: IToken[],
|
||||||
|
groups: {
|
||||||
|
[groupName: string]: IToken[];
|
||||||
|
}
|
||||||
|
) => CustomPatternMatcherReturn | RegExpExecArray | null;
|
||||||
|
|
||||||
|
interface ICustomPattern {
|
||||||
|
exec: CustomPatternMatcherFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenPattern = RegExp | string | CustomPatternMatcherFunc | ICustomPattern;
|
||||||
|
|
||||||
|
export interface IToken {
|
||||||
|
image: string;
|
||||||
|
startOffset: number;
|
||||||
|
startLine?: number;
|
||||||
|
startColumn?: number;
|
||||||
|
endOffset?: number;
|
||||||
|
endLine?: number;
|
||||||
|
endColumn?: number;
|
||||||
|
isInsertedInRecovery?: boolean;
|
||||||
|
tokenTypeIdx: number;
|
||||||
|
tokenType: TokenType;
|
||||||
|
payload?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenType {
|
||||||
|
name: string;
|
||||||
|
GROUP?: string;
|
||||||
|
PATTERN?: TokenPattern;
|
||||||
|
LABEL?: string;
|
||||||
|
LONGER_ALT?: TokenType | TokenType[];
|
||||||
|
POP_MODE?: boolean;
|
||||||
|
PUSH_MODE?: string;
|
||||||
|
LINE_BREAKS?: boolean;
|
||||||
|
CATEGORIES?: TokenType[];
|
||||||
|
tokenTypeIdx?: number;
|
||||||
|
categoryMatches?: number[];
|
||||||
|
categoryMatchesMap?: {
|
||||||
|
[tokType: number]: boolean;
|
||||||
|
};
|
||||||
|
isParent?: boolean;
|
||||||
|
START_CHARS_HINT?: (string | number)[];
|
||||||
|
}
|
||||||
14
packages/parser/src/language/common/common.langium
Normal file
14
packages/parser/src/language/common/common.langium
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
fragment TitleAndAccessibilities:
|
||||||
|
((accDescr=ACC_DESCR | accTitle=ACC_TITLE | title=TITLE) NEWLINE+)+
|
||||||
|
;
|
||||||
|
|
||||||
|
terminal NEWLINE: /\r?\n/;
|
||||||
|
terminal ACC_DESCR: /accDescr(?:[\t ]*:[\t ]*[^\n\r]*?(?=%%)|\s*{[^}]*})|accDescr(?:[\t ]*:[\t ]*[^\n\r]*|\s*{[^}]*})/;
|
||||||
|
terminal ACC_TITLE: /accTitle[\t ]*:[\t ]*[^\n\r]*?(?=%%)|accTitle[\t ]*:[\t ]*[^\n\r]*/;
|
||||||
|
terminal TITLE: /title(?:[\t ]+[^\n\r]*?|)(?=%%)|title(?:[\t ]+[^\n\r]*|)/;
|
||||||
|
|
||||||
|
hidden terminal WHITESPACE: /[\t ]+/;
|
||||||
|
// TODO: add YAML_COMMENT hidden rule without interfere actual grammar
|
||||||
|
hidden terminal YAML: /---[\t ]*\r?\n[\S\s]*?---[\t ]*(?!.)/;
|
||||||
|
hidden terminal DIRECTIVE: /[\t ]*%%{[\S\s]*?}%%\s*/;
|
||||||
|
hidden terminal SINGLE_LINE_COMMENT: /[\t ]*%%[^\n\r]*/;
|
||||||
8
packages/parser/src/language/common/commonLexer.ts
Normal file
8
packages/parser/src/language/common/commonLexer.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { LexerResult } from 'langium';
|
||||||
|
import { DefaultLexer } from 'langium';
|
||||||
|
|
||||||
|
export class CommonLexer extends DefaultLexer {
|
||||||
|
public override tokenize(text: string): LexerResult {
|
||||||
|
return super.tokenize(text + '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
14
packages/parser/src/language/common/commonMatcher.ts
Normal file
14
packages/parser/src/language/common/commonMatcher.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Matches single and multiline accessible description
|
||||||
|
*/
|
||||||
|
export const accessibilityDescrRegex = /accDescr(?:[\t ]*:[\t ]*([^\n\r]*)|\s*{([^}]*)})/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches single line accessible title
|
||||||
|
*/
|
||||||
|
export const accessibilityTitleRegex = /accTitle[\t ]*:[\t ]*([^\n\r]*)/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a single line title
|
||||||
|
*/
|
||||||
|
export const titleRegex = /title([\t ]+([^\n\r]*)|)/;
|
||||||
74
packages/parser/src/language/common/commonValueConverters.ts
Normal file
74
packages/parser/src/language/common/commonValueConverters.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import type { CstNode, GrammarAST, ValueType } from 'langium';
|
||||||
|
import { DefaultValueConverter } from 'langium';
|
||||||
|
|
||||||
|
import { accessibilityDescrRegex, accessibilityTitleRegex, titleRegex } from './commonMatcher.js';
|
||||||
|
|
||||||
|
export class CommonValueConverter extends DefaultValueConverter {
|
||||||
|
protected override runConverter(
|
||||||
|
rule: GrammarAST.AbstractRule,
|
||||||
|
input: string,
|
||||||
|
cstNode: CstNode
|
||||||
|
): ValueType {
|
||||||
|
const value: ValueType | undefined = CommonValueConverter.customRunConverter(
|
||||||
|
rule,
|
||||||
|
input,
|
||||||
|
cstNode
|
||||||
|
);
|
||||||
|
if (value === undefined) {
|
||||||
|
return super.runConverter(rule, input, cstNode);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method contains convert logic to be used by class itself or `MermaidValueConverter`.
|
||||||
|
*
|
||||||
|
* @param rule - Parsed rule.
|
||||||
|
* @param input - Matched string.
|
||||||
|
* @param _cstNode - Node in the Concrete Syntax Tree (CST).
|
||||||
|
* @returns converted the value if it's common rule or `undefined` if it's not.
|
||||||
|
*/
|
||||||
|
public static customRunConverter(
|
||||||
|
rule: GrammarAST.AbstractRule,
|
||||||
|
input: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
_cstNode: CstNode
|
||||||
|
): ValueType | undefined {
|
||||||
|
let regex: RegExp | undefined;
|
||||||
|
switch (rule.name) {
|
||||||
|
case 'ACC_DESCR': {
|
||||||
|
regex = new RegExp(accessibilityDescrRegex.source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ACC_TITLE': {
|
||||||
|
regex = new RegExp(accessibilityTitleRegex.source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'TITLE': {
|
||||||
|
regex = new RegExp(titleRegex.source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (regex === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const match = regex.exec(input);
|
||||||
|
if (match === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// single line title, accTitle, accDescr
|
||||||
|
if (match[1] !== undefined) {
|
||||||
|
return match[1].trim().replaceAll(/[\t ]{2,}/gm, ' ');
|
||||||
|
}
|
||||||
|
// multi line accDescr
|
||||||
|
if (match[2] !== undefined) {
|
||||||
|
return match[2]
|
||||||
|
.replaceAll(/^\s*/gm, '')
|
||||||
|
.replaceAll(/\s+$/gm, '')
|
||||||
|
.replaceAll(/[\t ]{2,}/gm, ' ')
|
||||||
|
.replaceAll(/[\n\r]{2,}/gm, '\n');
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/parser/src/language/common/index.ts
Normal file
2
packages/parser/src/language/common/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './commonLexer.js';
|
||||||
|
export * from './commonValueConverters.js';
|
||||||
6
packages/parser/src/language/index.ts
Normal file
6
packages/parser/src/language/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export * from './generated/ast.js';
|
||||||
|
export * from './generated/grammar.js';
|
||||||
|
export * from './generated/module.js';
|
||||||
|
|
||||||
|
export * from './common/index.js';
|
||||||
|
export * from './info/index.js';
|
||||||
1
packages/parser/src/language/info/index.ts
Normal file
1
packages/parser/src/language/info/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './infoModule.js';
|
||||||
9
packages/parser/src/language/info/info.langium
Normal file
9
packages/parser/src/language/info/info.langium
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
grammar Info
|
||||||
|
import "../common/common";
|
||||||
|
|
||||||
|
entry Info:
|
||||||
|
NEWLINE*
|
||||||
|
"info" NEWLINE*
|
||||||
|
("showInfo" NEWLINE*)?
|
||||||
|
TitleAndAccessibilities?
|
||||||
|
;
|
||||||
72
packages/parser/src/language/info/infoModule.ts
Normal file
72
packages/parser/src/language/info/infoModule.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import type {
|
||||||
|
DefaultSharedModuleContext,
|
||||||
|
LangiumServices,
|
||||||
|
LangiumSharedServices,
|
||||||
|
Module,
|
||||||
|
PartialLangiumServices,
|
||||||
|
} from 'langium';
|
||||||
|
import { EmptyFileSystem, createDefaultModule, createDefaultSharedModule, inject } from 'langium';
|
||||||
|
|
||||||
|
import { MermaidGeneratedSharedModule, InfoGeneratedModule } from '../generated/module.js';
|
||||||
|
import { CommonLexer } from '../common/commonLexer.js';
|
||||||
|
import { CommonValueConverter } from '../common/commonValueConverters.js';
|
||||||
|
import { InfoTokenBuilder } from './infoTokenBuilder.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declaration of `Info` services.
|
||||||
|
*/
|
||||||
|
type InfoAddedServices = {
|
||||||
|
parser: {
|
||||||
|
Lexer: CommonLexer;
|
||||||
|
TokenBuilder: InfoTokenBuilder;
|
||||||
|
ValueConverter: CommonValueConverter;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union of Langium default services and `Info` services.
|
||||||
|
*/
|
||||||
|
export type InfoServices = LangiumServices & InfoAddedServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injection module that overrides Langium default services and
|
||||||
|
* contributes the declared `Info` services.
|
||||||
|
*/
|
||||||
|
export const InfoModule: Module<InfoServices, PartialLangiumServices & InfoAddedServices> = {
|
||||||
|
parser: {
|
||||||
|
Lexer: (services) => new CommonLexer(services),
|
||||||
|
TokenBuilder: () => new InfoTokenBuilder(),
|
||||||
|
ValueConverter: () => new CommonValueConverter(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the full set of services required by Langium.
|
||||||
|
*
|
||||||
|
* First inject the shared services by merging two modules:
|
||||||
|
* - Langium default shared services
|
||||||
|
* - Services generated by langium-cli
|
||||||
|
*
|
||||||
|
* Then inject the language-specific services by merging three modules:
|
||||||
|
* - Langium default language-specific services
|
||||||
|
* - Services generated by langium-cli
|
||||||
|
* - Services specified in this file
|
||||||
|
* @param context - Optional module context with the LSP connection
|
||||||
|
* @returns An object wrapping the shared services and the language-specific services
|
||||||
|
*/
|
||||||
|
export function createInfoServices(context: DefaultSharedModuleContext = EmptyFileSystem): {
|
||||||
|
shared: LangiumSharedServices;
|
||||||
|
Info: InfoServices;
|
||||||
|
} {
|
||||||
|
const shared: LangiumSharedServices = inject(
|
||||||
|
createDefaultSharedModule(context),
|
||||||
|
MermaidGeneratedSharedModule
|
||||||
|
);
|
||||||
|
const Info: InfoServices = inject(
|
||||||
|
createDefaultModule({ shared }),
|
||||||
|
InfoGeneratedModule,
|
||||||
|
InfoModule
|
||||||
|
);
|
||||||
|
shared.ServiceRegistry.register(Info);
|
||||||
|
return { shared, Info };
|
||||||
|
}
|
||||||
24
packages/parser/src/language/info/infoTokenBuilder.ts
Normal file
24
packages/parser/src/language/info/infoTokenBuilder.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import type { GrammarAST, Stream, TokenBuilderOptions } from 'langium';
|
||||||
|
import { DefaultTokenBuilder } from 'langium';
|
||||||
|
|
||||||
|
import type { TokenType } from '../chevrotainWrapper.js';
|
||||||
|
|
||||||
|
export class InfoTokenBuilder extends DefaultTokenBuilder {
|
||||||
|
protected override buildKeywordTokens(
|
||||||
|
rules: Stream<GrammarAST.AbstractRule>,
|
||||||
|
terminalTokens: TokenType[],
|
||||||
|
options?: TokenBuilderOptions
|
||||||
|
): TokenType[] {
|
||||||
|
const tokenTypes: TokenType[] = super.buildKeywordTokens(rules, terminalTokens, options);
|
||||||
|
// to restrict users, they mustn't have any non-whitespace characters after the keyword.
|
||||||
|
tokenTypes.forEach((tokenType: TokenType): void => {
|
||||||
|
if (
|
||||||
|
(tokenType.name === 'info' || tokenType.name === 'showInfo') &&
|
||||||
|
tokenType.PATTERN !== undefined
|
||||||
|
) {
|
||||||
|
tokenType.PATTERN = new RegExp(tokenType.PATTERN.toString() + '(?!\\S)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tokenTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
packages/parser/src/parse.ts
Normal file
43
packages/parser/src/parse.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import type { LangiumParser, ParseResult } from 'langium';
|
||||||
|
import type { Info } from './index.js';
|
||||||
|
import { createInfoServices } from './language/index.js';
|
||||||
|
|
||||||
|
export type DiagramAST = Info;
|
||||||
|
|
||||||
|
const parsers: Record<string, LangiumParser> = {};
|
||||||
|
|
||||||
|
const initializers = {
|
||||||
|
info: () => {
|
||||||
|
// Will have to make parse async to use this. Can try later...
|
||||||
|
// const { createInfoServices } = await import('./language/info/index.js');
|
||||||
|
const parser = createInfoServices().Info.parser.LangiumParser;
|
||||||
|
parsers['info'] = parser;
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
export function parse(diagramType: 'info', text: string): Info;
|
||||||
|
export function parse<T extends DiagramAST>(
|
||||||
|
diagramType: keyof typeof initializers,
|
||||||
|
text: string
|
||||||
|
): T {
|
||||||
|
const initializer = initializers[diagramType];
|
||||||
|
if (!initializer) {
|
||||||
|
throw new Error(`Unknown diagram type: ${diagramType}`);
|
||||||
|
}
|
||||||
|
if (!parsers[diagramType]) {
|
||||||
|
initializer();
|
||||||
|
}
|
||||||
|
const parser: LangiumParser = parsers[diagramType];
|
||||||
|
const result: ParseResult<T> = parser.parse<T>(text);
|
||||||
|
if (result.lexerErrors.length > 0 || result.parserErrors.length > 0) {
|
||||||
|
throw new MermaidParseError(result);
|
||||||
|
}
|
||||||
|
return result.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MermaidParseError extends Error {
|
||||||
|
constructor(public result: ParseResult<DiagramAST>) {
|
||||||
|
const lexerErrors: string = result.lexerErrors.map((err) => err.message).join('\n');
|
||||||
|
const parserErrors: string = result.parserErrors.map((err) => err.message).join('\n');
|
||||||
|
super(`Parsing failed: ${lexerErrors} ${parserErrors}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
packages/parser/tests/info.test.ts
Normal file
57
packages/parser/tests/info.test.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import type { LangiumParser, ParseResult } from 'langium';
|
||||||
|
|
||||||
|
import type { InfoServices } from '../src/language/index.js';
|
||||||
|
import { Info, createInfoServices } from '../src/language/index.js';
|
||||||
|
import { noErrorsOrAlternatives } from './test-util.js';
|
||||||
|
|
||||||
|
const services: InfoServices = createInfoServices().Info;
|
||||||
|
const parser: LangiumParser = services.parser.LangiumParser;
|
||||||
|
function createInfoTestServices(): {
|
||||||
|
services: InfoServices;
|
||||||
|
parse: (input: string) => ParseResult<Info>;
|
||||||
|
} {
|
||||||
|
const parse = (input: string) => {
|
||||||
|
return parser.parse<Info>(input);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { services, parse };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('info', () => {
|
||||||
|
const { parse } = createInfoTestServices();
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
`info`,
|
||||||
|
`
|
||||||
|
info`,
|
||||||
|
`info
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
info
|
||||||
|
`,
|
||||||
|
])('should handle empty info', (context: string) => {
|
||||||
|
const result = parse(context);
|
||||||
|
noErrorsOrAlternatives(result);
|
||||||
|
|
||||||
|
expect(result.value.$type).toBe(Info);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
`info showInfo`,
|
||||||
|
`
|
||||||
|
info showInfo`,
|
||||||
|
`info
|
||||||
|
showInfo
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
info
|
||||||
|
showInfo
|
||||||
|
`,
|
||||||
|
])('should handle showInfo', (context: string) => {
|
||||||
|
const result = parse(context);
|
||||||
|
noErrorsOrAlternatives(result);
|
||||||
|
|
||||||
|
expect(result.value.$type).toBe(Info);
|
||||||
|
});
|
||||||
|
});
|
||||||
18
packages/parser/tests/test-util.ts
Normal file
18
packages/parser/tests/test-util.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { expect, vi } from 'vitest';
|
||||||
|
import type { ParseResult } from 'langium';
|
||||||
|
|
||||||
|
const consoleMock = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper test function that validate that the result doesn't have errors
|
||||||
|
* or any ambiguous alternatives from chevrotain.
|
||||||
|
*
|
||||||
|
* @param result - the result `parse` function.
|
||||||
|
*/
|
||||||
|
export function noErrorsOrAlternatives(result: ParseResult) {
|
||||||
|
expect(result.lexerErrors).toHaveLength(0);
|
||||||
|
expect(result.parserErrors).toHaveLength(0);
|
||||||
|
|
||||||
|
expect(consoleMock).not.toHaveBeenCalled();
|
||||||
|
consoleMock.mockReset();
|
||||||
|
}
|
||||||
12
packages/parser/tsconfig.json
Normal file
12
packages/parser/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": ".",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"allowJs": false,
|
||||||
|
"preserveSymlinks": false,
|
||||||
|
"strictPropertyInitialization": false
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*.ts", "./tests/**/*.ts"],
|
||||||
|
"typeRoots": ["./src/types"]
|
||||||
|
}
|
||||||
6585
pnpm-lock.yaml
generated
6585
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Fail on errors
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
|
||||||
|
# Increase heap size
|
||||||
|
export NODE_OPTIONS="--max_old_space_size=4096"
|
||||||
|
|
||||||
|
# Build mermaid
|
||||||
pnpm build
|
pnpm build
|
||||||
|
|
||||||
# Clone the Mermaid Live Editor repository
|
# Clone the Mermaid Live Editor repository
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import jison from './.vite/jisonPlugin.js';
|
import jison from './.vite/jisonPlugin.js';
|
||||||
import jsonSchemaPlugin from './.vite/jsonSchemaPlugin.js';
|
import jsonSchemaPlugin from './.vite/jsonSchemaPlugin.js';
|
||||||
import typescript from '@rollup/plugin-typescript';
|
import typescript from '@rollup/plugin-typescript';
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defaultExclude, defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -22,7 +22,7 @@ export default defineConfig({
|
|||||||
provider: 'v8',
|
provider: 'v8',
|
||||||
reporter: ['text', 'json', 'html', 'lcov'],
|
reporter: ['text', 'json', 'html', 'lcov'],
|
||||||
reportsDirectory: './coverage/vitest',
|
reportsDirectory: './coverage/vitest',
|
||||||
exclude: ['**/node_modules/**', '**/tests/**', '**/__mocks__/**'],
|
exclude: [...defaultExclude, './tests/**', '**/__mocks__/**', '**/generated/'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
|||||||
Reference in New Issue
Block a user