Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move ELK to standalone package #5049

Merged
merged 13 commits into from
Nov 24, 2023
Merged
5 changes: 5 additions & 0 deletions .build/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ export const packageOptions = {
packageName: 'mermaid-zenuml',
file: 'detector.ts',
},
'mermaid-flowchart-elk': {
name: 'mermaid-flowchart-elk',
packageName: 'mermaid-flowchart-elk',
file: 'detector.ts',
},
} as const;
16 changes: 16 additions & 0 deletions .build/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { packageOptions } from './common.js';
import { execSync } from 'child_process';

const buildType = (packageName: string) => {
console.log(`Building types for ${packageName}`);
try {
const out = execSync(`tsc -p ./packages/${packageName}/tsconfig.json --emitDeclarationOnly`);
out.length > 0 && console.log(out.toString());
} catch (e) {
console.error(e);
}
};

for (const { packageName } of Object.values(packageOptions)) {
buildType(packageName);
}
48 changes: 17 additions & 31 deletions .esbuild/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,25 @@ import { getBuildConfig, defaultOptions } from './util.js';
import { context } from 'esbuild';
import chokidar from 'chokidar';
import { generateLangium } from '../.build/generateLangium.js';
import { packageOptions } from '../.build/common.js';

const parserCtx = await context(
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'parser' })
const configs = Object.values(packageOptions).map(({ packageName }) =>
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName })
);
const mermaidCtx = await context(
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' })
);
const mermaidIIFECtx = await context(
getBuildConfig({
...defaultOptions,
minify: false,
core: false,
entryName: 'mermaid',
format: 'iife',
})
);
const externalCtx = await context(
getBuildConfig({
...defaultOptions,
minify: false,
core: false,
entryName: 'mermaid-example-diagram',
})
);
const zenumlCtx = await context(
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' })
);
const contexts = [parserCtx, mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx];
const mermaidIIFEConfig = getBuildConfig({
...defaultOptions,
minify: false,
core: false,
entryName: 'mermaid',
format: 'iife',
});
configs.push(mermaidIIFEConfig);

const contexts = await Promise.all(configs.map((config) => context(config)));

const rebuildAll = async () => {
console.time('Rebuild time');
await Promise.all(contexts.map((ctx) => ctx.rebuild()));
await Promise.all(contexts.map((ctx) => ctx.rebuild())).catch((e) => console.error(e));
console.timeEnd('Rebuild time');
};

Expand Down Expand Up @@ -101,10 +88,9 @@ async function createServer() {

app.use(cors());
app.get('/events', eventsHandler);
app.use(express.static('./packages/parser/dist'));
app.use(express.static('./packages/mermaid/dist'));
app.use(express.static('./packages/mermaid-zenuml/dist'));
app.use(express.static('./packages/mermaid-example-diagram/dist'));
for (const { packageName } of Object.values(packageOptions)) {
app.use(express.static(`./packages/${packageName}/dist`));
}
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));

Expand Down
1 change: 1 addition & 0 deletions .github/codecov.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ coverage:
# Turing off for now as code coverage isn't stable and causes unnecessary build failures.
# default:
# threshold: 2%
patch: off
8 changes: 4 additions & 4 deletions .vite/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express from 'express';
import cors from 'cors';
import { createServer as createViteServer } from 'vite';
import { packageOptions } from '../.build/common.js';

async function createServer() {
const app = express();
Expand All @@ -14,10 +15,9 @@ async function createServer() {
});

app.use(cors());
app.use(express.static('./packages/parser/dist'));
app.use(express.static('./packages/mermaid/dist'));
app.use(express.static('./packages/mermaid-zenuml/dist'));
app.use(express.static('./packages/mermaid-example-diagram/dist'));
for (const { packageName } of Object.values(packageOptions)) {
app.use(express.static(`./packages/${packageName}/dist`));
}
app.use(vite.middlewares);
app.use(express.static('demos'));
app.use(express.static('cypress/platform'));
Expand Down
14 changes: 14 additions & 0 deletions cypress/integration/other/flowchart-elk.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { urlSnapshotTest, openURLAndVerifyRendering } from '../../helpers/util.ts';

