feat(mermaid): support elk layout (#11357)
Some checks are pending
Argos CI / take-screenshots (push) Waiting to run
Build Hash Router / Build Hash Router (push) Waiting to run
Canary Release / Publish Canary (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
Continuous Releases / Continuous Releases (push) Waiting to run
E2E Tests / E2E — Yarn v1 (18.0) (push) Waiting to run
E2E Tests / E2E — Yarn v1 (20) (push) Waiting to run
E2E Tests / E2E — Yarn v1 (22) (push) Waiting to run
E2E Tests / E2E — Yarn v1 (24) (push) Waiting to run
E2E Tests / E2E — Yarn v1 Windows (push) Waiting to run
E2E Tests / E2E — Yarn Berry (node-modules, -s) (push) Waiting to run
E2E Tests / E2E — Yarn Berry (node-modules, -st) (push) Waiting to run
E2E Tests / E2E — Yarn Berry (pnp, -s) (push) Waiting to run
E2E Tests / E2E — Yarn Berry (pnp, -st) (push) Waiting to run
E2E Tests / E2E — npm (push) Waiting to run
E2E Tests / E2E — pnpm (push) Waiting to run

Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
Feez2403 2025-08-24 20:11:21 +02:00 committed by GitHub
parent c131034bc2
commit 305910758b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 215 additions and 4 deletions

View File

@ -46,7 +46,13 @@
},
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
"react-dom": "^18.0.0 || ^19.0.0",
"@mermaid-js/layout-elk": "^0.1.9"
},
"peerDependenciesMeta": {
"@mermaid-js/layout-elk": {
"optional": true
}
},
"engines": {
"node": ">=18.0"

View File

@ -8,6 +8,8 @@
import {useState, useEffect, useMemo} from 'react';
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
import mermaid from 'mermaid';
import {ensureLayoutsRegistered} from './layouts';
import type {RenderResult, MermaidConfig} from 'mermaid';
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
@ -37,7 +39,7 @@ function useMermaidId(): string {
Note: Mermaid doesn't like values provided by Rect.useId() and throws
*/
// TODO 2025-2026: check if useId() now works
// TODO Docusaurus v4: check if useId() now works
// It could work thanks to https://github.com/facebook/react/pull/32001
// return useId(); // tried that, doesn't work ('#d:re:' is not a valid selector.)
@ -53,6 +55,8 @@ async function renderMermaid({
text: string;
config: MermaidConfig;
}): Promise<RenderResult> {
await ensureLayoutsRegistered();
/*
Mermaid API is really weird :s
It is a big mutable singleton with multiple config levels
@ -71,7 +75,7 @@ async function renderMermaid({
To use a new mermaid config (on colorMode change for example) we should
update siteConfig, and it can only be done with initialize()
*/
mermaid.mermaidAPI.initialize(config);
mermaid.initialize(config);
try {
return await mermaid.render(id, text);

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import mermaid from 'mermaid';
declare global {
// Global variable provided by bundler DefinePlugin
/* eslint-disable-next-line no-underscore-dangle */
const __DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__: boolean;
}
async function registerOptionalElkLayout() {
// Mermaid does not support ELK layouts by default
// See https://github.com/mermaid-js/mermaid/tree/develop/packages/mermaid-layout-elk
// ELK layouts are heavy, so we made it an optional peer dependency
// See https://github.com/facebook/docusaurus/pull/11357
if (__DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__) {
const elkLayout = (await import('@mermaid-js/layout-elk')).default;
mermaid.registerLayoutLoaders(elkLayout);
}
}
// Ensure we only try to register layouts once
let layoutsRegistered = false;
export async function ensureLayoutsRegistered(): Promise<void> {
if (!layoutsRegistered) {
await registerOptionalElkLayout();
layoutsRegistered = true;
}
}

View File

@ -7,7 +7,26 @@
import type {Plugin} from '@docusaurus/types';
export default function themeMermaid(): Plugin<void> {
/**
* Check if the optional @mermaid-js/layout-elk package is available.
* It's an optional peer dependency because it's heavy and most Mermaid users
* might not need it.
*/
async function isElkLayoutPackageAvailable() {
try {
await import('@mermaid-js/layout-elk');
return true;
} catch (e) {
return false;
}
}
export default async function themeMermaid(): Promise<Plugin<void>> {
// For now, we infer based on package availability
// In the future, we could make it configurable so that users can disable it
// even if the package is installed?
const elkLayoutEnabled = await isElkLayoutPackageAvailable();
return {
name: 'docusaurus-theme-mermaid',
@ -17,6 +36,21 @@ export default function themeMermaid(): Plugin<void> {
getTypeScriptThemePath() {
return '../src/theme';
},
configureWebpack(config, isServer, utils) {
return {
plugins: [
new utils.currentBundler.instance.DefinePlugin({
__DOCUSAURUS_MERMAID_LAYOUT_ELK_ENABLED__: JSON.stringify(
// We only need to include the layout registration code on the
// client side. This also solves a weird Webpack-only bug when
// compiling the server config due to the module being ESM-only.
!isServer && elkLayoutEnabled,
),
}),
],
};
},
};
}

View File

@ -48,6 +48,7 @@ Csapo
Csvg
Dabit
dabit
dagre
Daishi
Datagit
datagit

View File

@ -447,3 +447,88 @@ architecture-beta
disk1:T -- B:server
disk2:T -- B:db
```
## ELK Styling
Mermaid provides an [ELK layout](https://mermaid.js.org/syntax/entityRelationshipDiagram.html#layout)
### Dagre
This is a "classical" Mermaid diagram, using the default Dagre layout.
```mermaid
erDiagram
COMPANY ||--o{ DEPARTMENT : has
COMPANY ||--o{ PROJECT : undertakes
COMPANY ||--o{ LOCATION : operates_in
COMPANY ||--o{ CLIENT : serves
DEPARTMENT ||--o{ EMPLOYEE : employs
DEPARTMENT ||--o{ PROJECT : manages
DEPARTMENT ||--o{ BUDGET : allocated
EMPLOYEE }o--o{ PROJECT : works_on
EMPLOYEE ||--|| ADDRESS : lives_at
EMPLOYEE }o--o{ SKILL : has
EMPLOYEE ||--o{ DEPENDENT : supports
PROJECT ||--o{ CLIENT : for
PROJECT ||--o{ TASK : contains
```
### ELK er diagram layout
This ER diagram should look different, using the ELK layout.
```mermaid
---
config:
layout: elk
---
erDiagram
COMPANY ||--o{ DEPARTMENT : has
COMPANY ||--o{ PROJECT : undertakes
COMPANY ||--o{ LOCATION : operates_in
COMPANY ||--o{ CLIENT : serves
DEPARTMENT ||--o{ EMPLOYEE : employs
DEPARTMENT ||--o{ PROJECT : manages
DEPARTMENT ||--o{ BUDGET : allocated
EMPLOYEE }o--o{ PROJECT : works_on
EMPLOYEE ||--|| ADDRESS : lives_at
EMPLOYEE }o--o{ SKILL : has
EMPLOYEE ||--o{ DEPENDENT : supports
PROJECT ||--o{ CLIENT : for
PROJECT ||--o{ TASK : contains
```
Mermaid also provides a way of setting config parameters using a directive `%%{init:{"layout":"elk"}}%%`
```mermaid
%%{init:{"layout":"elk"}}%%
erDiagram
COMPANY ||--o{ DEPARTMENT : has
COMPANY ||--o{ PROJECT : undertakes
COMPANY ||--o{ LOCATION : operates_in
COMPANY ||--o{ CLIENT : serves
DEPARTMENT ||--o{ EMPLOYEE : employs
DEPARTMENT ||--o{ PROJECT : manages
DEPARTMENT ||--o{ BUDGET : allocated
EMPLOYEE }o--o{ PROJECT : works_on
EMPLOYEE ||--|| ADDRESS : lives_at
EMPLOYEE }o--o{ SKILL : has
EMPLOYEE ||--o{ DEPENDENT : supports
PROJECT ||--o{ CLIENT : for
PROJECT ||--o{ TASK : contains
```

View File

@ -99,3 +99,36 @@ import Mermaid from '@theme/Mermaid';
C-->D;`}
/>
```
## Layouts
Mermaid supports different [layout engines](https://mermaid.js.org/intro/syntax-reference.html#layout-and-look):
- The `dagre` layout engine is supported by default in Docusaurus.
- The `elk` layout engine is heavier and can be enabled by installing the optional `@mermaid-js/layout-elk` dependency.
````md
```mermaid
---
config:
layout: elk
---
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
````
```mermaid
---
config:
layout: elk
---
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```

View File

@ -53,6 +53,7 @@
"@docusaurus/theme-mermaid": "3.8.1",
"@docusaurus/utils": "3.8.1",
"@docusaurus/utils-common": "3.8.1",
"@mermaid-js/layout-elk": "^0.1.9",
"clsx": "^2.0.0",
"color": "^4.2.3",
"fs-extra": "^11.1.1",

View File

@ -2591,6 +2591,14 @@
dependencies:
"@types/mdx" "^2.0.0"
"@mermaid-js/layout-elk@^0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@mermaid-js/layout-elk/-/layout-elk-0.1.9.tgz#c773b9454875858a2f45412fe04502bccec83cf2"
integrity sha512-HuvaqFZBr6yT9PpWYockvKAZPJVd89yn/UjOYPxhzbZxlybL2v+2BjVCg7MVH6vRs1irUohb/s42HEdec1CCZw==
dependencies:
d3 "^7.9.0"
elkjs "^0.9.3"
"@mermaid-js/parser@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@mermaid-js/parser/-/parser-0.4.0.tgz#c1de1f5669f8fcbd0d0c9d124927d36ddc00d8a6"
@ -7937,6 +7945,11 @@ electron-to-chromium@^1.5.160:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz#477b0957e42f071905a86f7c905a9848f95d2bdb"
integrity sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==
elkjs@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.9.3.tgz#16711f8ceb09f1b12b99e971b138a8384a529161"
integrity sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==
emittery@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"