chore: Move examples to different package

This commit is contained in:
Sidharth Vinod
2025-04-05 08:53:37 +05:30
parent 34e6112fea
commit 7e7a4fc665
55 changed files with 806 additions and 573 deletions

View File

@@ -33,4 +33,9 @@ export const packageOptions = {
packageName: 'mermaid-layout-elk',
file: 'layouts.ts',
},
'mermaid-examples': {
name: 'mermaid-examples',
packageName: 'examples',
file: 'index.ts',
},
} as const satisfies Record<string, PackageOptions>;

View File

@@ -0,0 +1,20 @@
{
"name": "@mermaid-js/examples",
"version": "0.0.1",
"description": "Mermaid examples package",
"author": "Sidharth Vinod",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"clean": "rimraf dist"
},
"dependencies": {},
"devDependencies": {
"mermaid": "workspace:*"
}
}

View File

@@ -0,0 +1,32 @@
import mermaid from 'mermaid';
import { diagramData } from './index.js';
describe('examples', () => {
beforeAll(async () => {
// To trigger the diagram registration
await mermaid.registerExternalDiagrams([]);
});
it('should have examples for each diagrams', () => {
const skippedDiagrams = [
// These diagrams have no examples
'error',
'info',
'---',
// These diagrams have v2 versions, with examples
'class',
'graph',
'flowchart-elk',
'flowchart',
'state',
];
const diagrams = mermaid.getDiagramData().filter((d) => !skippedDiagrams.includes(d.id));
expect(diagrams.length).toBeGreaterThan(0);
for (const diagram of diagrams) {
const data = diagramData.find((d) => d.id === diagram.id)!;
expect(data).toBeDefined();
expect(data.examples.length).toBeGreaterThan(0);
expect(data.examples.filter((e) => e.isDefault).length).toBe(1);
}
});
});

View File