describe('Flowchart elk', () => {
it('should use dagre as fallback', () => {
urlSnapshotTest('http://localhost:9000/flow-elk.html', {
name: 'flow-elk fallback to dagre',
});
});
it('should allow overriding with external package', () => {
urlSnapshotTest('http://localhost:9000/flow-elk.html?elk=true', {
name: 'flow-elk overriding dagre with elk',
});
});
});
28 changes: 28 additions & 0 deletions cypress/platform/flow-elk.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<html>
<body>
<pre class="mermaid">
flowchart-elk
a[hello] --> b[world]
b --> c{test}
c --> one
c --> two
c --> three
</pre>

<script type="module">
import mermaid from './mermaid.esm.mjs';
import elk from './mermaid-flowchart-elk.esm.min.mjs';
if (window.location.search.includes('elk')) {
await mermaid.registerExternalDiagrams([elk]);
}
mermaid.initialize({
logLevel: 3,
startOnLoad: false,
});
await mermaid.run();
if (window.Cypress) {
window.rendered = true;
}
</script>
</body>
</html>
35 changes: 35 additions & 0 deletions demos/flowchart-elk.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mermaid Flowchart ELK Test Page</title>
</head>

<body>
<h1>Flowchart ELK</h1>
<pre class="mermaid">
flowchart-elk TD
A([Start]) ==> B[Step 1]
B ==> C{Flow 1}
C -- Choice 1.1 --> D[Step 2.1]
C -- Choice 1.3 --> I[Step 2.3]
C == Choice 1.2 ==> E[Step 2.2]
D --> F{Flow 2}
E ==> F{Flow 2}
F{Flow 2} == Choice 2.1 ==> H[Feedback node]
H[Feedback node] ==> B[Step 1]
F{Flow 2} == Choice 2.2 ==> G((Finish))

</pre>

<script type="module">
import mermaid from './mermaid.esm.mjs';
import flowchartELK from './mermaid-flowchart-elk.esm.mjs';
await mermaid.registerExternalDiagrams([flowchartELK]);
mermaid.initialize({
logLevel: 3,
});
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"build:esbuild": "pnpm run -r clean && ts-node-esm --transpileOnly .esbuild/build.ts",
"build:mermaid": "pnpm build:esbuild --mermaid",
"build:viz": "pnpm build:esbuild --visualize",
"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",
"build:types": "ts-node-esm --transpileOnly .build/types.ts",
"build:types:watch": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly --watch",
"dev": "ts-node-esm --transpileOnly .esbuild/server.ts",
"dev:vite": "ts-node-esm --transpileOnly .vite/server.ts",
Expand Down
45 changes: 45 additions & 0 deletions packages/mermaid-flowchart-elk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@mermaid-js/flowchart-elk",
"version": "1.0.0",
"description": "Flowchart plugin for mermaid with ELK layout",
"module": "dist/mermaid-flowchart-elk.core.mjs",
"types": "dist/packages/mermaid-flowchart-elk/src/detector.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/mermaid-flowchart-elk.core.mjs",
"types": "./dist/packages/mermaid-flowchart-elk/src/detector.d.ts"
},
"./*": "./*"
},
"keywords": [
"diagram",
"markdown",
"flowchart",
"elk",
"mermaid"
],
"scripts": {
"prepublishOnly": "pnpm -w run build"
},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"
},
"author": "Knut Sveidqvist",
"license": "MIT",
"dependencies": {
"d3": "^7.4.0",
"dagre-d3-es": "7.0.10",
"khroma": "^2.0.0",
"elkjs": "^0.8.2"
},
"devDependencies": {
"concurrently": "^8.0.0",
"rimraf": "^5.0.0",
"mermaid": "workspace:^"
},
"files": [
"dist"
]
}
32 changes: 32 additions & 0 deletions packages/mermaid-flowchart-elk/src/detector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type {
ExternalDiagramDefinition,
DiagramDetector,
DiagramLoader,
} from '../../mermaid/src/diagram-api/types.js';

const id = 'flowchart-elk';

const detector: DiagramDetector = (txt, config): boolean => {
if (
// If diagram explicitly states flowchart-elk
/^\s*flowchart-elk/.test(txt) ||
// If a flowchart/graph diagram has their default renderer set to elk
(/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
) {
return true;
}
return false;
};

const loader: DiagramLoader = async () => {
const { diagram } = await import('./diagram-definition.js');
return { id, diagram };
};

const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};

export default plugin;
12 changes: 12 additions & 0 deletions packages/mermaid-flowchart-elk/src/diagram-definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @ts-ignore: JISON typing missing
import parser from '../../mermaid/src/diagrams/flowchart/parser/flow.jison';
import * as db from '../../mermaid/src/diagrams/flowchart/flowDb.js';
import styles from '../../mermaid/src/diagrams/flowchart/styles.js';
import renderer from './flowRenderer-elk.js';

export const diagram = {
db,
renderer,
parser,
styles,
};
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { select, line, curveLinear } from 'd3';
import { insertNode } from '../../../dagre-wrapper/nodes.js';
import insertMarkers from '../../../dagre-wrapper/markers.js';
import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
import { insertNode } from '../../mermaid/src/dagre-wrapper/nodes.js';
import insertMarkers from '../../mermaid/src/dagre-wrapper/markers.js';
import { insertEdgeLabel } from '../../mermaid/src/dagre-wrapper/edges.js';
import { findCommonAncestor } from './render-utils.js';
import { labelHelper } from '../../../dagre-wrapper/shapes/util.js';
import { getConfig } from '../../../config.js';
import { log } from '../../../logger.js';
import { setupGraphViewbox } from '../../../setupGraphViewbox.js';
import common from '../../common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../../utils.js';
import { labelHelper } from '../../mermaid/src/dagre-wrapper/shapes/util.js';
import { getConfig } from '../../mermaid/src/config.js';
import { log } from '../../mermaid/src/logger.js';
import { setupGraphViewbox } from '../../mermaid/src/setupGraphViewbox.js';
import common from '../../mermaid/src/diagrams/common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils.js';
import ELK from 'elkjs/lib/elk.bundled.js';
import { getLineFunctionsWithOffset } from '../../../utils/lineWithOffset.js';
import { getLineFunctionsWithOffset } from '../../mermaid/src/utils/lineWithOffset.js';

const elk = new ELK();

Expand Down Expand Up @@ -695,7 +695,7 @@ const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAb
*
* @param text
* @param diagObj
* @returns {Record<string, import('../../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
* @returns {Record<string, import('../../mermaid/src/diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
Expand Down
9 changes: 9 additions & 0 deletions packages/mermaid-flowchart-elk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "../..",
"outDir": "./dist"
},
"include": ["./src/**/*.ts"],
"typeRoots": ["./src/types"]
}
1 change: 0 additions & 1 deletion packages/mermaid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
"dagre-d3-es": "7.0.10",
"dayjs": "^1.11.7",
"dompurify": "^3.0.5",
"elkjs": "^0.8.2",
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"mdast-util-from-markdown": "^1.3.0",
Expand Down
5 changes: 2 additions & 3 deletions packages/mermaid/src/diagram-api/detectType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinitio

export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
if (detectors[key]) {
log.error(`Detector with key ${key} already exists`);
} else {
detectors[key] = { detector, loader };
log.warn(`Detector with key ${key} already exists. Overwriting.`);
}
detectors[key] = { detector, loader };
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
};

Expand Down
2 changes: 1 addition & 1 deletion packages/mermaid/src/diagram-api/diagramAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const registerDiagram = (
detector?: DiagramDetector
) => {
if (diagrams[id]) {
throw new Error(`Diagram ${id} already registered.`);
log.warn(`Diagram with id ${id} already registered. Overwriting.`);
}
diagrams[id] = diagram;
if (detector) {
Expand Down
Loading