@@ -0,0 +1,24 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'architecture',
name: 'Architecture Diagram',
description: 'Visualize system architecture and components',
examples: [
{
title: 'Basic System Architecture',
code: `architecture-beta
group api(cloud)[API]
service db(database)[Database] in api
service disk1(disk)[Storage] in api
service disk2(disk)[Storage] in api
service server(server)[Server] in api
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,27 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'block',
name: 'Block Diagram',
description: 'Create block-based visualizations with beta styling',
examples: [
{
title: 'Basic Block Layout',
code: `block-beta
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
block:ID
A
B["A wide one in the middle"]
C
end
space
D
ID --> D
C --> D
style B fill:#969,stroke:#333,stroke-width:4px`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,47 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'c4',
name: 'C4 Diagram',
description:
'Visualize software architecture using the C4 model (Context, Container, Component, Code)',
examples: [
{
isDefault: true,
code: `C4Context
title System Context diagram for Internet Banking System
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
Person(customerB, "Banking Customer B")
Person_Ext(customerC, "Banking Customer C", "desc")
Person(customerD, "Banking Customer D", "A customer of the bank, <br/> with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
System_Boundary(b2, "BankBoundary2") {
System(SystemA, "Banking System A")
System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts. next line.")
}
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.")
Boundary(b3, "BankBoundary3", "boundary") {
SystemQueue(SystemF, "Banking System F Queue", "A system of the bank.")
SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.")
}
}
}
BiRel(customerA, SystemAA, "Uses")
BiRel(SystemAA, SystemE, "Uses")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
Rel(SystemC, customerA, "Sends e-mails to")`,
title: 'Internet Banking System Context',
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,34 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'classDiagram',
name: 'Class Diagram',
description: 'Visualize class structures and relationships in object-oriented programming',
examples: [
{
isDefault: true,
code: `classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}`,
title: 'Basic Class Inheritance',
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,36 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'er',
name: 'Entity Relationship Diagram',
description: 'Visualize database schemas and relationships between entities',
examples: [
{
title: 'Basic ER Schema',
code: `erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT ||--o{ ORDER_ITEM : includes
CUSTOMER {
string id
string name
string email
}
ORDER {
string id
date orderDate
string status
}
PRODUCT {
string id
string name
float price
}
ORDER_ITEM {
int quantity
float price
}`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,19 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'flowchart-v2',
name: 'Flowchart',
description: 'Visualize flowcharts and directed graphs',
examples: [
{
isDefault: true,
code: `flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]`,
title: 'Basic Flowchart',
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,22 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'gantt',
name: 'Gantt Chart',
description: 'Visualize project schedules and timelines',
examples: [
{
isDefault: true,
code: `gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
another task : 24d`,
title: 'Basic Project Timeline',
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,28 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'gitGraph',
name: 'Git Graph',
description: 'Visualize Git repository history and branch relationships',
examples: [
{
title: 'Basic Git Flow',
code: `gitGraph
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
branch feature
checkout feature
commit
commit
checkout main
merge feature`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,37 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'kanban',
name: 'Kanban Diagram',
description: 'Visualize work items in a Kanban board',
examples: [
{
isDefault: true,
title: 'Kanban Diagram',
code: `---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
---
kanban
Todo
[Create Documentation]
docs[Create Blog about the new diagram]
[In progress]
id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
id9[Ready for deploy]
id8[Design grammar]@{ assigned: 'knsv' }
id10[Ready for test]
id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
id11[Done]
id5[define getData]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
`,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,32 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'mindmap',
name: 'Mindmap',
description: 'Visualize ideas and concepts in a tree-like structure',
examples: [
{
title: 'Basic Mindmap',
code: `mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid`,
isDefault: true,
},
],
} satisfies DiagramMetadata;
// cspell:ignore Buzan

View File

@@ -0,0 +1,34 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'packet',
name: 'Packet Diagram',
description: 'Visualize packet data and network traffic',
examples: [
{
title: 'TCP Packet',
code: `---
title: "TCP Packet"
---
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
100-105: "Reserved"
106: "URG"
107: "ACK"
108: "PSH"
109: "RST"
110: "SYN"
111: "FIN"
112-127: "Window"
128-143: "Checksum"
144-159: "Urgent Pointer"
160-191: "(Options and Padding)"
192-255: "Data (variable length)"`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,17 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'pie',
name: 'Pie Chart',
description: 'Visualize data as proportional segments of a circle',
examples: [
{
title: 'Basic Pie Chart',
code: `pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,27 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'quadrantChart',
name: 'Quadrant Chart',
description: 'Visualize items in a 2x2 matrix based on two variables',
examples: [
{
isDefault: true,
code: `quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.3, 0.6]
Campaign B: [0.45, 0.23]
Campaign C: [0.57, 0.69]
Campaign D: [0.78, 0.34]
Campaign E: [0.40, 0.34]
Campaign F: [0.35, 0.78]`,
title: 'Product Positioning',
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,25 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'radar',
name: 'Radar Diagram',
description: 'Visualize data in a radial format',
examples: [
{
title: 'Student Grades',
code: `---
title: "Grades"
---
radar-beta
axis m["Math"], s["Science"], e["English"]
axis h["History"], g["Geography"], a["Art"]
curve a["Alice"]{85, 90, 80, 70, 75, 90}
curve b["Bob"]{70, 75, 85, 80, 90, 85}
max 100
min 0
`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,27 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'requirement',
name: 'Requirement Diagram',
description: 'Visualize system requirements and their relationships',
examples: [
{
title: 'Basic Requirements',
code: `requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,88 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'sankey',
name: 'Sankey Diagram',
description: 'Visualize flow quantities between different stages or processes',
examples: [
{
title: 'Energy Flow',
code: `---
config:
sankey:
showValues: false
---
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,NGas,40.719
Gas reserves,NGas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
NGas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,18 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'sequence',
name: 'Sequence Diagram',
description: 'Visualize interactions between objects over time',
examples: [
{
title: 'Basic Sequence',
code: `sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,20 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'stateDiagram',
name: 'State Diagram',
description: 'Visualize the states and transitions of a system',
examples: [
{
title: 'Basic State Diagram',
code: `stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,20 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'timeline',
name: 'Timeline Diagram',
description: 'Visualize events and milestones in chronological order',
examples: [
{
isDefault: true,
code: `timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook
: Google
2005 : YouTube
2006 : Twitter`,
title: 'Project Timeline',
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,22 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'journey',
name: 'User Journey Diagram',
description: 'Visualize user interactions and experiences with a system',
examples: [
{
title: 'My Working Day',
code: `journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,19 @@
import type { DiagramMetadata } from '../types.js';
export default {
id: 'xychart',
name: 'XY Chart',
description: 'Create scatter plots and line charts with customizable axes',
examples: [
{
title: 'Sales Revenue',
code: `xychart-beta
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]`,
isDefault: true,
},
],
} satisfies DiagramMetadata;

View File

@@ -0,0 +1,46 @@
import type { DiagramMetadata } from './types.js';
import flowChart from './examples/flowchart.js';
import c4 from './examples/c4.js';
import kanban from './examples/kanban.js';
import classDiagram from './examples/class.js';
import sequenceDiagram from './examples/sequence.js';
import pieDiagram from './examples/pie.js';
import userJourneyDiagram from './examples/user-journey.js';
import mindmapDiagram from './examples/mindmap.js';
import requirementDiagram from './examples/requirement.js';
import radarDiagram from './examples/radar.js';
import stateDiagram from './examples/state.js';
import erDiagram from './examples/er.js';
import gitDiagram from './examples/git.js';
import architectureDiagram from './examples/architecture.js';
import xychartDiagram from './examples/xychart.js';
import sankeyDiagram from './examples/sankey.js';
import ganttDiagram from './examples/gantt.js';
import timelineDiagram from './examples/timeline.js';
import quadrantChart from './examples/quadrant-chart.js';
import packetDiagram from './examples/packet.js';
import blockDiagram from './examples/block.js';
export const diagramData: DiagramMetadata[] = [
flowChart,
c4,
kanban,
classDiagram,
sequenceDiagram,
pieDiagram,
userJourneyDiagram,
mindmapDiagram,
requirementDiagram,
radarDiagram,
stateDiagram,
erDiagram,
gitDiagram,
architectureDiagram,
xychartDiagram,
sankeyDiagram,
ganttDiagram,
timelineDiagram,
quadrantChart,
packetDiagram,
blockDiagram,
];

View File

@@ -0,0 +1,12 @@
export interface Example {
title: string;
code: string;
isDefault?: boolean;
}
export interface DiagramMetadata {
id: string;
name: string;
description: string;
examples: Example[];
}

View File

@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"composite": true,
"module": "NodeNext",
"moduleResolution": "NodeNext"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -1,10 +1,15 @@
import type { MermaidConfig } from '../config.type.js';
import { UnknownDiagramError } from '../errors.js';
import { log } from '../logger.js';
import type {
DetectorRecord,
DiagramDetector,
DiagramLoader,
ExternalDiagramDefinition,
} from './types.js';
import { anyCommentRegex, directiveRegex, frontMatterRegex } from './regexes.js';
import type { DetectorRecord, ExternalDiagramDefinition } from './types.js';
import { UnknownDiagramError } from '../errors.js';
export const diagramDefinitions: Record<string, Omit<DetectorRecord, 'id'>> = {};
export const detectors: Record<string, DetectorRecord> = {};
/**
* Detects the type of the graph text.
@@ -33,7 +38,7 @@ export const detectType = function (text: string, config?: MermaidConfig): strin
.replace(frontMatterRegex, '')
.replace(directiveRegex, '')
.replace(anyCommentRegex, '\n');
for (const [key, { detector }] of Object.entries(diagramDefinitions)) {
for (const [key, { detector }] of Object.entries(detectors)) {
const diagram = detector(text, config);
if (diagram) {
return key;
@@ -59,27 +64,19 @@ export const detectType = function (text: string, config?: MermaidConfig): strin
* @param diagrams - Diagrams to lazy load, and their detectors, in order of importance.
*/
export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => {
for (const definition of diagrams) {
addDiagramDefinition(definition);
for (const { id, detector, loader } of diagrams) {
addDetector(id, detector, loader);
}
};
export const addDiagramDefinition = ({ id, ...definition }: DetectorRecord) => {
if (diagramDefinitions[id]) {
log.warn(`Detector with key ${id} already exists. Overwriting.`);
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
if (detectors[key]) {
log.warn(`Detector with key ${key} already exists. Overwriting.`);
}
if (
definition.examples &&
definition.examples.filter(({ isDefault }) => isDefault).length !== 1
) {
throw new Error(
`Diagram with key ${id} must have exactly one default example. Set isDefault to true for one example.`
);
}
diagramDefinitions[id] = definition;
log.debug(`Detector with key ${id} added${definition.loader ? ' with loader' : ''}`);
detectors[key] = { detector, loader };
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
};
export const getDiagramLoader = (key: string) => {
return diagramDefinitions[key].loader;
return detectors[key].loader;
};

View File

@@ -1,4 +1,4 @@
import { addDiagramDefinition } from './detectType.js';
import { addDetector } from './detectType.js';
import { log as _log, setLogLevel as _setLogLevel } from '../logger.js';
import {
getConfig as _getConfig,
@@ -51,7 +51,7 @@ export const registerDiagram = (
}
diagrams[id] = diagram;
if (detector) {
addDiagramDefinition({ id, detector });
addDetector(id, detector);
}
addStylesForDiagram(id, diagram.styles);

View File

@@ -1,12 +1,12 @@
import { log } from '../logger.js';
import { diagramDefinitions } from './detectType.js';
import { detectors } from './detectType.js';
import { getDiagram, registerDiagram } from './diagramAPI.js';
export const loadRegisteredDiagrams = async () => {
log.debug(`Loading registered diagrams`);
// Load all lazy loaded diagrams in parallel
const results = await Promise.allSettled(
Object.entries(diagramDefinitions).map(async ([key, { detector, loader }]) => {
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
if (!loader) {
return;
}
@@ -20,7 +20,7 @@ export const loadRegisteredDiagrams = async () => {
} catch (err) {
// Remove failed diagram from detectors
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
delete diagramDefinitions[key];
delete detectors[key];
throw err;
}
}

View File

@@ -93,18 +93,11 @@ export interface DiagramDefinition {
export interface ExternalDiagramDefinition {
id: string;
/**
* Title, description, and examples for the diagram are optional only for backwards compatibility.
* It is strongly recommended to provide these values for all new diagrams.
*/
title?: string;
description?: string;
examples?: { code: string; title?: string; isDefault?: boolean }[];
detector: DiagramDetector;
loader: DiagramLoader;
}
export type DetectorRecord = SetOptional<ExternalDiagramDefinition, 'loader'>;
export type DetectorRecord = SetOptional<Omit<ExternalDiagramDefinition, 'id'>, 'loader'>;
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
export type DiagramLoader = () => Promise<{ id: string; diagram: DiagramDefinition }>;

View File

@@ -1,6 +1,6 @@
import { describe, test, expect } from 'vitest';
import { Diagram } from './Diagram.js';
import { addDiagramDefinition } from './diagram-api/detectType.js';
import { addDetector } from './diagram-api/detectType.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import type { DiagramLoader } from './diagram-api/types.js';
@@ -41,11 +41,11 @@ describe('diagram detection', () => {
});
test('should detect external diagrams', async () => {
addDiagramDefinition({
id: 'loki',
detector: (str) => str.startsWith('loki'),
loader: () => Promise.resolve(getDummyDiagram('loki')),
});
addDetector(
'loki',
(str) => str.startsWith('loki'),
() => Promise.resolve(getDummyDiagram('loki'))
);
const diagram = await Diagram.fromText('loki TD; A-->B');
expect(diagram).toBeInstanceOf(Diagram);
expect(diagram.type).toBe('loki');
@@ -53,11 +53,11 @@ describe('diagram detection', () => {
test('should allow external diagrams to override internal ones with same ID', async () => {
const title = 'overridden';
addDiagramDefinition({
id: 'flowchart-elk',
detector: (str) => str.startsWith('flowchart-elk'),
loader: () => Promise.resolve(getDummyDiagram('flowchart-elk', title)),
});
addDetector(
'flowchart-elk',
(str) => str.startsWith('flowchart-elk'),
() => Promise.resolve(getDummyDiagram('flowchart-elk', title))
);
const diagram = await Diagram.fromText('flowchart-elk TD; A-->B');
expect(diagram).toBeInstanceOf(Diagram);
expect(diagram.db.getDiagramTitle?.()).toBe(title);

View File

@@ -19,25 +19,6 @@ const architecture: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Architecture Diagram',
description: 'Visualize system architecture and components',
examples: [
{
isDefault: true,
code: `architecture-beta
group api(cloud)[API]
service db(database)[Database] in api
service disk1(disk)[Storage] in api
service disk2(disk)[Storage] in api
service server(server)[Server] in api
db:L -- R:server
disk1:T -- B:server
disk2:T -- B:db`,
title: 'Basic System Architecture',
},
],
};
export default architecture;

View File

@@ -15,28 +15,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Block Diagram',
description: 'Create block-based visualizations with beta styling',
examples: [
{
isDefault: true,
code: `block-beta
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
block:ID
A
B["A wide one in the middle"]
C
end
space
D
ID --> D
C --> D
style B fill:#969,stroke:#333,stroke-width:4px`,
title: 'Basic Block Layout',
},
],
};
export default plugin;

View File

@@ -19,48 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'C4 Diagram',
description:
'Visualize software architecture using the C4 model (Context, Container, Component, Code)',
examples: [
{
isDefault: true,
code: `C4Context
title System Context diagram for Internet Banking System
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
Person(customerB, "Banking Customer B")
Person_Ext(customerC, "Banking Customer C", "desc")
Person(customerD, "Banking Customer D", "A customer of the bank, <br/> with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
System_Boundary(b2, "BankBoundary2") {
System(SystemA, "Banking System A")
System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts. next line.")
}
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.")
Boundary(b3, "BankBoundary3", "boundary") {
SystemQueue(SystemF, "Banking System F Queue", "A system of the bank.")
SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.")
}
}
}
BiRel(customerA, SystemAA, "Uses")
BiRel(SystemAA, SystemE, "Uses")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
Rel(SystemC, customerA, "Sends e-mails to")`,
title: 'Internet Banking System Context',
},
],
};
export default plugin;

View File

@@ -24,35 +24,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Class Diagram',
description: 'Visualize class structures and relationships in object-oriented programming',
examples: [
{
isDefault: true,
code: `classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}`,
title: 'Basic Class Inheritance',
},
],
};
export default plugin;

View File

@@ -19,37 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Entity Relationship Diagram',
description: 'Visualize database schemas and relationships between entities',
examples: [
{
isDefault: true,
code: `erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT ||--o{ ORDER_ITEM : includes
CUSTOMER {
string id
string name
string email
}
ORDER {
string id
date orderDate
string status
}
PRODUCT {
string id
string name
float price
}
ORDER_ITEM {
int quantity
float price
}`,
title: 'Basic ER Schema',
},
],
};
export default plugin;

View File

@@ -31,20 +31,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Flowchart',
description: 'Visualize flowcharts and directed graphs',
examples: [
{
isDefault: true,
code: `flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]`,
title: 'Basic Flowchart',
},
],
};
export default plugin;

View File

@@ -19,23 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Gantt Chart',
description: 'Visualize project schedules and timelines',
examples: [
{
isDefault: true,
code: `gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
another task : 24d`,
title: 'Basic Project Timeline',
},
],
};
export default plugin;

View File

@@ -16,29 +16,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Git Graph',
description: 'Visualize Git repository history and branch relationships',
examples: [
{
isDefault: true,
code: `gitGraph
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
branch feature
checkout feature
commit
commit
checkout main
merge feature`,
title: 'Basic Git Flow',
},
],
};
export default plugin;

View File

@@ -18,37 +18,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Kanban Diagram',
description: 'Visualize work items in a Kanban board',
examples: [
{
isDefault: true,
code: `---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
---
kanban
Todo
[Create Documentation]
docs[Create Blog about the new diagram]
[In progress]
id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
id9[Ready for deploy]
id8[Design grammar]@{ assigned: 'knsv' }
id10[Ready for test]
id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
id11[Done]
id5[define getData]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
`,
},
],
};
export default plugin;

View File

@@ -18,32 +18,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Mindmap',
description: 'Visualize ideas and concepts in a tree-like structure',
examples: [
{
isDefault: true,
code: `mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid`,
},
],
};
export default plugin;
// cspell:ignore Buzan

View File

@@ -19,33 +19,4 @@ export const packet: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Packet Diagram',
description: 'Visualize packet data and network traffic',
examples: [
{
isDefault: true,
code: `---
title: "TCP Packet"
---
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
100-105: "Reserved"
106: "URG"
107: "ACK"
108: "PSH"
109: "RST"
110: "SYN"
111: "FIN"
112-127: "Window"
128-143: "Checksum"
144-159: "Urgent Pointer"
160-191: "(Options and Padding)"
192-255: "Data (variable length)"
`,
},
],
};

View File

@@ -19,16 +19,4 @@ export const pie: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Pie Chart',
description: 'Visualize data as proportional segments of a circle',
examples: [
{
isDefault: true,
code: `pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15`,
title: 'Basic Pie Chart',
},
],
};

View File

@@ -19,28 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Quadrant Chart',
description: 'Visualize items in a 2x2 matrix based on two variables',
examples: [
{
isDefault: true,
code: `quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.3, 0.6]
Campaign B: [0.45, 0.23]
Campaign C: [0.57, 0.69]
Campaign D: [0.78, 0.34]
Campaign E: [0.40, 0.34]
Campaign F: [0.35, 0.78]`,
title: 'Product Positioning',
},
],
};
export default plugin;

View File

@@ -19,23 +19,4 @@ export const radar: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Radar Diagram',
description: 'Visualize data in a radial format',
examples: [
{
isDefault: true,
code: `---
title: "Grades"
---
radar-beta
axis m["Math"], s["Science"], e["English"]
axis h["History"], g["Geography"], a["Art"]
curve a["Alice"]{85, 90, 80, 70, 75, 90}
curve b["Bob"]{70, 75, 85, 80, 90, 85}
max 100
min 0
`,
},
],
};

View File

@@ -19,28 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Requirement Diagram',
description: 'Visualize system requirements and their relationships',
examples: [
{
isDefault: true,
code: `requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req`,
title: 'Basic Requirements',
},
],
};
export default plugin;

View File

@@ -15,89 +15,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Sankey Diagram',
description: 'Visualize flow quantities between different stages or processes',
examples: [
{
isDefault: true,
code: `---
config:
sankey:
showValues: false
---
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,NGas,40.719
Gas reserves,NGas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
NGas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366`,
title: 'Energy Flow',
},
],
};
export default plugin;

View File

@@ -19,19 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Sequence Diagram',
description: 'Visualize interactions between objects over time',
examples: [
{
isDefault: true,
code: `sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!`,
title: 'Basic Sequence',
},
],
};
export default plugin;

View File

@@ -25,21 +25,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'State Diagram',
description: 'Visualize state transitions and behaviors of a system',
examples: [
{
isDefault: true,
code: `stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]`,
title: 'Basic State',
},
],
};
export default plugin;

View File

@@ -19,21 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'Timeline Diagram',
description: 'Visualize events and milestones in chronological order',
examples: [
{
isDefault: true,
code: `timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook
: Google
2005 : YouTube
2006 : Twitter`,
title: 'Project Timeline',
},
],
};
export default plugin;

View File

@@ -19,23 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'User Journey Diagram',
description: 'Visualize user interactions and experiences with a system',
examples: [
{
isDefault: true,
code: `journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 5: Me`,
title: 'My Working Day',
},
],
};
export default plugin;

View File

@@ -19,20 +19,6 @@ const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
title: 'XY Chart',
description: 'Create scatter plots and line charts with customizable axes',
examples: [
{
isDefault: true,
code: `xychart-beta
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]`,
title: 'Sales Revenue',
},
],
};
export default plugin;

View File

@@ -5,11 +5,7 @@
import { registerIconPacks } from './rendering-util/icons.js';
import { dedent } from 'ts-dedent';
import type { MermaidConfig } from './config.type.js';
import {
detectType,
diagramDefinitions,
registerLazyLoadedDiagrams,
} from './diagram-api/detectType.js';
import { detectType, detectors, registerLazyLoadedDiagrams } from './diagram-api/detectType.js';
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js';
import type { ExternalDiagramDefinition, SVG, SVGGroup } from './diagram-api/types.js';
@@ -419,15 +415,9 @@ const render: typeof mermaidAPI.render = (id, text, container) => {
});
};
const getDiagramData = (): Pick<
ExternalDiagramDefinition,
'id' | 'title' | 'description' | 'examples'
>[] => {
return Object.entries(diagramDefinitions).map(([id, { title, description, examples }]) => ({
const getDiagramData = (): Pick<ExternalDiagramDefinition, 'id'>[] => {
return Object.keys(detectors).map((id) => ({
id,
title,
description,
examples,
}));
};

20
pnpm-lock.yaml generated
View File

@@ -421,6 +421,12 @@ importers:
specifier: ^6.0.1
version: 6.0.1
packages/mermaid-examples:
devDependencies:
mermaid:
specifier: workspace:*
version: link:../mermaid
packages/mermaid-layout-elk:
dependencies:
d3:
@@ -3287,6 +3293,9 @@ packages:
'@types/node@18.19.76':
resolution: {integrity: sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==}
'@types/node@20.17.30':
resolution: {integrity: sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==}
'@types/node@22.13.5':
resolution: {integrity: sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==}
@@ -9238,6 +9247,9 @@ packages:
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
undici-types@6.19.8:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
@@ -13088,6 +13100,10 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/node@20.17.30':
dependencies:
undici-types: 6.19.8
'@types/node@22.13.5':
dependencies:
undici-types: 6.20.0
@@ -17394,7 +17410,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.13.5
'@types/node': 20.17.30
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -20275,6 +20291,8 @@ snapshots:
undici-types@5.26.5: {}
undici-types@6.19.8: {}
undici-types@6.20.0: {}
undici@5.28.4: