mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-25 05:36:23 +01:00
Compare commits
1 Commits
dev-tool
...
update-lin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b50eb13746 |
@@ -1,46 +0,0 @@
|
||||
export interface PackageOptions {
|
||||
name: string;
|
||||
packageName: string;
|
||||
file: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared common options for both ESBuild and Vite
|
||||
*/
|
||||
export const packageOptions = {
|
||||
parser: {
|
||||
name: 'mermaid-parser',
|
||||
packageName: 'parser',
|
||||
file: 'index.ts',
|
||||
},
|
||||
mermaid: {
|
||||
name: 'mermaid',
|
||||
packageName: 'mermaid',
|
||||
file: 'mermaid.ts',
|
||||
},
|
||||
'mermaid-example-diagram': {
|
||||
name: 'mermaid-example-diagram',
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-zenuml': {
|
||||
name: 'mermaid-zenuml',
|
||||
packageName: 'mermaid-zenuml',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-layout-elk': {
|
||||
name: 'mermaid-layout-elk',
|
||||
packageName: 'mermaid-layout-elk',
|
||||
file: 'layouts.ts',
|
||||
},
|
||||
'mermaid-layout-tidy-tree': {
|
||||
name: 'mermaid-layout-tidy-tree',
|
||||
packageName: 'mermaid-layout-tidy-tree',
|
||||
file: 'index.ts',
|
||||
},
|
||||
examples: {
|
||||
name: 'mermaid-examples',
|
||||
packageName: 'examples',
|
||||
file: 'index.ts',
|
||||
},
|
||||
} as const satisfies Record<string, PackageOptions>;
|
||||
@@ -1,5 +0,0 @@
|
||||
import { generate } from 'langium-cli';
|
||||
|
||||
export async function generateLangium() {
|
||||
await generate({ file: `./packages/parser/langium-config.json` });
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import { load, JSON_SCHEMA } from 'js-yaml';
|
||||
import assert from 'node:assert';
|
||||
import Ajv2019, { type JSONSchemaType } from 'ajv/dist/2019.js';
|
||||
import type { MermaidConfig, BaseDiagramConfig } from '../packages/mermaid/src/config.type.js';
|
||||
|
||||
/**
|
||||
* All of the keys in the mermaid config that have a mermaid diagram config.
|
||||
*/
|
||||
const MERMAID_CONFIG_DIAGRAM_KEYS = [
|
||||
'flowchart',
|
||||
'sequence',
|
||||
'gantt',
|
||||
'journey',
|
||||
'class',
|
||||
'state',
|
||||
'er',
|
||||
'pie',
|
||||
'quadrantChart',
|
||||
'xyChart',
|
||||
'requirement',
|
||||
'mindmap',
|
||||
'kanban',
|
||||
'timeline',
|
||||
'gitGraph',
|
||||
'c4',
|
||||
'sankey',
|
||||
'block',
|
||||
'packet',
|
||||
'architecture',
|
||||
'radar',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Generate default values from the JSON Schema.
|
||||
*
|
||||
* AJV does not support nested default values yet (or default values with $ref),
|
||||
* so we need to manually find them (this may be fixed in ajv v9).
|
||||
*
|
||||
* @param mermaidConfigSchema - The Mermaid JSON Schema to use.
|
||||
* @returns The default mermaid config object.
|
||||
*/
|
||||
function generateDefaults(mermaidConfigSchema: JSONSchemaType<MermaidConfig>) {
|
||||
const ajv = new Ajv2019({
|
||||
useDefaults: true,
|
||||
allowUnionTypes: true,
|
||||
strict: true,
|
||||
});
|
||||
|
||||
ajv.addKeyword({
|
||||
keyword: 'meta:enum', // used by jsonschema2md
|
||||
errors: false,
|
||||
});
|
||||
ajv.addKeyword({
|
||||
keyword: 'tsType', // used by json-schema-to-typescript
|
||||
errors: false,
|
||||
});
|
||||
|
||||
// ajv currently doesn't support nested default values, see https://github.com/ajv-validator/ajv/issues/1718
|
||||
// (may be fixed in v9) so we need to manually use sub-schemas
|
||||
const mermaidDefaultConfig = {};
|
||||
|
||||
assert.ok(mermaidConfigSchema.$defs);
|
||||
const baseDiagramConfig = mermaidConfigSchema.$defs.BaseDiagramConfig;
|
||||
|
||||
for (const key of MERMAID_CONFIG_DIAGRAM_KEYS) {
|
||||
const subSchemaRef = mermaidConfigSchema.properties[key].$ref;
|
||||
const [root, defs, defName] = subSchemaRef.split('/');
|
||||
assert.strictEqual(root, '#');
|
||||
assert.strictEqual(defs, '$defs');
|
||||
const subSchema = {
|
||||
$schema: mermaidConfigSchema.$schema,
|
||||
$defs: mermaidConfigSchema.$defs,
|
||||
...mermaidConfigSchema.$defs[defName],
|
||||
} as JSONSchemaType<BaseDiagramConfig>;
|
||||
|
||||
const validate = ajv.compile(subSchema);
|
||||
|
||||
mermaidDefaultConfig[key] = {};
|
||||
|
||||
for (const required of subSchema.required ?? []) {
|
||||
if (subSchema.properties[required] === undefined && baseDiagramConfig.properties[required]) {
|
||||
mermaidDefaultConfig[key][required] = baseDiagramConfig.properties[required].default;
|
||||
}
|
||||
}
|
||||
if (!validate(mermaidDefaultConfig[key])) {
|
||||
throw new Error(
|
||||
`schema for subconfig ${key} does not have valid defaults! Errors were ${JSON.stringify(
|
||||
validate.errors,
|
||||
undefined,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const validate = ajv.compile(mermaidConfigSchema);
|
||||
|
||||
if (!validate(mermaidDefaultConfig)) {
|
||||
throw new Error(
|
||||
`Mermaid config JSON Schema does not have valid defaults! Errors were ${JSON.stringify(
|
||||
validate.errors,
|
||||
undefined,
|
||||
2
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return mermaidDefaultConfig;
|
||||
}
|
||||
|
||||
export const loadSchema = (src: string, filename: string): JSONSchemaType<MermaidConfig> => {
|
||||
const jsonSchema = load(src, {
|
||||
filename,
|
||||
// only allow JSON types in our YAML doc (will probably be default in YAML 1.3)
|
||||
// e.g. `true` will be parsed a boolean `true`, `True` will be parsed as string `"True"`.
|
||||
schema: JSON_SCHEMA,
|
||||
}) as JSONSchemaType<MermaidConfig>;
|
||||
return jsonSchema;
|
||||
};
|
||||
|
||||
export const getDefaults = (schema: JSONSchemaType<MermaidConfig>) => {
|
||||
return `export default ${JSON.stringify(generateDefaults(schema), undefined, 2)};`;
|
||||
};
|
||||
|
||||
export const getSchema = (schema: JSONSchemaType<MermaidConfig>) => {
|
||||
return `export default ${JSON.stringify(schema, undefined, 2)};`;
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
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`);
|
||||
if (out.length > 0) {
|
||||
console.log(out.toString());
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.stdout.length > 0) {
|
||||
console.error(e.stdout.toString());
|
||||
}
|
||||
if (e.stderr.length > 0) {
|
||||
console.error(e.stderr.toString());
|
||||
}
|
||||
// Exit the build process if we are in CI
|
||||
if (process.env.CI) {
|
||||
throw new Error(`Failed to build types for ${packageName}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const { packageName } of Object.values(packageOptions)) {
|
||||
buildType(packageName);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Prevent HTML tags from being escaped in sandbox label rendering
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Support edge animation in hand drawn look
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Resolved parsing error where direction TD was not recognized within subgraphs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(treemap): Fixed treemap classDef style application to properly apply user-defined styles
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Correct viewBox casing and make SVGs responsive
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||
"changelog": ["@changesets/changelog-github", { "repo": "mermaid-js/mermaid" }],
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "master",
|
||||
"updateInternalDependencies": "patch",
|
||||
"bumpVersionsWithWorkspaceProtocolOnly": true,
|
||||
"ignore": ["@mermaid-js/docs", "@mermaid-js/webpack-test", "@mermaid-js/mermaid-example-diagram"]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Improve participant parsing and prevent recursive loops on invalid syntax
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
feat: add alias support for new participant syntax of sequence diagrams
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Add half-arrowheads (solid & stick) and central connection support
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: allow to put notes in namespaces on classDiagram
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@mermaid': patch
|
||||
---
|
||||
|
||||
fix: Mindmap breaking in ELK layout
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Support ComponentQueue_Ext to prevent parsing error
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: validate dates and tick interval to prevent UI freeze/crash in gantt diagramtype
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Mindmap rendering issue when the number of Level 2 nodes exceeds 11
|
||||
3
.commitlintrc.json
Normal file
3
.commitlintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
!viewbox
|
||||
# It should be viewBox
|
||||
# This file contains coding related terms
|
||||
ALPHANUM
|
||||
antiscript
|
||||
APPLYCLASS
|
||||
ARROWHEADSTYLE
|
||||
ARROWTYPE
|
||||
autonumber
|
||||
axisl-line
|
||||
Bigdecimal
|
||||
birel
|
||||
BIREL
|
||||
bqstring
|
||||
BQUOTE
|
||||
bramp
|
||||
BRKT
|
||||
brotli
|
||||
callbackargs
|
||||
callbackname
|
||||
classdef
|
||||
classdefid
|
||||
classentity
|
||||
classname
|
||||
COLONSEP
|
||||
COMPOSIT_STATE
|
||||
concat
|
||||
controlx
|
||||
controly
|
||||
CSSCLASS
|
||||
curv
|
||||
CYLINDEREND
|
||||
CYLINDERSTART
|
||||
DAGA
|
||||
datakey
|
||||
DEND
|
||||
descr
|
||||
distp
|
||||
distq
|
||||
divs
|
||||
docref
|
||||
DOMID
|
||||
doublecircle
|
||||
DOUBLECIRCLEEND
|
||||
DOUBLECIRCLESTART
|
||||
DQUOTE
|
||||
DSTART
|
||||
edgesep
|
||||
EMPTYSTR
|
||||
enddate
|
||||
ERDIAGRAM
|
||||
eslint
|
||||
flatmap
|
||||
forwardable
|
||||
frontmatter
|
||||
funs
|
||||
gantt
|
||||
GENERICTYPE
|
||||
grammr
|
||||
graphtype
|
||||
halign
|
||||
iife
|
||||
interp
|
||||
introdcued
|
||||
INVTRAPEND
|
||||
INVTRAPSTART
|
||||
JDBC
|
||||
jison
|
||||
Kaufmann
|
||||
keyify
|
||||
LABELPOS
|
||||
LABELTYPE
|
||||
layoutstop
|
||||
lcov
|
||||
LEFTOF
|
||||
Lexa
|
||||
linebreak
|
||||
LINETYPE
|
||||
LINKSTYLE
|
||||
LLABEL
|
||||
loglevel
|
||||
LOGMSG
|
||||
lookaheads
|
||||
mdast
|
||||
metafile
|
||||
minlen
|
||||
Mstartx
|
||||
MULT
|
||||
NODIR
|
||||
NSTR
|
||||
outdir
|
||||
Qcontrolx
|
||||
QSTR
|
||||
reinit
|
||||
rels
|
||||
reqs
|
||||
rewritelinks
|
||||
rgba
|
||||
RIGHTOF
|
||||
roughjs
|
||||
sankey
|
||||
sequencenumber
|
||||
shrc
|
||||
signaltype
|
||||
someclass
|
||||
SPACELINE
|
||||
SPACELIST
|
||||
STADIUMEND
|
||||
STADIUMSTART
|
||||
startdate
|
||||
startx
|
||||
starty
|
||||
STMNT
|
||||
stopx
|
||||
stopy
|
||||
strikethrough
|
||||
stringifying
|
||||
struct
|
||||
STYLECLASS
|
||||
STYLEDEF
|
||||
STYLEOPTS
|
||||
subcomponent
|
||||
subcomponents
|
||||
subconfig
|
||||
SUBROUTINEEND
|
||||
SUBROUTINESTART
|
||||
Subschemas
|
||||
substr
|
||||
SVGG
|
||||
SVGSVG
|
||||
TAGEND
|
||||
TAGSTART
|
||||
techn
|
||||
TESTSTR
|
||||
TEXTDATA
|
||||
TEXTLENGTH
|
||||
titlevalue
|
||||
topbar
|
||||
TRAPEND
|
||||
TRAPSTART
|
||||
treemap
|
||||
ts-nocheck
|
||||
tsdoc
|
||||
typeof
|
||||
typestr
|
||||
unshift
|
||||
urlsafe
|
||||
verifymethod
|
||||
VERIFYMTHD
|
||||
WARN_DOCSDIR_DOESNT_MATCH
|
||||
xhost
|
||||
yaxis
|
||||
yfunc
|
||||
yytext
|
||||
zenuml
|
||||
@@ -1,12 +0,0 @@
|
||||
# Contributors to mermaidjs, one per line
|
||||
Ashish Jain
|
||||
cpettitt
|
||||
Dong Cai
|
||||
fourcube
|
||||
knsv
|
||||
Knut Sveidqvist
|
||||
Nikolay Rozhkov
|
||||
Peng Xiao
|
||||
Per Brolin
|
||||
Sidharth Vinod
|
||||
subhash-halder
|
||||
@@ -1,55 +0,0 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json
|
||||
|
||||
$schema: https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json
|
||||
|
||||
dictionaryDefinitions:
|
||||
- name: code-terms
|
||||
path: ./code-terms.txt
|
||||
description: A list of coding related terms.
|
||||
addWords: true
|
||||
- name: mermaid-terms
|
||||
path: ./mermaid-terms.txt
|
||||
description: A list of terms related to the mermaid project.
|
||||
addWords: true
|
||||
- name: misc-terms
|
||||
path: ./misc-terms.txt
|
||||
description: A list of miscellaneous terms.
|
||||
- name: 3rd-party-terms
|
||||
path: ./libraries.txt
|
||||
description: A list of 3rd party terms from dependencies.
|
||||
addWords: true
|
||||
- name: contributors
|
||||
path: ./contributors.txt
|
||||
description: A list of contributors to the mermaid project.
|
||||
type: 'W'
|
||||
addWords: true
|
||||
|
||||
# cspell:disable
|
||||
- name: suggestions
|
||||
words:
|
||||
- none
|
||||
- disp
|
||||
- subproc
|
||||
- tria
|
||||
suggestWords:
|
||||
- seperator:separator
|
||||
- vertice:vertex
|
||||
# cspell:enable
|
||||
|
||||
patterns:
|
||||
- name: character-set-cyrillic
|
||||
pattern: '/\p{Script_Extensions=Cyrillic}+/gu'
|
||||
- name: svg-block
|
||||
pattern: '<svg[\S\s]+?</svg>'
|
||||
- name: json-property
|
||||
pattern: '/"[\w/@-]+":/g'
|
||||
|
||||
dictionaries:
|
||||
- mermaid-terms
|
||||
- suggestions
|
||||
- contributors
|
||||
|
||||
ignorePaths:
|
||||
- '*.txt' # do not spell check local dictionaries
|
||||
|
||||
# cspell:dictionary misc-terms
|
||||
@@ -1,81 +0,0 @@
|
||||
# Add third party library terms below
|
||||
acyclicer
|
||||
Antlr
|
||||
Appli
|
||||
applitools
|
||||
Asciidoctor
|
||||
Astah
|
||||
automerge
|
||||
bilkent
|
||||
bisheng
|
||||
Blazor
|
||||
codedoc
|
||||
Codemia
|
||||
codepaths
|
||||
csstree
|
||||
cytoscape
|
||||
cytoscape-cose-bilkent
|
||||
dagre
|
||||
dagre-d3
|
||||
Deepdwn
|
||||
Docsify
|
||||
Docsy
|
||||
Doctave
|
||||
DokuWiki
|
||||
dompurify
|
||||
elkjs
|
||||
fcose
|
||||
fontawesome
|
||||
Fonticons
|
||||
Forgejo
|
||||
Foswiki
|
||||
Gitea
|
||||
graphlib
|
||||
Grav
|
||||
icones
|
||||
iconify
|
||||
Inkdrop
|
||||
jiti
|
||||
jsdocs
|
||||
jsfiddle
|
||||
jsonschema
|
||||
katex
|
||||
khroma
|
||||
langium
|
||||
mathml
|
||||
matplotlib
|
||||
mdbook
|
||||
Mermerd
|
||||
mkdocs
|
||||
Nextra
|
||||
nodenext
|
||||
npmjs
|
||||
pageview
|
||||
pathe
|
||||
phpbb
|
||||
pixelmatch
|
||||
Podlite
|
||||
presetAttributify
|
||||
pyplot
|
||||
redmine
|
||||
rehype
|
||||
roughjs
|
||||
rscratch
|
||||
shiki
|
||||
Slidev
|
||||
sparkline
|
||||
speccharts
|
||||
sphinxcontrib
|
||||
ssim
|
||||
stylis
|
||||
Swimm
|
||||
tsbuildinfo
|
||||
tseslint
|
||||
Tuleap
|
||||
Typora
|
||||
unocss
|
||||
unplugin
|
||||
unstub
|
||||
vite
|
||||
vitest
|
||||
Zune
|
||||
@@ -1,45 +0,0 @@
|
||||
Adamiecki
|
||||
arrowend
|
||||
Bendpoints
|
||||
bmatrix
|
||||
braintree
|
||||
catmull
|
||||
compositTitleSize
|
||||
cose
|
||||
curv
|
||||
doublecircle
|
||||
elem
|
||||
elems
|
||||
gantt
|
||||
gitgraph
|
||||
gzipped
|
||||
handDrawn
|
||||
kanban
|
||||
marginx
|
||||
marginy
|
||||
Markdownish
|
||||
mermaidchart
|
||||
mermaidjs
|
||||
mindmap
|
||||
mindmaps
|
||||
mrtree
|
||||
multigraph
|
||||
nodesep
|
||||
NOTEGROUP
|
||||
Pinterest
|
||||
procs
|
||||
rankdir
|
||||
ranksep
|
||||
rect
|
||||
rects
|
||||
sandboxed
|
||||
siebling
|
||||
statediagram
|
||||
substate
|
||||
unfixable
|
||||
Viewbox
|
||||
viewports
|
||||
visio
|
||||
vitepress
|
||||
xlink
|
||||
xychart
|
||||
@@ -1,8 +0,0 @@
|
||||
BRANDES
|
||||
Buzan
|
||||
circo
|
||||
handDrawn
|
||||
KOEPF
|
||||
neato
|
||||
newbranch
|
||||
validify
|
||||
@@ -1,107 +0,0 @@
|
||||
import { build } from 'esbuild';
|
||||
import { cp, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
import type { MermaidBuildOptions } from './util.js';
|
||||
import { defaultOptions, getBuildConfig } from './util.js';
|
||||
|
||||
const shouldVisualize = process.argv.includes('--visualize');
|
||||
|
||||
const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
||||
const commonOptions: MermaidBuildOptions = {
|
||||
...defaultOptions,
|
||||
options: packageOptions[entryName],
|
||||
} as const;
|
||||
const buildConfigs: MermaidBuildOptions[] = [
|
||||
// package.mjs
|
||||
{ ...commonOptions },
|
||||
// package.min.mjs
|
||||
{
|
||||
...commonOptions,
|
||||
minify: true,
|
||||
metafile: shouldVisualize,
|
||||
},
|
||||
// package.core.mjs
|
||||
{ ...commonOptions, core: true },
|
||||
];
|
||||
|
||||
if (entryName === 'mermaid') {
|
||||
const iifeOptions: MermaidBuildOptions = { ...commonOptions, format: 'iife' };
|
||||
buildConfigs.push(
|
||||
// mermaid.js
|
||||
{ ...iifeOptions },
|
||||
// mermaid.min.js
|
||||
{ ...iifeOptions, minify: true, metafile: shouldVisualize },
|
||||
// mermaid.tiny.min.js
|
||||
{
|
||||
...iifeOptions,
|
||||
minify: true,
|
||||
includeLargeFeatures: false,
|
||||
metafile: shouldVisualize,
|
||||
sourcemap: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (entryName === 'mermaid-zenuml') {
|
||||
const iifeOptions: MermaidBuildOptions = {
|
||||
...commonOptions,
|
||||
format: 'iife',
|
||||
globalName: 'mermaid-zenuml',
|
||||
};
|
||||
buildConfigs.push(
|
||||
// mermaid-zenuml.js
|
||||
{ ...iifeOptions },
|
||||
// mermaid-zenuml.min.js
|
||||
{ ...iifeOptions, minify: true, metafile: shouldVisualize }
|
||||
);
|
||||
}
|
||||
|
||||
const results = await Promise.all(buildConfigs.map((option) => build(getBuildConfig(option))));
|
||||
|
||||
if (shouldVisualize) {
|
||||
for (const { metafile } of results) {
|
||||
if (!metafile?.outputs) {
|
||||
continue;
|
||||
}
|
||||
const fileName = Object.keys(metafile.outputs)
|
||||
.find((file) => !file.includes('chunks') && file.endsWith('js'))!
|
||||
.replace('dist/', '');
|
||||
// Upload metafile into https://esbuild.github.io/analyze/
|
||||
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handler = (e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
const buildTinyMermaid = async () => {
|
||||
await mkdir('./packages/tiny/dist', { recursive: true });
|
||||
await rename(
|
||||
'./packages/mermaid/dist/mermaid.tiny.min.js',
|
||||
'./packages/tiny/dist/mermaid.tiny.js'
|
||||
);
|
||||
// Copy version from mermaid's package.json to tiny's package.json
|
||||
const mermaidPkg = JSON.parse(await readFile('./packages/mermaid/package.json', 'utf8'));
|
||||
const tinyPkg = JSON.parse(await readFile('./packages/tiny/package.json', 'utf8'));
|
||||
tinyPkg.version = mermaidPkg.version;
|
||||
|
||||
await writeFile('./packages/tiny/package.json', JSON.stringify(tinyPkg, null, 2) + '\n');
|
||||
await cp('./packages/mermaid/CHANGELOG.md', './packages/tiny/CHANGELOG.md');
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
await generateLangium();
|
||||
await mkdir('stats', { recursive: true });
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
// it should build `parser` before `mermaid` because it's a dependency
|
||||
for (const pkg of packageNames) {
|
||||
await buildPackage(pkg).catch(handler);
|
||||
}
|
||||
await buildTinyMermaid();
|
||||
};
|
||||
|
||||
void main();
|
||||
@@ -1,53 +0,0 @@
|
||||
# Dev Explorer frontend: architecture + debugging notes
|
||||
|
||||
## Root cause of the “CSS changes do nothing” problem
|
||||
|
||||
The page loads `/dev/styles.css`, but **document-level CSS does not apply through a shadow DOM boundary**.
|
||||
|
||||
Historically, `dev-explorer-app` was a `LitElement` using Lit’s default `shadowRoot`, while the rest of the UI used light DOM. That meant:
|
||||
|
||||
- The browser showed the _right classes_ (`card`, `card-folder`, `card-file`) in Elements panel.
|
||||
- `/dev/styles.css` was clearly being served/updated.
|
||||
- Yet computed styles for `.card` looked like UA defaults because the selector never matched across the shadow root.
|
||||
|
||||
Fix: make `dev-explorer-app` light DOM too (`createRenderRoot() { return this; }`), so `/dev/styles.css` reliably styles the whole UI.
|
||||
|
||||
## Debugging traps (and fast detection)
|
||||
|
||||
- **Shadow DOM trap**
|
||||
- Symptom: “CSS is loaded but doesn’t apply”, especially for simple class selectors.
|
||||
- Fast check:
|
||||
- DevTools console: `document.querySelector('dev-explorer-app')?.shadowRoot`
|
||||
- If non-null, global CSS won’t style inside it.
|
||||
- Or: right-click an element you expect styled → “Reveal in Elements” → see if it’s under `#shadow-root`.
|
||||
|
||||
- **“Light DOM child inside shadow DOM parent” trap**
|
||||
- Even if a child component uses `createRenderRoot() { return this; }`,
|
||||
if it’s _rendered inside the parent’s shadow root_, it’s still effectively in shadow for document styles.
|
||||
|
||||
- **Dev loop trap (CSS-only changes don’t trigger reload)**
|
||||
- The server watches TypeScript bundle inputs + `.mmd` files; static `/dev/styles.css` previously didn’t emit SSE reload events.
|
||||
- That makes CSS changes look flaky unless you manually refresh.
|
||||
- Fix: watch `.esbuild/dev-explorer/public/**/*` and emit SSE on changes.
|
||||
|
||||
- **Caching trap (less common here, but real)**
|
||||
- If a query param is constant (`?v=3`) and you don’t reload, the browser can keep a cached stylesheet.
|
||||
- Fast check: DevTools → Network → disable cache + hard reload; or check “(from disk cache)” on the CSS request.
|
||||
|
||||
## Styling strategy recommendation (pragmatic)
|
||||
|
||||
For a dev-only explorer, keep it simple:
|
||||
|
||||
- **Light DOM everywhere**
|
||||
- **One stylesheet**: `.esbuild/dev-explorer/public/styles.css` served as `/dev/styles.css`
|
||||
- **Scoped selectors** under `dev-explorer-app` to avoid generic class collisions (`.header`, `.content`, etc.)
|
||||
|
||||
If you later _want_ Shadow DOM isolation, do it deliberately:
|
||||
|
||||
- Put UI styles in Lit `static styles` or adopt a `CSSStyleSheet` into `this.renderRoot.adoptedStyleSheets`.
|
||||
- Avoid relying on document CSS selectors for component internals.
|
||||
|
||||
## Shoelace integration notes
|
||||
|
||||
- Current setup is correct for dev: `setBasePath('/dev/vendor/shoelace')` and `registerIconLibrary(...)`.
|
||||
- Prefer theming via CSS variables (Shoelace tokens) rather than overriding internal parts everywhere.
|
||||
@@ -1,187 +0,0 @@
|
||||
import { LitElement, html } from 'lit';
|
||||
|
||||
import '@shoelace-style/shoelace/dist/components/input/input.js';
|
||||
|
||||
export type LogLevel = 'info' | 'warn' | 'error';
|
||||
|
||||
export type LogEntry = {
|
||||
ts: number;
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
};
|
||||
|
||||
function formatTs(ts: number) {
|
||||
const d = new Date(ts);
|
||||
return (
|
||||
d.toLocaleTimeString(undefined, { hour12: false }) +
|
||||
'.' +
|
||||
String(d.getMilliseconds()).padStart(3, '0')
|
||||
);
|
||||
}
|
||||
|
||||
function levelVariant(level: LogLevel) {
|
||||
switch (level) {
|
||||
case 'error':
|
||||
return 'danger';
|
||||
case 'warn':
|
||||
return 'warning';
|
||||
default:
|
||||
return 'neutral';
|
||||
}
|
||||
}
|
||||
|
||||
type DisplayLevel = 'debug' | LogLevel;
|
||||
|
||||
function displayLevel(entry: LogEntry): DisplayLevel {
|
||||
// Mermaid often emits debug lines through console.log/info with a marker.
|
||||
if (entry.message.includes(': DEBUG :')) return 'debug';
|
||||
return entry.level;
|
||||
}
|
||||
|
||||
function displayVariant(level: DisplayLevel) {
|
||||
switch (level) {
|
||||
case 'error':
|
||||
return 'danger';
|
||||
case 'warn':
|
||||
return 'warning';
|
||||
case 'debug':
|
||||
return 'success';
|
||||
default:
|
||||
return 'neutral';
|
||||
}
|
||||
}
|
||||
|
||||
export class DevConsolePanel extends LitElement {
|
||||
static properties = {
|
||||
logs: { state: true },
|
||||
showInfo: { state: true },
|
||||
showWarn: { state: true },
|
||||
showError: { state: true },
|
||||
showDebug: { state: true },
|
||||
filterText: { state: true },
|
||||
};
|
||||
|
||||
declare logs: LogEntry[];
|
||||
declare showInfo: boolean;
|
||||
declare showWarn: boolean;
|
||||
declare showError: boolean;
|
||||
declare showDebug: boolean;
|
||||
declare filterText: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.logs = [];
|
||||
this.showInfo = true;
|
||||
this.showWarn = true;
|
||||
this.showError = true;
|
||||
this.showDebug = true;
|
||||
this.filterText = '';
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.logs = [];
|
||||
}
|
||||
|
||||
append(entry: LogEntry) {
|
||||
this.logs = [...this.logs, entry];
|
||||
}
|
||||
|
||||
async copyVisible() {
|
||||
const visible = this.filteredLogs();
|
||||
const text = visible
|
||||
.map((l) => `[${formatTs(l.ts)}] ${l.level.toUpperCase()} ${l.message}`)
|
||||
.join('\n');
|
||||
await navigator.clipboard.writeText(text);
|
||||
}
|
||||
|
||||
filteredLogs() {
|
||||
const q = this.filterText.trim().toLowerCase();
|
||||
return this.logs.filter((l) => {
|
||||
const isDebugLine = l.message.includes(': DEBUG :');
|
||||
// Treat debug-marked lines as their own independent toggle, since Mermaid often routes them through
|
||||
// console.log/info with a marker rather than a distinct "debug" level.
|
||||
if (isDebugLine && !this.showDebug) return false;
|
||||
|
||||
if (!isDebugLine) {
|
||||
const levelOk =
|
||||
l.level === 'info' ? this.showInfo : l.level === 'warn' ? this.showWarn : this.showError;
|
||||
if (!levelOk) return false;
|
||||
}
|
||||
|
||||
if (!q) return true;
|
||||
return l.message.toLowerCase().includes(q);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const visible = this.filteredLogs();
|
||||
return html`
|
||||
<div class="console">
|
||||
<div class="console-toolbar">
|
||||
<div class="spacer"></div>
|
||||
<sl-input
|
||||
size="small"
|
||||
placeholder="filter…"
|
||||
clearable
|
||||
value=${this.filterText}
|
||||
@sl-input=${(e: any) => (this.filterText = e.target.value ?? '')}
|
||||
></sl-input>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showDebug}
|
||||
@sl-change=${(e: any) => (this.showDebug = e.target.checked)}
|
||||
>debug</sl-checkbox
|
||||
>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showInfo}
|
||||
@sl-change=${(e: any) => (this.showInfo = e.target.checked)}
|
||||
>info</sl-checkbox
|
||||
>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showWarn}
|
||||
@sl-change=${(e: any) => (this.showWarn = e.target.checked)}
|
||||
>warn</sl-checkbox
|
||||
>
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.showError}
|
||||
@sl-change=${(e: any) => (this.showError = e.target.checked)}
|
||||
>error</sl-checkbox
|
||||
>
|
||||
<sl-button size="small" variant="default" @click=${() => void this.copyVisible()}>
|
||||
<sl-icon slot="prefix" name="clipboard"></sl-icon>
|
||||
Copy
|
||||
</sl-button>
|
||||
<sl-button size="small" variant="default" @click=${() => this.clear()}>
|
||||
<sl-icon slot="prefix" name="trash"></sl-icon>
|
||||
Clear
|
||||
</sl-button>
|
||||
</div>
|
||||
<div class="console-body">
|
||||
${visible.length === 0
|
||||
? html`<div class="empty">No logs yet.</div>`
|
||||
: visible.map((l) => {
|
||||
const lvl = displayLevel(l);
|
||||
return html`
|
||||
<div class="logline">
|
||||
<div class="logmeta">
|
||||
<sl-badge variant=${displayVariant(lvl)}>${lvl}</sl-badge>
|
||||
<span class="path">${formatTs(l.ts)}</span>
|
||||
</div>
|
||||
<div>${l.message}</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-console-panel', DevConsolePanel);
|
||||
@@ -1,551 +0,0 @@
|
||||
import { LitElement, html, nothing } from 'lit';
|
||||
|
||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
|
||||
import '@shoelace-style/shoelace/dist/components/select/select.js';
|
||||
import '@shoelace-style/shoelace/dist/components/option/option.js';
|
||||
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
|
||||
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
|
||||
|
||||
import './console-panel';
|
||||
import type { LogEntry, LogLevel } from './console-panel';
|
||||
|
||||
type MermaidIife = {
|
||||
initialize: (config: Record<string, unknown>) => void | Promise<void>;
|
||||
render: (
|
||||
id: string,
|
||||
text: string,
|
||||
container?: Element
|
||||
) => Promise<{ svg: string; bindFunctions?: (el: Element) => void }>;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
mermaid?: MermaidIife;
|
||||
mermaidReady?: Promise<MermaidIife>;
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyArgs(args: unknown[]) {
|
||||
// Mermaid's internal logger frequently uses console formatting like:
|
||||
// console.log('%c...message...', 'color: lightgreen', ...)
|
||||
// For the log panel we want the human text, not the formatting tokens/styles.
|
||||
let normalized = [...args];
|
||||
if (typeof normalized[0] === 'string') {
|
||||
const fmt = normalized[0];
|
||||
const cssCount = (fmt.match(/%c/g) ?? []).length;
|
||||
if (cssCount > 0) {
|
||||
normalized[0] = fmt.replaceAll('%c', '');
|
||||
// Drop the corresponding CSS args that follow the format string.
|
||||
normalized.splice(1, cssCount);
|
||||
}
|
||||
}
|
||||
|
||||
return normalized
|
||||
.map((a) => {
|
||||
if (typeof a === 'string') return a;
|
||||
if (a instanceof Error) return a.stack ?? a.message;
|
||||
try {
|
||||
return JSON.stringify(a);
|
||||
} catch {
|
||||
return String(a);
|
||||
}
|
||||
})
|
||||
.join(' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
type MermaidTheme = 'default' | 'dark' | 'forest' | 'neutral' | 'base';
|
||||
type MermaidLayout = 'dagre' | 'elk';
|
||||
type MermaidLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
|
||||
const DEFAULT_THEME: MermaidTheme = 'default';
|
||||
const DEFAULT_LAYOUT: MermaidLayout = 'dagre';
|
||||
const DEFAULT_MERMAID_LOG_LEVEL: MermaidLogLevel = 'warn';
|
||||
|
||||
function readUrlParam(name: string) {
|
||||
try {
|
||||
return new URL(window.location.href).searchParams.get(name);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setUrlParams(pairs: Record<string, string | null | undefined>) {
|
||||
const url = new URL(window.location.href);
|
||||
for (const [k, v] of Object.entries(pairs)) {
|
||||
if (!v) url.searchParams.delete(k);
|
||||
else url.searchParams.set(k, v);
|
||||
}
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
function readStorage(key: string) {
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeStorage(key: string, value: string) {
|
||||
try {
|
||||
localStorage.setItem(key, value);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function isTheme(v: unknown): v is MermaidTheme {
|
||||
return v === 'default' || v === 'dark' || v === 'forest' || v === 'neutral' || v === 'base';
|
||||
}
|
||||
|
||||
function isLayout(v: unknown): v is MermaidLayout {
|
||||
return v === 'dagre' || v === 'elk';
|
||||
}
|
||||
|
||||
function isMermaidLogLevel(v: unknown): v is MermaidLogLevel {
|
||||
return (
|
||||
v === 'trace' || v === 'debug' || v === 'info' || v === 'warn' || v === 'error' || v === 'fatal'
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeLayout(v: unknown): MermaidLayout | null {
|
||||
// Back-compat:
|
||||
// - older UI used `renderer=dagre-d3|dagre-wrapper|elk`
|
||||
// - new UI uses `layout=dagre|elk`
|
||||
if (v === 'dagre' || v === 'elk') return v;
|
||||
if (v === 'dagre-d3' || v === 'dagre-wrapper') return 'dagre';
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseBoolean(v: unknown): boolean | null {
|
||||
if (typeof v !== 'string') return null;
|
||||
const s = v.trim().toLowerCase();
|
||||
if (['1', 'true', 'yes', 'on'].includes(s)) return true;
|
||||
if (['0', 'false', 'no', 'off'].includes(s)) return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
export class DevDiagramViewer extends LitElement {
|
||||
static properties = {
|
||||
filePath: { type: String },
|
||||
sseToken: { type: Number },
|
||||
theme: { state: true },
|
||||
layout: { state: true },
|
||||
mermaidLogLevel: { state: true },
|
||||
useMaxWidth: { state: true },
|
||||
loading: { state: true },
|
||||
error: { state: true },
|
||||
source: { state: true },
|
||||
svg: { state: true },
|
||||
};
|
||||
|
||||
declare filePath: string;
|
||||
declare sseToken: number;
|
||||
declare theme: MermaidTheme;
|
||||
declare layout: MermaidLayout;
|
||||
declare mermaidLogLevel: MermaidLogLevel;
|
||||
declare useMaxWidth: boolean;
|
||||
declare loading: boolean;
|
||||
declare error: string;
|
||||
declare source: string;
|
||||
declare svg: string;
|
||||
|
||||
#renderSeq = 0;
|
||||
#consolePatched = false;
|
||||
#originalConsole?: {
|
||||
log: typeof console.log;
|
||||
info: typeof console.info;
|
||||
debug: typeof console.debug;
|
||||
warn: typeof console.warn;
|
||||
error: typeof console.error;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const themeParam = readUrlParam('theme');
|
||||
const layoutParam = readUrlParam('layout');
|
||||
const rendererParam = readUrlParam('renderer'); // legacy
|
||||
const logParam = readUrlParam('logLevel');
|
||||
const useMaxWidthParam = readUrlParam('useMaxWidth');
|
||||
|
||||
const storedTheme = readStorage('devExplorer.viewer.theme');
|
||||
const storedLayout = readStorage('devExplorer.viewer.layout');
|
||||
const storedRenderer = readStorage('devExplorer.viewer.renderer'); // legacy
|
||||
const storedLog = readStorage('devExplorer.viewer.logLevel');
|
||||
const storedUseMaxWidth = readStorage('devExplorer.viewer.useMaxWidth');
|
||||
|
||||
this.theme = isTheme(themeParam)
|
||||
? themeParam
|
||||
: isTheme(storedTheme)
|
||||
? storedTheme
|
||||
: DEFAULT_THEME;
|
||||
this.layout =
|
||||
normalizeLayout(layoutParam) ??
|
||||
normalizeLayout(rendererParam) ??
|
||||
normalizeLayout(storedLayout) ??
|
||||
normalizeLayout(storedRenderer) ??
|
||||
DEFAULT_LAYOUT;
|
||||
this.mermaidLogLevel = isMermaidLogLevel(logParam)
|
||||
? logParam
|
||||
: isMermaidLogLevel(storedLog)
|
||||
? storedLog
|
||||
: DEFAULT_MERMAID_LOG_LEVEL;
|
||||
|
||||
this.useMaxWidth = parseBoolean(useMaxWidthParam) ?? parseBoolean(storedUseMaxWidth) ?? true;
|
||||
|
||||
this.filePath = '';
|
||||
this.sseToken = 0;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.source = '';
|
||||
this.svg = '';
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.#installConsoleCapture();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.#restoreConsoleCapture();
|
||||
}
|
||||
|
||||
updated(changed: Map<string, unknown>) {
|
||||
if (changed.has('filePath')) {
|
||||
void this.#loadAndRender();
|
||||
} else if (changed.has('sseToken')) {
|
||||
// On rebuild events, re-fetch + re-render the currently open diagram.
|
||||
if (this.filePath) void this.#loadAndRender();
|
||||
} else if (
|
||||
changed.has('theme') ||
|
||||
changed.has('layout') ||
|
||||
changed.has('mermaidLogLevel') ||
|
||||
changed.has('useMaxWidth')
|
||||
) {
|
||||
// Re-render the currently loaded diagram with the new config without refetching.
|
||||
if (this.source) void this.#renderCurrentSource();
|
||||
}
|
||||
}
|
||||
|
||||
#back() {
|
||||
this.dispatchEvent(new CustomEvent('back', { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
#persistSettings() {
|
||||
writeStorage('devExplorer.viewer.theme', this.theme);
|
||||
writeStorage('devExplorer.viewer.layout', this.layout);
|
||||
writeStorage('devExplorer.viewer.logLevel', this.mermaidLogLevel);
|
||||
writeStorage('devExplorer.viewer.useMaxWidth', String(this.useMaxWidth));
|
||||
setUrlParams({
|
||||
theme: this.theme,
|
||||
layout: this.layout,
|
||||
renderer: null, // drop legacy param
|
||||
logLevel: this.mermaidLogLevel,
|
||||
useMaxWidth: this.useMaxWidth ? '1' : '0',
|
||||
});
|
||||
}
|
||||
|
||||
#syncConsolePanelFilters() {
|
||||
const panel = this.querySelector('dev-console-panel') as any;
|
||||
if (!panel) return;
|
||||
// This is intentionally opinionated: less noise by default as logLevel increases.
|
||||
if (
|
||||
this.mermaidLogLevel === 'trace' ||
|
||||
this.mermaidLogLevel === 'debug' ||
|
||||
this.mermaidLogLevel === 'info'
|
||||
) {
|
||||
panel.showInfo = true;
|
||||
panel.showWarn = true;
|
||||
panel.showError = true;
|
||||
return;
|
||||
}
|
||||
if (this.mermaidLogLevel === 'warn') {
|
||||
panel.showInfo = false;
|
||||
panel.showWarn = true;
|
||||
panel.showError = true;
|
||||
return;
|
||||
}
|
||||
// error / fatal
|
||||
panel.showInfo = false;
|
||||
panel.showWarn = false;
|
||||
panel.showError = true;
|
||||
}
|
||||
|
||||
#appendLog(entry: LogEntry) {
|
||||
const panel = this.querySelector('dev-console-panel') as any;
|
||||
panel?.append?.(entry);
|
||||
}
|
||||
|
||||
#installConsoleCapture() {
|
||||
if (this.#consolePatched) return;
|
||||
this.#consolePatched = true;
|
||||
|
||||
this.#originalConsole = {
|
||||
log: console.log,
|
||||
info: console.info,
|
||||
debug: console.debug,
|
||||
warn: console.warn,
|
||||
error: console.error,
|
||||
};
|
||||
|
||||
const capture = (level: LogLevel, args: unknown[]) => {
|
||||
this.#appendLog({
|
||||
ts: Date.now(),
|
||||
level,
|
||||
message: stringifyArgs(args),
|
||||
});
|
||||
};
|
||||
|
||||
// Mermaid uses its own logger which routes to console.info/debug/warn/error.
|
||||
// Capture those too (map debug/info/log -> panel "info").
|
||||
console.log = (...args) => {
|
||||
capture('info', args);
|
||||
this.#originalConsole!.log.apply(console, args as any);
|
||||
};
|
||||
console.info = (...args) => {
|
||||
capture('info', args);
|
||||
this.#originalConsole!.info.apply(console, args as any);
|
||||
};
|
||||
console.debug = (...args) => {
|
||||
capture('info', args);
|
||||
this.#originalConsole!.debug.apply(console, args as any);
|
||||
};
|
||||
console.warn = (...args) => {
|
||||
capture('warn', args);
|
||||
this.#originalConsole!.warn.apply(console, args as any);
|
||||
};
|
||||
console.error = (...args) => {
|
||||
capture('error', args);
|
||||
this.#originalConsole!.error.apply(console, args as any);
|
||||
};
|
||||
}
|
||||
|
||||
#restoreConsoleCapture() {
|
||||
if (!this.#consolePatched) return;
|
||||
this.#consolePatched = false;
|
||||
if (!this.#originalConsole) return;
|
||||
console.log = this.#originalConsole.log;
|
||||
console.info = this.#originalConsole.info;
|
||||
console.debug = this.#originalConsole.debug;
|
||||
console.warn = this.#originalConsole.warn;
|
||||
console.error = this.#originalConsole.error;
|
||||
this.#originalConsole = undefined;
|
||||
}
|
||||
|
||||
#clearLogs() {
|
||||
const panel = this.querySelector('dev-console-panel') as any;
|
||||
panel?.clear?.();
|
||||
}
|
||||
|
||||
async #fetchSource() {
|
||||
const url = new URL('/dev/api/file', window.location.origin);
|
||||
url.searchParams.set('path', this.filePath);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return await res.text();
|
||||
}
|
||||
|
||||
async #loadAndRender() {
|
||||
const seq = ++this.#renderSeq;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.svg = '';
|
||||
this.#clearLogs();
|
||||
this.#syncConsolePanelFilters();
|
||||
|
||||
try {
|
||||
const source = await this.#fetchSource();
|
||||
if (seq !== this.#renderSeq) return;
|
||||
this.source = source;
|
||||
await this.#renderMermaid(source);
|
||||
} catch (e) {
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async #renderCurrentSource() {
|
||||
const seq = ++this.#renderSeq;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.svg = '';
|
||||
this.#clearLogs();
|
||||
this.#syncConsolePanelFilters();
|
||||
try {
|
||||
const source = this.source;
|
||||
if (!source) return;
|
||||
if (seq !== this.#renderSeq) return;
|
||||
await this.#renderMermaid(source);
|
||||
} catch (e) {
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async #renderMermaid(text: string) {
|
||||
const m = (await window.mermaidReady?.catch(() => undefined)) ?? window.mermaid;
|
||||
if (!m) {
|
||||
throw new Error(
|
||||
'window.mermaid is not available (did /mermaid.esm.mjs load and did the bootstrap set window.mermaid?)'
|
||||
);
|
||||
}
|
||||
|
||||
const initConfig = {
|
||||
startOnLoad: false,
|
||||
securityLevel: 'strict',
|
||||
theme: this.theme,
|
||||
layout: this.layout,
|
||||
logLevel: this.mermaidLogLevel,
|
||||
flowchart: {
|
||||
useMaxWidth: this.useMaxWidth,
|
||||
},
|
||||
};
|
||||
|
||||
// Debugging aid: log exactly what we are about to initialize/render with.
|
||||
// Do it *before* initialize so detector issues can be correlated.
|
||||
const previewLimit = 4000;
|
||||
const preview =
|
||||
text.length > previewLimit
|
||||
? `${text.slice(0, previewLimit)}\n… (${text.length - previewLimit} more chars)`
|
||||
: text;
|
||||
console.log('[dev-explorer] mermaid.initialize config:', initConfig);
|
||||
console.log('[dev-explorer] diagram source preview:\n' + preview);
|
||||
|
||||
// Keep it deterministic-ish between reloads.
|
||||
await m.initialize(initConfig);
|
||||
|
||||
const id = `dev-explorer-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
const { svg, bindFunctions } = await m.render(id, text);
|
||||
this.svg = svg;
|
||||
// Allow mermaid to attach event handlers (e.g. links).
|
||||
await this.updateComplete;
|
||||
// If the page ever ended up scrolled down due to a previous oversized render, snap back to top.
|
||||
// (We intentionally removed vertical scrollbars in the viewer.)
|
||||
try {
|
||||
window.scrollTo(0, 0);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
const container = this.querySelector('.diagram-inner');
|
||||
if (container && bindFunctions) bindFunctions(container);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="header">
|
||||
<sl-button size="small" variant="default" @click=${() => this.#back()}>
|
||||
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
|
||||
Back
|
||||
</sl-button>
|
||||
<div style="min-width: 0;">
|
||||
<div class="title">Diagram</div>
|
||||
<div class="path">${this.filePath}</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="viewer-controls">
|
||||
<div class="control">
|
||||
<span class="label">Theme</span>
|
||||
<sl-select
|
||||
size="small"
|
||||
value=${this.theme}
|
||||
@sl-change=${(e: any) => {
|
||||
const v = e.target?.value;
|
||||
if (isTheme(v)) {
|
||||
this.theme = v;
|
||||
this.#persistSettings();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<sl-option value="default">default</sl-option>
|
||||
<sl-option value="dark">dark</sl-option>
|
||||
<sl-option value="forest">forest</sl-option>
|
||||
<sl-option value="neutral">neutral</sl-option>
|
||||
<sl-option value="base">base</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<span class="label">Layout</span>
|
||||
<sl-select
|
||||
size="small"
|
||||
value=${this.layout}
|
||||
@sl-change=${(e: any) => {
|
||||
const v = e.target?.value;
|
||||
if (isLayout(v)) {
|
||||
this.layout = v;
|
||||
this.#persistSettings();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<sl-option value="dagre">dagre</sl-option>
|
||||
<sl-option value="elk">elk</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<span class="label">Log</span>
|
||||
<sl-select
|
||||
size="small"
|
||||
value=${this.mermaidLogLevel}
|
||||
@sl-change=${(e: any) => {
|
||||
const v = e.target?.value;
|
||||
if (isMermaidLogLevel(v)) {
|
||||
this.mermaidLogLevel = v;
|
||||
this.#persistSettings();
|
||||
this.#syncConsolePanelFilters();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<sl-option value="trace">trace</sl-option>
|
||||
<sl-option value="debug">debug</sl-option>
|
||||
<sl-option value="info">info</sl-option>
|
||||
<sl-option value="warn">warn</sl-option>
|
||||
<sl-option value="error">error</sl-option>
|
||||
<sl-option value="fatal">fatal</sl-option>
|
||||
</sl-select>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<sl-checkbox
|
||||
size="small"
|
||||
?checked=${this.useMaxWidth}
|
||||
@sl-change=${(e: any) => {
|
||||
this.useMaxWidth = Boolean(e.target?.checked);
|
||||
this.#persistSettings();
|
||||
}}
|
||||
>useMaxWidth</sl-checkbox
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
${this.loading ? html`<div class="subtle">rendering…</div>` : nothing}
|
||||
</div>
|
||||
|
||||
${this.error
|
||||
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
|
||||
: nothing}
|
||||
|
||||
<div class="content">
|
||||
<sl-split-panel position="75" style="height: 100%;">
|
||||
<div slot="start" class="diagram">
|
||||
<div class="diagram-inner" data-theme=${this.theme} .innerHTML=${this.svg}></div>
|
||||
</div>
|
||||
<div slot="end" style="height: 100%;">
|
||||
<dev-console-panel></dev-console-panel>
|
||||
</div>
|
||||
</sl-split-panel>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-diagram-viewer', DevDiagramViewer);
|
||||
@@ -1,143 +0,0 @@
|
||||
import { LitElement, html, nothing } from 'lit';
|
||||
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
|
||||
import { registerIconLibrary } from '@shoelace-style/shoelace/dist/utilities/icon-library.js';
|
||||
|
||||
import '@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js';
|
||||
import '@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js';
|
||||
import '@shoelace-style/shoelace/dist/components/button/button.js';
|
||||
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
|
||||
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
|
||||
import '@shoelace-style/shoelace/dist/components/badge/badge.js';
|
||||
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
|
||||
|
||||
import './file-explorer';
|
||||
import './diagram-viewer';
|
||||
|
||||
type ViewMode = 'explorer' | 'viewer';
|
||||
|
||||
function getInitialStateFromUrl() {
|
||||
const url = new URL(window.location.href);
|
||||
const dir = url.searchParams.get('path') ?? '';
|
||||
const file = url.searchParams.get('file') ?? '';
|
||||
return { dir, file };
|
||||
}
|
||||
|
||||
function setUrlState({ dir, file }: { dir: string; file: string }) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.delete('path');
|
||||
url.searchParams.delete('file');
|
||||
if (dir) url.searchParams.set('path', dir);
|
||||
if (file) url.searchParams.set('file', file);
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
export class DevExplorerApp extends LitElement {
|
||||
static properties = {
|
||||
mode: { state: true },
|
||||
dirPath: { state: true },
|
||||
lastDirPath: { state: true },
|
||||
filePath: { state: true },
|
||||
sseToken: { state: true },
|
||||
};
|
||||
|
||||
declare mode: ViewMode;
|
||||
declare dirPath: string;
|
||||
declare lastDirPath: string;
|
||||
declare filePath: string;
|
||||
declare sseToken: number;
|
||||
|
||||
#events?: EventSource;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const { dir, file } = getInitialStateFromUrl();
|
||||
this.dirPath = dir;
|
||||
this.lastDirPath = dir;
|
||||
this.filePath = file;
|
||||
this.mode = file ? 'viewer' : 'explorer';
|
||||
this.sseToken = 0;
|
||||
|
||||
setBasePath('/dev/vendor/shoelace');
|
||||
registerIconLibrary('default', {
|
||||
resolver: (name) => `/dev/vendor/shoelace/assets/icons/${name}.svg`,
|
||||
});
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
// Use light DOM so document-level CSS (public/styles.css => /dev/styles.css) applies to the UI.
|
||||
// Without this, Lit's default shadow root will block global selectors like `.list` / `button.card`.
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.#events = new EventSource('/events');
|
||||
this.#events.onmessage = () => {
|
||||
this.sseToken++;
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', this.#onPopState);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.#events?.close();
|
||||
window.removeEventListener('popstate', this.#onPopState);
|
||||
}
|
||||
|
||||
#onPopState = () => {
|
||||
const { dir, file } = getInitialStateFromUrl();
|
||||
this.dirPath = dir;
|
||||
this.lastDirPath = dir;
|
||||
this.filePath = file;
|
||||
this.mode = file ? 'viewer' : 'explorer';
|
||||
};
|
||||
|
||||
#goToDir = (dir: string) => {
|
||||
this.dirPath = dir;
|
||||
this.lastDirPath = dir;
|
||||
this.mode = 'explorer';
|
||||
this.filePath = '';
|
||||
setUrlState({ dir, file: '' });
|
||||
};
|
||||
|
||||
#openFile = (filePath: string) => {
|
||||
this.filePath = filePath;
|
||||
this.mode = 'viewer';
|
||||
setUrlState({ dir: this.lastDirPath, file: filePath });
|
||||
};
|
||||
|
||||
#backToExplorer = () => {
|
||||
this.mode = 'explorer';
|
||||
this.filePath = '';
|
||||
setUrlState({ dir: this.lastDirPath, file: '' });
|
||||
};
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="app">
|
||||
${this.mode === 'explorer'
|
||||
? html`
|
||||
<dev-file-explorer
|
||||
.path=${this.dirPath}
|
||||
.sseToken=${this.sseToken}
|
||||
@navigate=${(e: CustomEvent<{ path: string }>) => this.#goToDir(e.detail.path)}
|
||||
@open-file=${(e: CustomEvent<{ path: string }>) => this.#openFile(e.detail.path)}
|
||||
></dev-file-explorer>
|
||||
`
|
||||
: nothing}
|
||||
${this.mode === 'viewer'
|
||||
? html`
|
||||
<dev-diagram-viewer
|
||||
.filePath=${this.filePath}
|
||||
.sseToken=${this.sseToken}
|
||||
@back=${this.#backToExplorer}
|
||||
></dev-diagram-viewer>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-explorer-app', DevExplorerApp);
|
||||
@@ -1,182 +0,0 @@
|
||||
import { LitElement, html, nothing } from 'lit';
|
||||
|
||||
type Entry = {
|
||||
name: string;
|
||||
kind: 'dir' | 'file';
|
||||
path: string;
|
||||
};
|
||||
|
||||
type FilesResponse = {
|
||||
root: string;
|
||||
path: string;
|
||||
entries: Entry[];
|
||||
};
|
||||
|
||||
function dirname(posixPath: string) {
|
||||
const p = posixPath.replaceAll('\\', '/').replace(/\/+$/, '');
|
||||
if (!p) return '';
|
||||
const idx = p.lastIndexOf('/');
|
||||
if (idx <= 0) return '';
|
||||
return p.slice(0, idx);
|
||||
}
|
||||
|
||||
function pathSegments(posixPath: string) {
|
||||
const p = posixPath.replaceAll('\\', '/').replace(/^\/+/, '').replace(/\/+$/, '');
|
||||
if (!p) return [];
|
||||
return p.split('/').filter(Boolean);
|
||||
}
|
||||
|
||||
export class DevFileExplorer extends LitElement {
|
||||
static properties = {
|
||||
path: { type: String },
|
||||
sseToken: { type: Number },
|
||||
loading: { state: true },
|
||||
error: { state: true },
|
||||
root: { state: true },
|
||||
entries: { state: true },
|
||||
};
|
||||
|
||||
declare path: string;
|
||||
declare sseToken: number;
|
||||
declare loading: boolean;
|
||||
declare error: string;
|
||||
declare root: string;
|
||||
declare entries: Entry[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.path = '';
|
||||
this.sseToken = 0;
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
this.root = '';
|
||||
this.entries = [];
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
// Use light DOM so global CSS in public/styles.css applies.
|
||||
return this;
|
||||
}
|
||||
|
||||
updated(changed: Map<string, unknown>) {
|
||||
if (changed.has('path') || changed.has('sseToken')) {
|
||||
void this.#load();
|
||||
}
|
||||
}
|
||||
|
||||
async #load() {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
try {
|
||||
const url = new URL('/dev/api/files', window.location.origin);
|
||||
if (this.path) url.searchParams.set('path', this.path);
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const json = (await res.json()) as FilesResponse;
|
||||
this.root = json.root ?? '';
|
||||
this.entries = json.entries ?? [];
|
||||
} catch (e) {
|
||||
this.error = e instanceof Error ? e.message : String(e);
|
||||
this.entries = [];
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
#emitNavigate(nextPath: string) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('navigate', { detail: { path: nextPath }, bubbles: true, composed: true })
|
||||
);
|
||||
}
|
||||
|
||||
#emitOpenFile(filePath: string) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('open-file', { detail: { path: filePath }, bubbles: true, composed: true })
|
||||
);
|
||||
}
|
||||
|
||||
#onActivate(kind: Entry['kind'], entryPath: string) {
|
||||
if (kind === 'dir') {
|
||||
this.#emitNavigate(entryPath);
|
||||
} else {
|
||||
this.#emitOpenFile(entryPath);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const segments = pathSegments(this.path);
|
||||
const itemLabel = this.entries.length === 1 ? 'item' : 'items';
|
||||
|
||||
return html`
|
||||
<div class="header">
|
||||
<div style="min-width: 0;">
|
||||
<div class="title">Dev Explorer</div>
|
||||
<div class="subtle">
|
||||
root:
|
||||
<span class="path">${this.root || 'cypress/platform/dev-diagrams'}</span>
|
||||
</div>
|
||||
<div style="margin-top: 6px;">
|
||||
<sl-breadcrumb>
|
||||
<sl-breadcrumb-item @click=${() => this.#emitNavigate('')}>root</sl-breadcrumb-item>
|
||||
${segments.map((seg, idx) => {
|
||||
const to = segments.slice(0, idx + 1).join('/');
|
||||
return html`<sl-breadcrumb-item @click=${() => this.#emitNavigate(to)}
|
||||
>${seg}</sl-breadcrumb-item
|
||||
>`;
|
||||
})}
|
||||
</sl-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="subtle">
|
||||
${this.loading ? 'loading…' : html`<span>${this.entries.length} ${itemLabel}</span>`}
|
||||
</div>
|
||||
<sl-button
|
||||
size="small"
|
||||
variant="default"
|
||||
?disabled=${!this.path}
|
||||
@click=${() => this.#emitNavigate(dirname(this.path))}
|
||||
>
|
||||
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
|
||||
Up
|
||||
</sl-button>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
${this.error
|
||||
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
|
||||
: nothing}
|
||||
${!this.error && !this.loading && this.entries.length === 0
|
||||
? html`<div class="empty">No folders or <span class="path">.mmd</span> files here.</div>`
|
||||
: nothing}
|
||||
|
||||
<div class="list">
|
||||
${this.entries.map((e) => {
|
||||
const icon = e.kind === 'dir' ? 'folder-fill' : 'file-earmark-code';
|
||||
const cardClass = e.kind === 'dir' ? 'card card-folder' : 'card card-file';
|
||||
const click =
|
||||
e.kind === 'dir'
|
||||
? () => this.#emitNavigate(e.path)
|
||||
: () => this.#emitOpenFile(e.path);
|
||||
const onKeyDown = (ev: KeyboardEvent) => {
|
||||
if (ev.key === 'Enter' || ev.key === ' ') {
|
||||
ev.preventDefault();
|
||||
this.#onActivate(e.kind, e.path);
|
||||
}
|
||||
};
|
||||
return html`
|
||||
<button class=${cardClass} type="button" @click=${click} @keydown=${onKeyDown}>
|
||||
<div class="card-inner">
|
||||
<sl-icon class="card-icon" name=${icon}></sl-icon>
|
||||
<div class="card-title">${e.name}</div>
|
||||
</div>
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('dev-file-explorer', DevFileExplorer);
|
||||
@@ -1,39 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="sl-theme-dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Mermaid Dev Explorer</title>
|
||||
|
||||
<link rel="stylesheet" href="/dev/vendor/shoelace/themes/dark.css" />
|
||||
<link rel="stylesheet" href="/dev/styles.css?v=6" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- Mermaid ESM + ELK layout loaders (required for `layout: 'elk'`) -->
|
||||
<script type="module">
|
||||
// Expose a single async hook so the app can reliably await Mermaid "activation".
|
||||
// This avoids races where the UI calls initialize/render before layouts/diagrams are ready.
|
||||
window.mermaidReady = (async () => {
|
||||
try {
|
||||
const [{ default: mermaid }, { default: layouts }] = await Promise.all([
|
||||
import('/mermaid.esm.mjs'),
|
||||
import('/mermaid-layout-elk.esm.mjs'),
|
||||
]);
|
||||
|
||||
mermaid.registerLayoutLoaders(layouts);
|
||||
|
||||
// Keep the rest of the dev explorer simple: expose mermaid on window for the Lit components.
|
||||
window.mermaid = mermaid;
|
||||
return mermaid;
|
||||
} catch (err) {
|
||||
console.error('[dev-explorer] Failed to initialize mermaid (ESM + elk loaders).', err);
|
||||
throw err;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<dev-explorer-app></dev-explorer-app>
|
||||
|
||||
<script type="module" src="/dev/assets/explorer-app.js?v=6"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,359 +0,0 @@
|
||||
/* Dev Explorer tokens. Keep on :root so the page background picks them up too. */
|
||||
:root {
|
||||
--app-bg: #0b1020;
|
||||
--app-fg: #e8eefc;
|
||||
--panel-bg: #0f1733;
|
||||
--muted: rgba(232, 238, 252, 0.75);
|
||||
--border: rgba(232, 238, 252, 0.12);
|
||||
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--app-bg);
|
||||
color: var(--app-fg);
|
||||
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
||||
/* Keep the page from becoming scrollable when components accidentally overflow. */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Scope app-level layout rules to avoid generic class collisions. */
|
||||
dev-explorer-app {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
dev-explorer-app .app {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Let the top-level view components actually fill the available vertical space. */
|
||||
dev-explorer-app dev-file-explorer,
|
||||
dev-explorer-app dev-diagram-viewer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
dev-explorer-app .header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--panel-bg);
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls .control {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls .label {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
dev-explorer-app .viewer-controls sl-select {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
dev-explorer-app .header .spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
dev-explorer-app .title {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
dev-explorer-app .subtle {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
dev-explorer-app .content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Diagram view should behave like a full-height canvas; avoid nested scrollbars. */
|
||||
dev-explorer-app dev-diagram-viewer .content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Shoelace split panel: ensure slot content can't overflow and push itself off-screen. */
|
||||
dev-explorer-app sl-split-panel {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
dev-explorer-app sl-split-panel::part(base) {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
dev-explorer-app sl-split-panel::part(start),
|
||||
dev-explorer-app sl-split-panel::part(end) {
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
dev-explorer-app .list {
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
grid-template-columns: repeat(auto-fill, 260px);
|
||||
}
|
||||
|
||||
/* Card base - use button element */
|
||||
dev-explorer-app button.card {
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
width: 260px;
|
||||
height: 200px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
/* Folder card style */
|
||||
dev-explorer-app button.card.card-folder {
|
||||
background: linear-gradient(160deg, #1a3052 0%, #0d1a2d 100%);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.4),
|
||||
0 2px 8px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-folder:hover {
|
||||
transform: translateY(-6px) scale(1.02);
|
||||
box-shadow:
|
||||
0 16px 48px rgba(0, 0, 0, 0.5),
|
||||
0 4px 12px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
/* File card style */
|
||||
dev-explorer-app button.card.card-file {
|
||||
background: linear-gradient(160deg, #1e2d4a 0%, #111827 100%);
|
||||
box-shadow:
|
||||
0 8px 32px rgba(0, 0, 0, 0.4),
|
||||
0 2px 8px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-file:hover {
|
||||
transform: translateY(-6px) scale(1.02);
|
||||
box-shadow:
|
||||
0 16px 48px rgba(0, 0, 0, 0.5),
|
||||
0 4px 12px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
dev-explorer-app button.card:focus-visible {
|
||||
outline: 3px solid rgba(96, 165, 250, 0.7);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
/* Subtle border overlay */
|
||||
dev-explorer-app button.card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
dev-explorer-app .card-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
padding: 24px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Icon styling */
|
||||
dev-explorer-app .card-icon {
|
||||
font-size: 56px !important;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-folder .card-icon {
|
||||
color: #f59e0b;
|
||||
filter: drop-shadow(0 4px 12px rgba(245, 158, 11, 0.4));
|
||||
}
|
||||
|
||||
dev-explorer-app button.card.card-file .card-icon {
|
||||
color: #3b82f6;
|
||||
filter: drop-shadow(0 4px 12px rgba(59, 130, 246, 0.35));
|
||||
}
|
||||
|
||||
/* Title styling */
|
||||
dev-explorer-app .card-title {
|
||||
font-size: 12px;
|
||||
font-family: var(--mono);
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 230px;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
dev-explorer-app .row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
dev-explorer-app .row:hover {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
dev-explorer-app .row sl-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
dev-explorer-app .path {
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
dev-explorer-app .diagram {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
dev-explorer-app .diagram-inner {
|
||||
/* Canvas background behind the rendered SVG (theme-dependent). */
|
||||
background: #fff;
|
||||
color: #111;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Fit the rendered SVG within the available pane (no scrollbars). */
|
||||
dev-explorer-app .diagram-inner > svg {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 100% !important;
|
||||
max-height: 100% !important;
|
||||
}
|
||||
|
||||
dev-explorer-app .diagram-inner[data-theme='dark'] {
|
||||
background: #0b1020;
|
||||
color: #e8eefc;
|
||||
}
|
||||
|
||||
dev-explorer-app .console {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-left: 1px solid var(--border);
|
||||
background: var(--panel-bg);
|
||||
}
|
||||
|
||||
dev-explorer-app .spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
dev-explorer-app .console-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
dev-explorer-app .console-toolbar sl-input {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
dev-explorer-app .console-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
dev-explorer-app .logline {
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
margin: 0 0 8px 0;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
dev-explorer-app .logmeta {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
dev-explorer-app .empty {
|
||||
padding: 18px 12px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
dev-explorer-app sl-breadcrumb::part(base) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { transformJison } from '../.build/jisonTransformer.js';
|
||||
import type { Plugin } from 'esbuild';
|
||||
|
||||
export const jisonPlugin: Plugin = {
|
||||
name: 'jison',
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /\.jison$/ }, async (args) => {
|
||||
// Load the file from the file system
|
||||
const source = await readFile(args.path, 'utf8');
|
||||
const contents = transformJison(source);
|
||||
return { contents, warnings: [] };
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { JSONSchemaType } from 'ajv/dist/2019.js';
|
||||
import type { MermaidConfig } from '../packages/mermaid/src/config.type.js';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
|
||||
|
||||
/**
|
||||
* ESBuild plugin that handles JSON Schemas saved as a `.schema.yaml` file.
|
||||
*
|
||||
* Use `my-example.schema.yaml?only-defaults=true` to only load the default values.
|
||||
*/
|
||||
|
||||
export const jsonSchemaPlugin = {
|
||||
name: 'json-schema-plugin',
|
||||
setup(build) {
|
||||
let schema: JSONSchemaType<MermaidConfig> | undefined = undefined;
|
||||
let content = '';
|
||||
|
||||
build.onLoad({ filter: /config\.schema\.yaml$/ }, async (args) => {
|
||||
// Load the file from the file system
|
||||
const source = await readFile(args.path, 'utf8');
|
||||
const resolvedSchema: JSONSchemaType<MermaidConfig> =
|
||||
content === source && schema ? schema : loadSchema(source, args.path);
|
||||
if (content !== source) {
|
||||
content = source;
|
||||
schema = resolvedSchema;
|
||||
}
|
||||
const contents = args.suffix.includes('only-defaults')
|
||||
? getDefaults(resolvedSchema)
|
||||
: getSchema(resolvedSchema);
|
||||
return { contents, warnings: [] };
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default jsonSchemaPlugin;
|
||||
@@ -1,311 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import chokidar from 'chokidar';
|
||||
import cors from 'cors';
|
||||
import { context } from 'esbuild';
|
||||
import { promises as fs } from 'fs';
|
||||
import type { Request, Response } from 'express';
|
||||
import express from 'express';
|
||||
import path, { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
import { defaultOptions, getBuildConfig } from './util.js';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
const configs = Object.values(packageOptions).map(({ packageName }) =>
|
||||
getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: false,
|
||||
core: false,
|
||||
options: packageOptions[packageName],
|
||||
})
|
||||
);
|
||||
const mermaidIIFEConfig = getBuildConfig({
|
||||
...defaultOptions,
|
||||
minify: true,
|
||||
core: false,
|
||||
options: packageOptions.mermaid,
|
||||
format: 'iife',
|
||||
});
|
||||
configs.push(mermaidIIFEConfig);
|
||||
|
||||
const contexts = await Promise.all(
|
||||
configs.map(async (config) => ({ config, context: await context(config) }))
|
||||
);
|
||||
|
||||
let rebuildCounter = 1;
|
||||
const rebuildAll = async () => {
|
||||
const buildNumber = rebuildCounter++;
|
||||
const timeLabel = `Rebuild ${buildNumber} Time (total)`;
|
||||
console.time(timeLabel);
|
||||
await Promise.all(
|
||||
contexts.map(async ({ config, context }) => {
|
||||
const buildVariant = `Rebuild ${buildNumber} Time (${Object.keys(config.entryPoints!)[0]} ${config.format})`;
|
||||
console.time(buildVariant);
|
||||
await context.rebuild();
|
||||
console.timeEnd(buildVariant);
|
||||
})
|
||||
).catch((e) => console.error(e));
|
||||
console.timeEnd(timeLabel);
|
||||
};
|
||||
|
||||
let clients: { id: number; response: Response }[] = [];
|
||||
function eventsHandler(request: Request, response: Response) {
|
||||
const headers = {
|
||||
'Content-Type': 'text/event-stream',
|
||||
Connection: 'keep-alive',
|
||||
'Cache-Control': 'no-cache',
|
||||
};
|
||||
response.writeHead(200, headers);
|
||||
const clientId = Date.now();
|
||||
clients.push({
|
||||
id: clientId,
|
||||
response,
|
||||
});
|
||||
request.on('close', () => {
|
||||
clients = clients.filter((client) => client.id !== clientId);
|
||||
});
|
||||
}
|
||||
|
||||
let timeoutID: NodeJS.Timeout | undefined = undefined;
|
||||
|
||||
/**
|
||||
* Debounce file change events to avoid rebuilding multiple times.
|
||||
*/
|
||||
function handleFileChange() {
|
||||
if (timeoutID !== undefined) {
|
||||
clearTimeout(timeoutID);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
timeoutID = setTimeout(async () => {
|
||||
await rebuildAll();
|
||||
sendEventsToAll();
|
||||
timeoutID = undefined;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function sendEventsToAll() {
|
||||
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
|
||||
}
|
||||
|
||||
interface DevExplorerEntry {
|
||||
name: string;
|
||||
kind: 'dir' | 'file';
|
||||
path: string; // posix-style, relative to root
|
||||
}
|
||||
|
||||
const devExplorerRootAbs = resolve(
|
||||
process.cwd(),
|
||||
process.env.MERMAID_DEV_EXPLORER_ROOT ?? 'cypress/platform/dev-diagrams'
|
||||
);
|
||||
|
||||
function toPosixPath(p: string) {
|
||||
return p.split(path.sep).join('/');
|
||||
}
|
||||
|
||||
function resolveWithinDevExplorerRoot(requestedPath: unknown) {
|
||||
const requested = typeof requestedPath === 'string' ? requestedPath : '';
|
||||
if (requested.includes('\0')) {
|
||||
throw new Error('Invalid path');
|
||||
}
|
||||
|
||||
// Normalize slashes and avoid weird absolute-path cases.
|
||||
const cleaned = requested.replaceAll('\\', '/').replace(/^\/+/, '');
|
||||
const absPath = resolve(devExplorerRootAbs, cleaned);
|
||||
const rel = path.relative(devExplorerRootAbs, absPath);
|
||||
|
||||
// Prevent traversal above root.
|
||||
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
||||
throw new Error('Path escapes configured root');
|
||||
}
|
||||
|
||||
return { absPath, relPath: toPosixPath(rel) };
|
||||
}
|
||||
|
||||
async function createDevExplorerBundle() {
|
||||
const devExplorerDir = resolve(__dirname, 'dev-explorer');
|
||||
const entryPoint = resolve(devExplorerDir, 'explorer-app.ts');
|
||||
const outDir = resolve(devExplorerDir, 'dist');
|
||||
|
||||
try {
|
||||
const devExplorerCtx = await context({
|
||||
absWorkingDir: process.cwd(),
|
||||
entryPoints: [entryPoint],
|
||||
bundle: true,
|
||||
format: 'esm',
|
||||
target: 'es2020',
|
||||
sourcemap: true,
|
||||
outdir: outDir,
|
||||
logLevel: 'info',
|
||||
plugins: [
|
||||
{
|
||||
name: 'dev-explorer-reload',
|
||||
setup(build) {
|
||||
build.onEnd(() => {
|
||||
sendEventsToAll();
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await devExplorerCtx.watch();
|
||||
await devExplorerCtx.rebuild();
|
||||
} catch (err) {
|
||||
console.error(
|
||||
[
|
||||
'Dev Explorer bundle build failed.',
|
||||
'Install dependencies: pnpm add -D lit @shoelace-style/shoelace',
|
||||
'Then restart the dev server.',
|
||||
].join('\n')
|
||||
);
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
async function createServer() {
|
||||
await generateLangium();
|
||||
handleFileChange();
|
||||
const app = express();
|
||||
chokidar
|
||||
.watch('**/src/**/*.{js,ts,langium,yaml,json}', {
|
||||
ignoreInitial: true,
|
||||
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
.on('all', async (event, path) => {
|
||||
// Ignore other events.
|
||||
if (!['add', 'change'].includes(event)) {
|
||||
return;
|
||||
}
|
||||
console.log(`${path} changed. Rebuilding...`);
|
||||
if (path.endsWith('.langium')) {
|
||||
await generateLangium();
|
||||
}
|
||||
handleFileChange();
|
||||
});
|
||||
|
||||
// Rebuild the dev-explorer client bundle on changes (and emit SSE so the browser reloads).
|
||||
await createDevExplorerBundle();
|
||||
|
||||
// Emit SSE when Dev Explorer static assets change (e.g. public/styles.css),
|
||||
// otherwise CSS-only changes can look "ignored" unless the user manually refreshes.
|
||||
chokidar
|
||||
.watch(['.esbuild/dev-explorer/public/**/*'], {
|
||||
ignoreInitial: true,
|
||||
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
|
||||
})
|
||||
.on('all', (event, changedPath) => {
|
||||
if (!['add', 'change', 'unlink'].includes(event)) {
|
||||
return;
|
||||
}
|
||||
console.log(`[dev-explorer] ${event}: ${changedPath}`);
|
||||
sendEventsToAll();
|
||||
});
|
||||
|
||||
// Emit SSE when .mmd files inside the configured explorer root change.
|
||||
chokidar
|
||||
.watch('**/*.mmd', {
|
||||
cwd: devExplorerRootAbs,
|
||||
ignoreInitial: true,
|
||||
})
|
||||
.on('all', (event, changedPath) => {
|
||||
if (!['add', 'change', 'unlink'].includes(event)) {
|
||||
return;
|
||||
}
|
||||
console.log(`[dev-explorer] ${event}: ${changedPath}`);
|
||||
sendEventsToAll();
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
app.get('/events', eventsHandler);
|
||||
|
||||
// --- Dev Explorer API + UI -------------------------------------------------
|
||||
const devExplorerDir = resolve(__dirname, 'dev-explorer');
|
||||
const devExplorerPublicDir = resolve(devExplorerDir, 'public');
|
||||
const devExplorerDistDir = resolve(devExplorerDir, 'dist');
|
||||
|
||||
// Shoelace assets (theme css + icons). Safe: only mounted in dev server.
|
||||
app.use(
|
||||
'/dev/vendor/shoelace',
|
||||
express.static(resolve(process.cwd(), 'node_modules/@shoelace-style/shoelace/dist'))
|
||||
);
|
||||
|
||||
app.get('/dev/api/files', async (req, res) => {
|
||||
try {
|
||||
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
|
||||
const stats = await fs.stat(absPath);
|
||||
if (!stats.isDirectory()) {
|
||||
res.status(400).json({ error: 'Not a directory' });
|
||||
return;
|
||||
}
|
||||
|
||||
const dirEntries = await fs.readdir(absPath, { withFileTypes: true });
|
||||
const entries = dirEntries
|
||||
.filter((d) => d.isDirectory() || (d.isFile() && d.name.endsWith('.mmd')))
|
||||
.map<DevExplorerEntry>((d) => ({
|
||||
name: d.name,
|
||||
kind: d.isDirectory() ? 'dir' : 'file',
|
||||
path: toPosixPath(path.join(relPath, d.name)),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.kind !== b.kind) {
|
||||
return a.kind === 'dir' ? -1 : 1;
|
||||
}
|
||||
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
|
||||
});
|
||||
|
||||
res.json({
|
||||
root: toPosixPath(path.relative(process.cwd(), devExplorerRootAbs)),
|
||||
path: relPath === '' ? '' : relPath,
|
||||
entries,
|
||||
});
|
||||
} catch (_e) {
|
||||
res.status(400).json({ error: 'Invalid path' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/dev/api/file', async (req, res) => {
|
||||
try {
|
||||
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
|
||||
if (!absPath.endsWith('.mmd')) {
|
||||
res.status(400).send('Only .mmd files are allowed');
|
||||
return;
|
||||
}
|
||||
const stats = await fs.stat(absPath);
|
||||
if (!stats.isFile()) {
|
||||
res.status(400).send('Not a file');
|
||||
return;
|
||||
}
|
||||
const content = await fs.readFile(absPath, 'utf-8');
|
||||
res.type('text/plain').send(content);
|
||||
// Optional: include relPath for debugging.
|
||||
void relPath;
|
||||
} catch (_e) {
|
||||
res.status(400).send('Invalid path');
|
||||
}
|
||||
});
|
||||
|
||||
// Static assets for the dev-explorer UI.
|
||||
app.use('/dev/assets', express.static(devExplorerDistDir));
|
||||
// Serve `/dev/` (and `/dev`) from public/, including index.html.
|
||||
app.use(
|
||||
'/dev',
|
||||
express.static(devExplorerPublicDir, {
|
||||
index: ['index.html'],
|
||||
})
|
||||
);
|
||||
|
||||
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'));
|
||||
|
||||
app.listen(9000, () => {
|
||||
console.log(`Listening on http://localhost:9000`);
|
||||
});
|
||||
}
|
||||
|
||||
void createServer();
|
||||
120
.esbuild/util.ts
120
.esbuild/util.ts
@@ -1,120 +0,0 @@
|
||||
import { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { BuildOptions } from 'esbuild';
|
||||
import { readFileSync } from 'fs';
|
||||
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
|
||||
import type { PackageOptions } from '../.build/common.js';
|
||||
import { jisonPlugin } from './jisonPlugin.js';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
export interface MermaidBuildOptions extends BuildOptions {
|
||||
minify: boolean;
|
||||
core: boolean;
|
||||
metafile: boolean;
|
||||
format: 'esm' | 'iife';
|
||||
options: PackageOptions;
|
||||
includeLargeFeatures: boolean;
|
||||
}
|
||||
|
||||
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'> = {
|
||||
minify: false,
|
||||
metafile: false,
|
||||
core: false,
|
||||
format: 'esm',
|
||||
includeLargeFeatures: true,
|
||||
} as const;
|
||||
|
||||
const buildOptions = (override: BuildOptions): BuildOptions => {
|
||||
return {
|
||||
bundle: true,
|
||||
minify: true,
|
||||
keepNames: true,
|
||||
platform: 'browser',
|
||||
tsconfig: 'tsconfig.json',
|
||||
resolveExtensions: ['.ts', '.js', '.json', '.jison', '.yaml'],
|
||||
external: ['require', 'fs', 'path'],
|
||||
outdir: 'dist',
|
||||
plugins: [jisonPlugin, jsonSchemaPlugin],
|
||||
sourcemap: 'external',
|
||||
...override,
|
||||
};
|
||||
};
|
||||
|
||||
const getFileName = (
|
||||
fileName: string,
|
||||
{ core, format, minify, includeLargeFeatures }: MermaidBuildOptions
|
||||
) => {
|
||||
if (core) {
|
||||
fileName += '.core';
|
||||
} else if (format === 'esm') {
|
||||
fileName += '.esm';
|
||||
}
|
||||
if (!includeLargeFeatures) {
|
||||
fileName += '.tiny';
|
||||
}
|
||||
if (minify) {
|
||||
fileName += '.min';
|
||||
}
|
||||
return fileName;
|
||||
};
|
||||
|
||||
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
|
||||
const {
|
||||
core,
|
||||
format,
|
||||
options: { name, file, packageName },
|
||||
globalName = 'mermaid',
|
||||
includeLargeFeatures,
|
||||
...rest
|
||||
} = options;
|
||||
|
||||
const external: string[] = ['require', 'fs', 'path'];
|
||||
const outFileName = getFileName(name, options);
|
||||
const { dependencies, version } = JSON.parse(
|
||||
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
|
||||
);
|
||||
const output: BuildOptions = buildOptions({
|
||||
...rest,
|
||||
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
|
||||
entryPoints: {
|
||||
[outFileName]: `src/${file}`,
|
||||
},
|
||||
globalName,
|
||||
logLevel: 'info',
|
||||
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
|
||||
define: {
|
||||
// This needs to be stringified for esbuild
|
||||
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
|
||||
'injected.version': `'${version}'`,
|
||||
'import.meta.vitest': 'undefined',
|
||||
},
|
||||
});
|
||||
|
||||
if (core) {
|
||||
// Core build is used to generate file without bundled dependencies.
|
||||
// This is used by downstream projects to bundle dependencies themselves.
|
||||
// Ignore dependencies and any dependencies of dependencies
|
||||
external.push(...Object.keys(dependencies));
|
||||
output.external = external;
|
||||
}
|
||||
|
||||
if (format === 'iife') {
|
||||
output.format = 'iife';
|
||||
output.splitting = false;
|
||||
const originalGlobalName = output.globalName ?? 'mermaid';
|
||||
output.globalName = `__esbuild_esm_mermaid_nm[${JSON.stringify(originalGlobalName)}]`;
|
||||
// Workaround for removing the .default access in esbuild IIFE.
|
||||
// https://github.com/mermaid-js/mermaid/pull/4109#discussion_r1292317396
|
||||
output.footer = {
|
||||
js: `globalThis[${JSON.stringify(originalGlobalName)}] = globalThis.${output.globalName}.default;`,
|
||||
};
|
||||
output.outExtension = { '.js': '.js' };
|
||||
} else {
|
||||
output.format = 'esm';
|
||||
output.splitting = true;
|
||||
output.outExtension = { '.js': '.mjs' };
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
8
.eslintignore
Normal file
8
.eslintignore
Normal file
@@ -0,0 +1,8 @@
|
||||
dist/**
|
||||
.github/**
|
||||
docs/Setup.md
|
||||
cypress.config.js
|
||||
cypress/plugins/index.js
|
||||
coverage
|
||||
*.json
|
||||
node_modules
|
||||
150
.eslintrc.cjs
Normal file
150
.eslintrc.cjs
Normal file
@@ -0,0 +1,150 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
'jest/globals': true,
|
||||
node: true,
|
||||
},
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
experimentalObjectRestSpread: true,
|
||||
jsx: true,
|
||||
},
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
allowAutomaticSingleRunInference: true,
|
||||
project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:json/recommended',
|
||||
'plugin:markdown/recommended',
|
||||
'plugin:@cspell/recommended',
|
||||
'prettier',
|
||||
],
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'no-only-tests',
|
||||
'html',
|
||||
'jest',
|
||||
'jsdoc',
|
||||
'json',
|
||||
'@cspell',
|
||||
'lodash',
|
||||
'unicorn',
|
||||
],
|
||||
rules: {
|
||||
curly: 'error',
|
||||
'no-console': 'error',
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'cypress/no-async-tests': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-misused-promises': 'error',
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
'ts-ignore': 'allow-with-description',
|
||||
'ts-nocheck': 'allow-with-description',
|
||||
'ts-check': 'allow-with-description',
|
||||
minimumDescriptionLength: 10,
|
||||
},
|
||||
],
|
||||
'json/*': ['error', 'allowComments'],
|
||||
'@cspell/spellchecker': [
|
||||
'error',
|
||||
{
|
||||
checkIdentifiers: false,
|
||||
checkStrings: false,
|
||||
checkStringTemplates: false,
|
||||
},
|
||||
],
|
||||
'no-empty': [
|
||||
'error',
|
||||
{
|
||||
allowEmptyCatch: true,
|
||||
},
|
||||
],
|
||||
'no-only-tests/no-only-tests': 'error',
|
||||
'lodash/import-scope': ['error', 'method'],
|
||||
'unicorn/better-regex': 'error',
|
||||
'unicorn/no-abusive-eslint-disable': 'error',
|
||||
'unicorn/no-array-push-push': 'error',
|
||||
'unicorn/no-for-loop': 'error',
|
||||
'unicorn/no-instanceof-array': 'error',
|
||||
'unicorn/no-typeof-undefined': 'error',
|
||||
'unicorn/no-unnecessary-await': 'error',
|
||||
'unicorn/no-unsafe-regex': 'warn',
|
||||
'unicorn/no-useless-promise-resolve-reject': 'error',
|
||||
'unicorn/prefer-array-find': 'error',
|
||||
'unicorn/prefer-array-flat-map': 'error',
|
||||
'unicorn/prefer-array-index-of': 'error',
|
||||
'unicorn/prefer-array-some': 'error',
|
||||
'unicorn/prefer-default-parameters': 'error',
|
||||
'unicorn/prefer-includes': 'error',
|
||||
'unicorn/prefer-negative-index': 'error',
|
||||
'unicorn/prefer-object-from-entries': 'error',
|
||||
'unicorn/prefer-string-starts-ends-with': 'error',
|
||||
'unicorn/prefer-string-trim-start-end': 'error',
|
||||
'unicorn/string-content': 'error',
|
||||
'unicorn/prefer-spread': 'error',
|
||||
'unicorn/no-lonely-if': 'error',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['cypress/**', 'demos/**'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.{js,jsx,mjs,cjs}'],
|
||||
extends: ['plugin:jsdoc/recommended'],
|
||||
rules: {
|
||||
'jsdoc/check-indentation': 'off',
|
||||
'jsdoc/check-alignment': 'off',
|
||||
'jsdoc/check-line-alignment': 'off',
|
||||
'jsdoc/multiline-blocks': 'off',
|
||||
'jsdoc/newline-after-description': 'off',
|
||||
'jsdoc/tag-lines': 'off',
|
||||
'jsdoc/require-param-description': 'off',
|
||||
'jsdoc/require-param-type': 'off',
|
||||
'jsdoc/require-returns': 'off',
|
||||
'jsdoc/require-returns-description': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.{ts,tsx}'],
|
||||
plugins: ['tsdoc'],
|
||||
rules: {
|
||||
'tsdoc/syntax': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.spec.{ts,js}', 'cypress/**', 'demos/**', '**/docs/**'],
|
||||
rules: {
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.html', '*.md', '**/*.md/*'],
|
||||
rules: {
|
||||
'no-var': 'error',
|
||||
'no-undef': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'@typescript-eslint/no-misused-promises': 'off',
|
||||
},
|
||||
parserOptions: {
|
||||
project: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -17,9 +17,6 @@ body:
|
||||
- Use a clear and concise title
|
||||
- Fill out the text fields with as much detail as possible.
|
||||
- Never be shy to give us screenshots and/or code samples. It will help!
|
||||
|
||||
There is a chance that the bug is already fixed in the git `develop` branch, but is not released yet.
|
||||
So please check in [Live Editor - Develop](https://develop.git.mermaid.live) before raising an issue.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
@@ -46,7 +43,7 @@ body:
|
||||
attributes:
|
||||
label: Code Sample
|
||||
description: |-
|
||||
If applicable, add the code sample or a link to the [Live Editor - Develop](https://develop.git.mermaid.live).
|
||||
If applicable, add the code sample or a link to the [Live Editor](https://mermaid.live).
|
||||
Any text pasted here will be rendered as a Code block.
|
||||
render: text
|
||||
- type: textarea
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
14
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,18 +3,12 @@ contact_links:
|
||||
- name: GitHub Discussions
|
||||
url: https://github.com/mermaid-js/mermaid/discussions
|
||||
about: Ask the Community questions or share your own graphs in our discussions.
|
||||
- name: Discord
|
||||
url: https://discord.gg/sKeNQX4Wtj
|
||||
about: Join our Community on Discord for Help and a casual chat.
|
||||
- name: Slack
|
||||
url: https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE
|
||||
about: Join our Community on Slack for Help and a casual chat.
|
||||
- name: Documentation
|
||||
url: https://mermaid.js.org
|
||||
url: https://mermaid-js.github.io
|
||||
about: Read our documentation for all that Mermaid.js can offer.
|
||||
- name: Live Editor
|
||||
url: https://mermaid.live
|
||||
about: Try the live editor to preview graphs in no time.
|
||||
- name: Live Editor - Develop
|
||||
url: https://develop.git.mermaid.live
|
||||
about: Try unreleased changes in the develop branch.
|
||||
- name: Live Editor - Next
|
||||
url: https://next.git.mermaid.live
|
||||
about: Try unreleased changes in the next branch.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/theme_proposal.yml
vendored
2
.github/ISSUE_TEMPLATE/theme_proposal.yml
vendored
@@ -29,7 +29,7 @@ body:
|
||||
label: Colors
|
||||
description: |-
|
||||
A detailed list of the different colour values to use.
|
||||
See the [list of currently used variable names](https://mermaid-js.github.io/mermaid/#/theming?id=theme-variables-reference-table)
|
||||
A list of currently used variable names can be found [here](https://mermaid-js.github.io/mermaid/#/theming?id=theme-variables-reference-table)
|
||||
placeholder: |-
|
||||
- background: #f4f4f4
|
||||
- primaryColor: #fff4dd
|
||||
|
||||
69
.github/lychee.toml
vendored
69
.github/lychee.toml
vendored
@@ -1,69 +0,0 @@
|
||||
############################# Display #############################
|
||||
|
||||
# Verbose program output
|
||||
# Accepts log level: "error", "warn", "info", "debug", "trace"
|
||||
verbose = "debug"
|
||||
|
||||
# Don't show interactive progress bar while checking links.
|
||||
no_progress = true
|
||||
|
||||
############################# Cache ###############################
|
||||
|
||||
# Enable link caching. This can be helpful to avoid checking the same links on
|
||||
# multiple runs.
|
||||
cache = true
|
||||
|
||||
# Discard all cached requests older than this duration.
|
||||
max_cache_age = "1d"
|
||||
|
||||
############################# Requests ############################
|
||||
|
||||
# Comma-separated list of accepted status codes for valid links.
|
||||
accept = [200, 429]
|
||||
|
||||
############################# Exclusions ##########################
|
||||
|
||||
# Exclude URLs and mail addresses from checking (supports regex).
|
||||
exclude = [
|
||||
# Network error: Forbidden
|
||||
"https://codepen.io",
|
||||
|
||||
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
|
||||
"https://twitter.com/mermaidjs_",
|
||||
|
||||
# Don't check files that are generated during the build via `pnpm docs:code`
|
||||
'packages/mermaid/src/docs/config/setup/*',
|
||||
|
||||
# Ignore Discord invite
|
||||
"https://discord.gg",
|
||||
|
||||
# BundlePhobia has frequent downtime
|
||||
"https://bundlephobia.com",
|
||||
|
||||
# Chrome webstore migration issue. Temporary
|
||||
"https://chromewebstore.google.com",
|
||||
|
||||
# Drupal 403
|
||||
"https://(www.)?drupal.org",
|
||||
|
||||
# Phbpp 403
|
||||
"https://(www.)?phpbb.com",
|
||||
|
||||
# Swimm returns 404, even though the link is valid
|
||||
"https://docs.swimm.io",
|
||||
|
||||
# Certificate Error
|
||||
"https://noteshub.app",
|
||||
|
||||
# Timeout
|
||||
"https://huehive.co",
|
||||
"https://foswiki.org",
|
||||
"https://www.gnu.org",
|
||||
"https://redmine.org",
|
||||
"https://mermaid-preview.com"
|
||||
]
|
||||
|
||||
# Exclude all private IPs from checking.
|
||||
# Equivalent to setting `exclude_private`, `exclude_link_local`, and
|
||||
# `exclude_loopback` to true.
|
||||
exclude_all_private = true
|
||||
25
.github/pr-labeler.yml
vendored
25
.github/pr-labeler.yml
vendored
@@ -1,22 +1,3 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/release-drafter/release-drafter/master/schema.json
|
||||
autolabeler:
|
||||
- label: 'Type: Bug / Error'
|
||||
branch:
|
||||
- '/bug\/.+/'
|
||||
- '/fix\/.+/'
|
||||
- label: 'Type: Enhancement'
|
||||
branch:
|
||||
- '/feature\/.+/'
|
||||
- '/feat\/.+/'
|
||||
- label: 'Type: Other'
|
||||
branch:
|
||||
- '/other\/.+/'
|
||||
- '/chore\/.+/'
|
||||
- '/test\/.+/'
|
||||
- '/refactor\/.+/'
|
||||
- label: 'Area: Documentation'
|
||||
branch:
|
||||
- '/docs\/.+/'
|
||||
|
||||
template: |
|
||||
This field is unused, as we only use this config file for labeling PRs.
|
||||
'Type: Bug / Error': 'bug/*'
|
||||
'Type: Enhancement': 'feature/*'
|
||||
'Type: Other': 'other/*'
|
||||
|
||||
8
.github/pull_request_template.md
vendored
8
.github/pull_request_template.md
vendored
@@ -12,7 +12,7 @@ Describe the way your implementation works or what design decisions you made if
|
||||
|
||||
Make sure you
|
||||
|
||||
- [ ] :book: have read the [contribution guidelines](https://mermaid.js.org/community/contributing.html)
|
||||
- [ ] :computer: have added necessary unit/e2e tests.
|
||||
- [ ] :notebook: have added documentation. Make sure [`MERMAID_RELEASE_VERSION`](https://mermaid.js.org/community/contributing.html#update-documentation) is used for all new features.
|
||||
- [ ] :butterfly: If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. Changesets that add features should be `minor` and those that fix bugs should be `patch`. Please prefix changeset messages with `feat:`, `fix:`, or `chore:`.
|
||||
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
|
||||
- [ ] :computer: have added unit/e2e tests (if appropriate)
|
||||
- [ ] :notebook: have added documentation (if appropriate)
|
||||
- [ ] :bookmark: targeted `develop` branch
|
||||
|
||||
25
.github/release-drafter.yml
vendored
Normal file
25
.github/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name-template: '$NEXT_PATCH_VERSION'
|
||||
tag-template: '$NEXT_PATCH_VERSION'
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
- 'Type: Enhancement'
|
||||
- title: '🐛 Bug Fixes'
|
||||
labels:
|
||||
- 'Type: Bug / Error'
|
||||
- title: '🧰 Maintenance'
|
||||
label: 'Type: Other'
|
||||
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
|
||||
sort-by: title
|
||||
sort-direction: ascending
|
||||
branches:
|
||||
- develop
|
||||
exclude-labels:
|
||||
- 'Skip changelog'
|
||||
no-changes-template: 'This release contains minor changes and bugfixes.'
|
||||
template: |
|
||||
# Release Notes
|
||||
|
||||
$CHANGES
|
||||
|
||||
🎉 **Thanks to all contributors helping with this release!** 🎉
|
||||
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
@@ -15,5 +15,5 @@ markComment: >
|
||||
If you are still interested in this issue and it is still relevant you can comment to revive it.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed due to a lack of activity.
|
||||
This issue has been been automatically closed due to a lack of activity.
|
||||
This is done to maintain a clean list of issues that the community is interested in developing.
|
||||
|
||||
45
.github/workflows/autofix.yml
vendored
45
.github/workflows/autofix.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: autofix.ci # needed to securely identify the workflow
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'renovate/**'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
autofix:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: .cache/Cypress
|
||||
|
||||
- name: Fix Linting
|
||||
shell: bash
|
||||
run: pnpm -w run lint:fix
|
||||
|
||||
- name: Sync `./src/config.type.ts` with `./src/schemas/config.schema.yaml`
|
||||
shell: bash
|
||||
run: pnpm run --filter mermaid types:build-config
|
||||
|
||||
- name: Build Docs
|
||||
working-directory: ./packages/mermaid
|
||||
run: pnpm run docs:build
|
||||
|
||||
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 # main
|
||||
22
.github/workflows/build-docs.yml
vendored
22
.github/workflows/build-docs.yml
vendored
@@ -1,39 +1,29 @@
|
||||
name: Build Vitepress docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release/*
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-docs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
node-version: 18
|
||||
|
||||
- name: Install Packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify release version
|
||||
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release')) }}
|
||||
run: pnpm --filter mermaid run docs:verify-version
|
||||
|
||||
- name: Run Build
|
||||
run: pnpm --filter mermaid run docs:build:vitepress
|
||||
|
||||
51
.github/workflows/build.yml
vendored
Normal file
51
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: .cache/Cypress
|
||||
|
||||
- name: Run Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Upload Mermaid Build as Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mermaid-build
|
||||
path: packages/mermaid/dist
|
||||
|
||||
- name: Upload Mermaid Mindmap Build as Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mermaid-mindmap-build
|
||||
path: packages/mermaid-mindmap/dist
|
||||
4
.github/workflows/check-readme-in-sync.yml
vendored
4
.github/workflows/check-readme-in-sync.yml
vendored
@@ -14,11 +14,11 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-readme:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check for difference in README.md and docs/README.md
|
||||
run: |
|
||||
|
||||
25
.github/workflows/checks.yml
vendored
Normal file
25
.github/workflows/checks.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
name: Static analysis
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: check tests
|
||||
if: github.repository_owner == 'mermaid-js'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: testomatio/check-tests@stable
|
||||
with:
|
||||
framework: cypress
|
||||
tests: './cypress/e2e/**/**.spec.js'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
has-tests-label: true
|
||||
15
.github/workflows/codeql.yml
vendored
15
.github/workflows/codeql.yml
vendored
@@ -11,9 +11,6 @@ on:
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
permissions: # added using https://github.com/step-security/secure-repo
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
@@ -26,17 +23,17 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['javascript', 'actions']
|
||||
# CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
language: ['javascript']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
languages: ${{ matrix.language }}
|
||||
@@ -48,7 +45,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -62,4 +59,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
6
.github/workflows/dependency-review.yml
vendored
6
.github/workflows/dependency-review.yml
vendored
@@ -1,6 +1,6 @@
|
||||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
|
||||
@@ -15,6 +15,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||
uses: actions/dependency-review-action@v3
|
||||
|
||||
25
.github/workflows/e2e-applitools.yml
vendored
25
.github/workflows/e2e-applitools.yml
vendored
@@ -11,8 +11,6 @@ on:
|
||||
default: master
|
||||
description: 'Parent branch to use for PRs'
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -21,39 +19,40 @@ env:
|
||||
USE_APPLI: ${{ secrets.APPLITOOLS_API_KEY && 'true' || '' }}
|
||||
|
||||
jobs:
|
||||
e2e-applitools:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- if: ${{ ! env.USE_APPLI }}
|
||||
name: Warn if not using Applitools
|
||||
run: |
|
||||
echo "::error,title=Not using Applitools::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
|
||||
echo "::error,title=Not using Applitols::APPLITOOLS_API_KEY is empty, disabling Applitools for this run."
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- if: ${{ env.USE_APPLI }}
|
||||
name: Notify applitools of new batch
|
||||
# Copied from docs https://applitools.com/docs/topics/integrations/github-integration-ci-setup.html
|
||||
run: curl -L -d '' -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
|
||||
env:
|
||||
# e.g. mermaid-js/mermaid/my-branch
|
||||
APPLITOOLS_BRANCH: ${{ github.repository }}/${{ github.ref_name }}
|
||||
APPLITOOLS_PARENT_BRANCH: ${{ github.event.inputs.parent_branch }}
|
||||
APPLITOOLS_API_KEY: ${{ secrets.APPLITOOLS_API_KEY }}
|
||||
APPLITOOLS_SERVER_URL: 'https://eyesapi.applitools.com'
|
||||
uses: wei/curl@012398a392d02480afa2720780031f8621d5f94c
|
||||
with:
|
||||
args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
uses: cypress-io/github-action@v4
|
||||
id: cypress
|
||||
with:
|
||||
start: pnpm run dev
|
||||
|
||||
70
.github/workflows/e2e-timings.yml
vendored
70
.github/workflows/e2e-timings.yml
vendored
@@ -1,70 +0,0 @@
|
||||
name: E2E - Generate Timings
|
||||
|
||||
on:
|
||||
# run this workflow every night at 3am
|
||||
schedule:
|
||||
- cron: '28 3 * * *'
|
||||
# or when the user triggers it from GitHub Actions page
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
timings:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
|
||||
options: --user 1001
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
- name: Install dependencies
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
with:
|
||||
runTests: false
|
||||
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
id: cypress
|
||||
with:
|
||||
install: false
|
||||
start: pnpm run dev:coverage
|
||||
wait-on: 'http://localhost:9000'
|
||||
browser: chrome
|
||||
publish-summary: false
|
||||
env:
|
||||
VITEST_COVERAGE: true
|
||||
CYPRESS_COMMIT: ${{ github.sha }}
|
||||
SPLIT: 1
|
||||
SPLIT_INDEX: 0
|
||||
SPLIT_FILE: 'cypress/timings.json'
|
||||
|
||||
- name: Compare timings
|
||||
id: compare
|
||||
run: |
|
||||
OUTPUT=$(pnpm tsx scripts/compare-timings.ts)
|
||||
echo "$OUTPUT" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
echo "output<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$OUTPUT" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit and create pull request
|
||||
uses: peter-evans/create-pull-request@0979079bc20c05bbbb590a56c21c4e2b1d1f1bbe
|
||||
with:
|
||||
add-paths: |
|
||||
cypress/timings.json
|
||||
commit-message: 'chore: update E2E timings'
|
||||
branch: update-timings
|
||||
title: Update E2E Timings
|
||||
body: ${{ steps.compare.outputs.output }}
|
||||
delete-branch: true
|
||||
sign-commits: true
|
||||
142
.github/workflows/e2e.yml
vendored
142
.github/workflows/e2e.yml
vendored
@@ -1,151 +1,59 @@
|
||||
name: E2E
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- master
|
||||
- release/**
|
||||
pull_request:
|
||||
merge_group:
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# For PRs and MergeQueues, the target commit is used, and for push events to non-develop branches, github.event.previous is used if available. Otherwise, 'develop' is used.
|
||||
targetHash: >-
|
||||
${{
|
||||
github.event.pull_request.base.sha ||
|
||||
github.event.merge_group.base_sha ||
|
||||
(
|
||||
(
|
||||
(github.event_name == 'push' && github.ref == 'refs/heads/develop') ||
|
||||
github.event.before == '0000000000000000000000000000000000000000'
|
||||
) && 'develop'
|
||||
) ||
|
||||
github.event.before
|
||||
}}
|
||||
RUN_VISUAL_TEST: >-
|
||||
${{ github.repository == 'mermaid-js/mermaid' && (github.event_name != 'pull_request' || !startsWith(github.head_ref, 'renovate/')) }}
|
||||
jobs:
|
||||
cache:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
|
||||
options: --user 1001
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
- name: Cache snapshots
|
||||
id: cache-snapshot
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ./cypress/snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||
|
||||
# If a snapshot for a given Hash is not found, we checkout that commit, run the tests and cache the snapshots.
|
||||
- name: Switch to base branch
|
||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ env.targetHash }}
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
with:
|
||||
# just perform install
|
||||
runTests: false
|
||||
|
||||
- name: Calculate bundle size
|
||||
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true'}}
|
||||
run: |
|
||||
pnpm run build:viz
|
||||
mkdir -p cypress/snapshots/stats/base
|
||||
mv stats cypress/snapshots/stats/base
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1
|
||||
options: --user 1001
|
||||
needs: cache
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
containers: [1, 2, 3, 4, 5]
|
||||
node-version: [18.x]
|
||||
containers: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
|
||||
# These cached snapshots are downloaded, providing the reference snapshots.
|
||||
- name: Cache snapshots
|
||||
id: cache-snapshot
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: ./cypress/snapshots
|
||||
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
|
||||
|
||||
- name: Install dependencies
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
with:
|
||||
runTests: false
|
||||
|
||||
- name: Output size diff
|
||||
if: ${{ matrix.containers == 1 }}
|
||||
run: |
|
||||
pnpm run build:viz
|
||||
mv stats cypress/snapshots/stats/head
|
||||
echo '## Bundle size difference' >> "$GITHUB_STEP_SUMMARY"
|
||||
echo '' >> "$GITHUB_STEP_SUMMARY"
|
||||
npx tsx scripts/size.ts >> "$GITHUB_STEP_SUMMARY"
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
# Install NPM dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
|
||||
uses: cypress-io/github-action@v4
|
||||
id: cypress
|
||||
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
|
||||
# Otherwise (e.g. if running from fork), we run on a single container only
|
||||
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
|
||||
with:
|
||||
install: false
|
||||
start: pnpm run dev:coverage
|
||||
wait-on: 'http://localhost:9000'
|
||||
browser: chrome
|
||||
# Disable recording if we don't have an API key
|
||||
# e.g. if this action was run from a fork
|
||||
record: ${{ env.RUN_VISUAL_TEST == 'true' && secrets.CYPRESS_RECORD_KEY != '' }}
|
||||
record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
|
||||
parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
|
||||
env:
|
||||
ARGOS_PARALLEL: ${{ env.RUN_VISUAL_TEST == 'true' }}
|
||||
ARGOS_PARALLEL_TOTAL: ${{ env.RUN_VISUAL_TEST == 'true' && strategy.job-total || 1 }}
|
||||
ARGOS_PARALLEL_INDEX: ${{ env.RUN_VISUAL_TEST == 'true' && matrix.containers || 1 }}
|
||||
CYPRESS_COMMIT: ${{ github.sha }}
|
||||
CYPRESS_RECORD_KEY: ${{ env.RUN_VISUAL_TEST == 'true' && secrets.CYPRESS_RECORD_KEY || ''}}
|
||||
SPLIT: ${{ strategy.job-total }}
|
||||
SPLIT_INDEX: ${{ strategy.job-index }}
|
||||
SPLIT_FILE: 'cypress/timings.json'
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
VITEST_COVERAGE: true
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
|
||||
# Run step only pushes to develop and pull_requests
|
||||
if: ${{ steps.cypress.conclusion == 'success' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/develop')}}
|
||||
uses: codecov/codecov-action@v3
|
||||
if: steps.cypress.conclusion == 'success'
|
||||
with:
|
||||
files: coverage/cypress/lcov.info
|
||||
flags: e2e
|
||||
name: mermaid-codecov
|
||||
fail_ci_if_error: false
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() && steps.cypress.conclusion == 'failure' }}
|
||||
with:
|
||||
name: error-snapshots
|
||||
path: cypress/snapshots/**/__diff_output__/*
|
||||
|
||||
8
.github/workflows/issue-triage.yml
vendored
8
.github/workflows/issue-triage.yml
vendored
@@ -4,17 +4,11 @@ on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions: # added using https://github.com/step-security/secure-repo
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
issues: write # for andymckay/labeler to label issues
|
||||
pull-requests: write # for andymckay/labeler to label PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4
|
||||
- uses: andymckay/labeler@1.0.4
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
add-labels: 'Status: Triage'
|
||||
|
||||
16
.github/workflows/link-checker.yml
vendored
16
.github/workflows/link-checker.yml
vendored
@@ -19,30 +19,30 @@ on:
|
||||
# * is a special character in YAML so you have to quote this string
|
||||
- cron: '30 8 * * *'
|
||||
|
||||
permissions: # added using https://github.com/step-security/secure-repo
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
link-checker:
|
||||
linkChecker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# lychee only uses the GITHUB_TOKEN to avoid rate-limiting
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Restore lychee cache
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
- name: Link Checker
|
||||
uses: lycheeverse/lychee-action@f613c4a64e50d792e0b31ec34bbcbba12263c6a6 # v2.3.0
|
||||
uses: lycheeverse/lychee-action@v1.8.0
|
||||
with:
|
||||
args: >-
|
||||
--config .github/lychee.toml
|
||||
--verbose
|
||||
--no-progress
|
||||
--cache
|
||||
--max-cache-age 1d
|
||||
packages/mermaid/src/docs/**/*.md
|
||||
README.md
|
||||
README.zh-CN.md
|
||||
|
||||
72
.github/workflows/lint.yml
vendored
72
.github/workflows/lint.yml
vendored
@@ -1,38 +1,34 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
merge_group:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
docker-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: hadolint/hadolint-action@54c9adbab1582c2ef04b2016b760714a4bfde3cf # v3.1.0
|
||||
with:
|
||||
verbose: true
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
@@ -56,42 +52,20 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify `./src/config.type.ts` is in sync with `./src/schemas/config.schema.yaml`
|
||||
shell: bash
|
||||
run: |
|
||||
if ! pnpm run --filter mermaid types:verify-config; then
|
||||
ERROR_MESSAGE='Running `pnpm run --filter mermaid types:verify-config` failed.'
|
||||
ERROR_MESSAGE+=' This should be fixed by running'
|
||||
ERROR_MESSAGE+=' `pnpm run --filter mermaid types:build-config`'
|
||||
ERROR_MESSAGE+=' on your local machine.'
|
||||
echo "::error title=Lint failure::${ERROR_MESSAGE}"
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify no circular dependencies
|
||||
working-directory: ./packages/mermaid
|
||||
shell: bash
|
||||
run: |
|
||||
if ! pnpm run --filter mermaid checkCircle; then
|
||||
ERROR_MESSAGE='Circular dependency detected.'
|
||||
ERROR_MESSAGE+=' This should be fixed by removing the circular dependency.'
|
||||
ERROR_MESSAGE+=' Run `pnpm run --filter mermaid checkCircle` on your local machine'
|
||||
ERROR_MESSAGE+=' to see the circular dependency.'
|
||||
echo "::error title=Lint failure::${ERROR_MESSAGE}"
|
||||
# make sure to return an error exitcode so that GitHub actions shows a red-cross
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Verify Docs
|
||||
id: verifyDocs
|
||||
working-directory: ./packages/mermaid
|
||||
continue-on-error: ${{ github.event_name == 'push' }}
|
||||
run: pnpm run docs:verify
|
||||
|
||||
- uses: testomatio/check-tests@0ea638fcec1820cf2e7b9854fdbdd04128a55bd4 # stable
|
||||
- name: Rebuild Docs
|
||||
if: ${{ steps.verifyDocs.outcome == 'failure' && github.event_name == 'push' }}
|
||||
working-directory: ./packages/mermaid
|
||||
run: pnpm run docs:build
|
||||
|
||||
- name: Commit changes
|
||||
uses: EndBug/add-and-commit@v9
|
||||
if: ${{ steps.verifyDocs.outcome == 'failure' && github.event_name == 'push' }}
|
||||
with:
|
||||
framework: cypress
|
||||
tests: './cypress/e2e/**/**.spec.js'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
has-tests-label: true
|
||||
message: 'Update docs'
|
||||
add: 'docs/*'
|
||||
|
||||
19
.github/workflows/pr-labeler-config-validator.yml
vendored
Normal file
19
.github/workflows/pr-labeler-config-validator.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Validate PR Labeler Configuration
|
||||
on:
|
||||
push: {}
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
|
||||
jobs:
|
||||
pr-labeler:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Validate Configuration
|
||||
uses: Yash-Singh1/pr-labeler-config-validator@releases/v0.0.3
|
||||
with:
|
||||
configuration-path: .github/pr-labeler.yml
|
||||
48
.github/workflows/pr-labeler.yml
vendored
48
.github/workflows/pr-labeler.yml
vendored
@@ -1,57 +1,13 @@
|
||||
name: Apply labels to PR
|
||||
on:
|
||||
pull_request_target:
|
||||
# required for pr-labeler to support PRs from forks
|
||||
# ===================== ⛔ ☢️ 🚫 ⚠️ Warning ⚠️ 🚫 ☢️ ⛔ =======================
|
||||
# Be very careful what you put in this GitHub Action workflow file to avoid
|
||||
# malicious PRs from getting access to the Mermaid-js repo.
|
||||
#
|
||||
# Please read the following first before reviewing/merging:
|
||||
# - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
# - https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
pr-labeler:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read # read permission is required to read config file
|
||||
pull-requests: write # write permission is required to label PRs
|
||||
steps:
|
||||
- name: Label PR
|
||||
uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0
|
||||
with:
|
||||
config-name: pr-labeler.yml
|
||||
disable-autolabeler: false
|
||||
disable-releaser: true
|
||||
uses: TimonVS/pr-labeler-action@v4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add "Sponsored by MermaidChart" label
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { data: commits } = await github.rest.pulls.listCommits({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
const isSponsored = commits.every(
|
||||
(c) => c.commit.author.email?.endsWith('@mermaidchart.com')
|
||||
);
|
||||
|
||||
if (isSponsored) {
|
||||
console.log('PR is sponsored. Adding label.');
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['Sponsored by MermaidChart'],
|
||||
});
|
||||
}
|
||||
|
||||
20
.github/workflows/publish-docs.yml
vendored
20
.github/workflows/publish-docs.yml
vendored
@@ -19,41 +19,41 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build-docs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
node-version: 18
|
||||
|
||||
- name: Install Packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
|
||||
uses: actions/configure-pages@v3
|
||||
|
||||
- name: Run Build
|
||||
run: pnpm --filter mermaid run docs:build:vitepress
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
path: packages/mermaid/src/vitepress/.vitepress/dist
|
||||
|
||||
# Deployment job
|
||||
deploy-docs:
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-docs
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
|
||||
uses: actions/deploy-pages@v2
|
||||
|
||||
15
.github/workflows/release-draft.yml
vendored
Normal file
15
.github/workflows/release-draft.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: Draft Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
draft-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Draft Release
|
||||
uses: toolmantim/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
12
.github/workflows/release-preview-publish.yml
vendored
12
.github/workflows/release-preview-publish.yml
vendored
@@ -6,20 +6,20 @@ on:
|
||||
- 'release/**'
|
||||
|
||||
jobs:
|
||||
publish-preview:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
CYPRESS_CACHE_FOLDER: .cache/Cypress
|
||||
|
||||
- name: Install Json
|
||||
run: npm i json@11.0.0 --global
|
||||
run: npm i json --global
|
||||
|
||||
- name: Publish
|
||||
working-directory: ./packages/mermaid
|
||||
|
||||
43
.github/workflows/release-preview.yml
vendored
43
.github/workflows/release-preview.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Preview release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [develop]
|
||||
types: [opened, synchronize, labeled, ready_for_review]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
preview:
|
||||
if: ${{ github.repository_owner == 'mermaid-js' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
name: Publish preview release
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
|
||||
- name: Install Packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Publish packages
|
||||
run: pnpx pkg-pr-new publish --pnpm './packages/*'
|
||||
47
.github/workflows/release-publish.yml
vendored
Normal file
47
.github/workflows/release-publish.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Publish release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: fregante/setup-git-user@v2
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version: 18.x
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
pnpm install --frozen-lockfile
|
||||
npm i json --global
|
||||
env:
|
||||
CYPRESS_CACHE_FOLDER: .cache/Cypress
|
||||
|
||||
- name: Prepare release
|
||||
run: |
|
||||
VERSION=${GITHUB_REF:10}
|
||||
echo "Preparing release $VERSION"
|
||||
git checkout -t origin/release/$VERSION
|
||||
npm version --no-git-tag-version --allow-same-version $VERSION
|
||||
git add package.json
|
||||
git commit -nm "Bump version $VERSION"
|
||||
git checkout -t origin/master
|
||||
git merge -m "Release $VERSION" --no-ff release/$VERSION
|
||||
git push --no-verify
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
npm set //registry.npmjs.org/:_authToken $NPM_TOKEN
|
||||
npm publish
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
45
.github/workflows/release.yml
vendored
45
.github/workflows/release.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions: # added using https://github.com/step-security/secure-repo
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository == 'mermaid-js/mermaid'
|
||||
permissions:
|
||||
contents: write # to create release (changesets/action)
|
||||
id-token: write # OpenID Connect token needed for provenance
|
||||
pull-requests: write # to create pull request (changesets/action)
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
|
||||
- name: Install Packages
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
|
||||
with:
|
||||
version: pnpm changeset:version
|
||||
publish: pnpm changeset:publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
37
.github/workflows/scorecard.yml
vendored
37
.github/workflows/scorecard.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
branch_protection_rule:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
schedule:
|
||||
- cron: 29 15 * * 0
|
||||
permissions: read-all
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
permissions:
|
||||
id-token: write
|
||||
security-events: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run analysis
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
publish_results: true
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
- name: Upload to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
34
.github/workflows/test.yml
vendored
34
.github/workflows/test.yml
vendored
@@ -1,24 +1,27 @@
|
||||
name: Unit Tests
|
||||
|
||||
on: [push, pull_request, merge_group]
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
unit-test:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x]
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- uses: pnpm/action-setup@v2
|
||||
# uses version from "packageManager" field in package.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: pnpm
|
||||
node-version-file: '.node-version'
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
@@ -38,18 +41,17 @@ jobs:
|
||||
run: |
|
||||
pnpm exec vitest run ./packages/mermaid/src/diagrams/gantt/ganttDb.spec.ts --coverage
|
||||
|
||||
- name: Verify out-of-tree build with TypeScript
|
||||
run: |
|
||||
pnpm test:check:tsc
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
|
||||
# Run step only pushes to develop and pull_requests
|
||||
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage/vitest/lcov.info
|
||||
flags: unit
|
||||
name: mermaid-codecov
|
||||
fail_ci_if_error: false
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
token: 6845cc80-77ee-4e17-85a1-026cd95e0766
|
||||
# Coveralls is throwing 500. Disabled for now.
|
||||
# - name: Upload Coverage to Coveralls
|
||||
# uses: coverallsapp/github-action@v2
|
||||
# with:
|
||||
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# flag-name: unit
|
||||
|
||||
2
.github/workflows/unlock-reopened-issues.yml
vendored
2
.github/workflows/unlock-reopened-issues.yml
vendored
@@ -8,6 +8,6 @@ jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: Dunning-Kruger/unlock-issues@b06b7f7e5c3f2eaa1c6d5d89f40930e4d6d9699e # v1
|
||||
- uses: Dunning-Kruger/unlock-issues@v1
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
15
.github/workflows/update-browserlist.yml
vendored
15
.github/workflows/update-browserlist.yml
vendored
@@ -5,21 +5,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-browser-list:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
- run: npx update-browserslist-db@latest
|
||||
- uses: actions/checkout@v3
|
||||
- run: npx browserslist@latest --update-db
|
||||
- name: Commit changes
|
||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
author_name: ${{ github.actor }}
|
||||
author_email: ${{ github.actor }}@users.noreply.github.com
|
||||
message: 'chore: update browsers list'
|
||||
push: false
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
|
||||
with:
|
||||
branch: update-browserslist
|
||||
title: Update Browserslist
|
||||
|
||||
90
.github/workflows/validate-lockfile.yml
vendored
90
.github/workflows/validate-lockfile.yml
vendored
@@ -1,90 +0,0 @@
|
||||
name: Validate pnpm-lock.yaml
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'pnpm-lock.yaml'
|
||||
- '**/package.json'
|
||||
- '.github/workflows/validate-lockfile.yml'
|
||||
|
||||
jobs:
|
||||
validate-lockfile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- name: Validate pnpm-lock.yaml entries
|
||||
id: validate # give this step an ID so we can reference its outputs
|
||||
run: |
|
||||
issues=()
|
||||
|
||||
# 1) No tarball references
|
||||
if grep -qF 'tarball:' pnpm-lock.yaml; then
|
||||
issues+=("• Tarball references found (forbidden)")
|
||||
fi
|
||||
|
||||
# 2) No unwanted vitepress paths
|
||||
if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then
|
||||
issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run \`rm -rf packages/mermaid/src/vitepress && pnpm install\` to regenerate.")
|
||||
fi
|
||||
|
||||
# 3) Lockfile only changes when package.json changes
|
||||
git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} > changed.txt
|
||||
if grep -q '^pnpm-lock.yaml$' changed.txt && ! grep -q 'package.json' changed.txt; then
|
||||
issues+=("• pnpm-lock.yaml changed without any package.json modification")
|
||||
fi
|
||||
|
||||
# If any issues, output them and fail
|
||||
if [ ${#issues[@]} -gt 0 ]; then
|
||||
# Use the new GITHUB_OUTPUT approach to set a multiline output
|
||||
{
|
||||
echo "errors<<EOF"
|
||||
printf '%s\n' "${issues[@]}"
|
||||
echo "EOF"
|
||||
} >> $GITHUB_OUTPUT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Find existing lockfile validation comment
|
||||
if: always()
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: find-comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: 'Lockfile Validation Failed'
|
||||
|
||||
- name: Comment on PR if validation failed
|
||||
if: failure()
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body: |
|
||||
❌ **Lockfile Validation Failed**
|
||||
|
||||
The following issue(s) were detected:
|
||||
${{ steps.validate.outputs.errors }}
|
||||
|
||||
Please address these and push an update.
|
||||
|
||||
_Posted automatically by GitHub Actions_
|
||||
|
||||
- name: Delete comment if validation passed
|
||||
if: success() && steps.find-comment.outputs.comment-id != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.deleteComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: ${{ steps.find-comment.outputs.comment-id }},
|
||||
});
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -4,7 +4,6 @@ node_modules/
|
||||
coverage/
|
||||
.idea/
|
||||
.pnpm-store/
|
||||
.instructions/
|
||||
|
||||
dist
|
||||
v8-compile-cache-0
|
||||
@@ -36,7 +35,7 @@ cypress/snapshots/
|
||||
.tsbuildinfo
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
#knsv*.html
|
||||
knsv*.html
|
||||
local*.html
|
||||
stats/
|
||||
|
||||
@@ -44,12 +43,3 @@ stats/
|
||||
**/contributor-names.json
|
||||
.pnpm-store
|
||||
.nyc_output
|
||||
|
||||
demos/dev/**
|
||||
!/demos/dev/example.html
|
||||
!/demos/dev/reload.js
|
||||
tsx-0/**
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
# autogenereated by langium-cli
|
||||
generated/
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
ignored:
|
||||
- DL3002 # TODO: Last USER should not be root
|
||||
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
# . "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# npx --no-install commitlint --edit $1
|
||||
@@ -1,2 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm run pre-commit
|
||||
|
||||
@@ -6,6 +6,6 @@ export default {
|
||||
// https://prettier.io/docs/en/cli.html#--cache
|
||||
'prettier --write',
|
||||
],
|
||||
'.cspell/*.txt': ['tsx scripts/fixCSpell.ts'],
|
||||
'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
|
||||
'**/*.jison': ['pnpm -w run lint:jison'],
|
||||
};
|
||||
|
||||
17
.lycheeignore
Normal file
17
.lycheeignore
Normal file
@@ -0,0 +1,17 @@
|
||||
# These links are ignored by our link checker https://github.com/lycheeverse/lychee
|
||||
# The file allows you to list multiple regular expressions for exclusion (one pattern per line).
|
||||
|
||||
# Network error: Forbidden
|
||||
https://codepen.io
|
||||
|
||||
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
|
||||
https://twitter.com/mermaidjs_
|
||||
|
||||
# Don't check files that are generated during the build via `pnpm docs:code`
|
||||
packages/mermaid/src/docs/config/setup/*
|
||||
|
||||
# Ignore localhost
|
||||
http://localhost:3333/
|
||||
|
||||
# Ignore slack invite
|
||||
https://join.slack.com/
|
||||
@@ -1 +0,0 @@
|
||||
22.14.0
|
||||
2
.npmrc
2
.npmrc
@@ -1,4 +1,2 @@
|
||||
registry=https://registry.npmjs.org
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
package-import-method=clone-or-copy
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
dist
|
||||
cypress/platform/xss3.html
|
||||
.cache
|
||||
.pnpm-store
|
||||
coverage
|
||||
# Autogenerated by PNPM
|
||||
pnpm-lock.yaml
|
||||
stats
|
||||
**/.vitepress/components.d.ts
|
||||
**/.vitepress/cache
|
||||
packages/mermaid/src/docs/.vitepress/components.d.ts
|
||||
.nyc_output
|
||||
# Autogenerated by `pnpm run --filter mermaid types:build-config`
|
||||
packages/mermaid/src/config.type.ts
|
||||
# autogenereated by langium-cli
|
||||
generated/
|
||||
# Ignore the files creates in /demos/dev except for example.html
|
||||
demos/dev/**
|
||||
!/demos/dev/example.html
|
||||
# TODO: Lots of errors to fix
|
||||
cypress/platform/state-refactor.html
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
"printWidth": 100,
|
||||
"singleQuote": true,
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
"tabWidth": 2
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import type { InlineConfig } from 'vite';
|
||||
import { build, type PluginOption } from 'vite';
|
||||
import { build, InlineConfig, type PluginOption } from 'vite';
|
||||
import { resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import jisonPlugin from './jisonPlugin.js';
|
||||
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
|
||||
import { readFileSync } from 'fs';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template-types.js';
|
||||
import istanbul from 'vite-plugin-istanbul';
|
||||
import { packageOptions } from '../.build/common.js';
|
||||
import { generateLangium } from '../.build/generateLangium.js';
|
||||
|
||||
const visualize = process.argv.includes('--visualize');
|
||||
const watch = process.argv.includes('--watch');
|
||||
@@ -38,6 +35,24 @@ const visualizerOptions = (packageName: string, core = false): PluginOption[] =>
|
||||
);
|
||||
};
|
||||
|
||||
const packageOptions = {
|
||||
mermaid: {
|
||||
name: 'mermaid',
|
||||
packageName: 'mermaid',
|
||||
file: 'mermaid.ts',
|
||||
},
|
||||
'mermaid-example-diagram': {
|
||||
name: 'mermaid-example-diagram',
|
||||
packageName: 'mermaid-example-diagram',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
'mermaid-zenuml': {
|
||||
name: 'mermaid-zenuml',
|
||||
packageName: 'mermaid-zenuml',
|
||||
file: 'detector.ts',
|
||||
},
|
||||
};
|
||||
|
||||
interface BuildOptions {
|
||||
minify: boolean | 'esbuild';
|
||||
core?: boolean;
|
||||
@@ -47,18 +62,43 @@ interface BuildOptions {
|
||||
|
||||
export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions): InlineConfig => {
|
||||
const external: (string | RegExp)[] = ['require', 'fs', 'path'];
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(entryName, packageOptions[entryName]);
|
||||
const { name, file, packageName } = packageOptions[entryName];
|
||||
const output: OutputOptions = [
|
||||
let output: OutputOptions = [
|
||||
{
|
||||
name,
|
||||
format: 'esm',
|
||||
sourcemap,
|
||||
entryFileNames: `${name}.esm${minify ? '.min' : ''}.mjs`,
|
||||
},
|
||||
{
|
||||
name,
|
||||
format: 'umd',
|
||||
sourcemap,
|
||||
entryFileNames: `${name}${minify ? '.min' : ''}.js`,
|
||||
},
|
||||
];
|
||||
|
||||
if (core) {
|
||||
const { dependencies } = JSON.parse(
|
||||
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
|
||||
);
|
||||
// Core build is used to generate file without bundled dependencies.
|
||||
// This is used by downstream projects to bundle dependencies themselves.
|
||||
// Ignore dependencies and any dependencies of dependencies
|
||||
// Adapted from the RegEx used by `rollup-plugin-node`
|
||||
external.push(new RegExp('^(?:' + Object.keys(dependencies).join('|') + ')(?:/.+)?$'));
|
||||
// This needs to be an array. Otherwise vite will build esm & umd with same name and overwrite esm with umd.
|
||||
output = [
|
||||
{
|
||||
name,
|
||||
format: 'esm',
|
||||
sourcemap,
|
||||
entryFileNames: `${name}.core.mjs`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const config: InlineConfig = {
|
||||
configFile: false,
|
||||
build: {
|
||||
@@ -76,20 +116,15 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
output,
|
||||
},
|
||||
},
|
||||
define: {
|
||||
'import.meta.vitest': 'undefined',
|
||||
'injected.includeLargeFeatures': 'true',
|
||||
'injected.version': `'0.0.0'`,
|
||||
},
|
||||
resolve: {
|
||||
extensions: [],
|
||||
},
|
||||
plugins: [
|
||||
jisonPlugin(),
|
||||
jsonSchemaPlugin(), // handles `.schema.yaml` files
|
||||
// @ts-expect-error According to the type definitions, rollup plugins are incompatible with vite
|
||||
typescript({ compilerOptions: { declaration: false } }),
|
||||
istanbul({
|
||||
exclude: ['node_modules', 'test/', '__mocks__', 'generated'],
|
||||
exclude: ['node_modules', 'test/', '__mocks__'],
|
||||
extension: ['.js', '.ts'],
|
||||
requireEnv: true,
|
||||
forceBuildInstrument: coverage,
|
||||
@@ -109,28 +144,24 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
|
||||
const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
||||
await build(getBuildConfig({ minify: false, entryName }));
|
||||
await build(getBuildConfig({ minify: 'esbuild', entryName }));
|
||||
await build(getBuildConfig({ minify: false, core: true, entryName }));
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||
for (const pkg of packageNames.filter(
|
||||
(pkg) => !mermaidOnly || pkg === 'mermaid' || pkg === 'parser'
|
||||
)) {
|
||||
for (const pkg of packageNames.filter((pkg) => !mermaidOnly || pkg === 'mermaid')) {
|
||||
await buildPackage(pkg);
|
||||
}
|
||||
};
|
||||
|
||||
await generateLangium();
|
||||
|
||||
if (watch) {
|
||||
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
||||
void build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
||||
build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
|
||||
if (!mermaidOnly) {
|
||||
void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
void build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
|
||||
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-zenuml' }));
|
||||
}
|
||||
} 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: false, entryName: 'mermaid' }));
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { transformJison } from '../.build/jisonTransformer.js';
|
||||
|
||||
import { transformJison } from './jisonTransformer.js';
|
||||
const fileRegex = /\.(jison)$/;
|
||||
|
||||
export default function jison() {
|
||||
return {
|
||||
name: 'jison',
|
||||
|
||||
transform(src: string, id: string) {
|
||||
if (fileRegex.test(id)) {
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import jison from 'jison';
|
||||
|
||||
export const transformJison = (src: string): string => {
|
||||
// @ts-ignore - Jison is not typed properly
|
||||
const parser = new jison.Generator(src, {
|
||||
moduleType: 'js',
|
||||
'token-stack': true,
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { PluginOption } from 'vite';
|
||||
import { getDefaults, getSchema, loadSchema } from '../.build/jsonSchema.js';
|
||||
|
||||
/**
|
||||
* Vite plugin that handles JSON Schemas saved as a `.schema.yaml` file.
|
||||
*
|
||||
* Use `my-example.schema.yaml?only-defaults=true` to only load the default values.
|
||||
*/
|
||||
export default function jsonSchemaPlugin(): PluginOption {
|
||||
return {
|
||||
name: 'json-schema-plugin',
|
||||
transform(src: string, id: string) {
|
||||
const idAsUrl = new URL(id, 'file:///');
|
||||
|
||||
if (!idAsUrl.pathname.endsWith('schema.yaml')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jsonSchema = loadSchema(src, idAsUrl.pathname);
|
||||
return {
|
||||
code: idAsUrl.searchParams.get('only-defaults')
|
||||
? getDefaults(jsonSchema)
|
||||
: getSchema(jsonSchema),
|
||||
map: null, // no source map
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
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();
|
||||
@@ -15,17 +14,16 @@ async function createServer() {
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
for (const { packageName } of Object.values(packageOptions)) {
|
||||
app.use(express.static(`./packages/${packageName}/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'));
|
||||
app.use(vite.middlewares);
|
||||
app.use(express.static('demos'));
|
||||
app.use(express.static('cypress/platform'));
|
||||
|
||||
app.listen(9000, () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Listening on http://localhost:9000`);
|
||||
});
|
||||
}
|
||||
|
||||
void createServer();
|
||||
createServer();
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -2,7 +2,7 @@
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"vitest.explorer",
|
||||
"zixuanchen.vitest-explorer",
|
||||
"luniclynx.bison"
|
||||
]
|
||||
}
|
||||
|
||||
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
@@ -17,9 +17,8 @@
|
||||
"name": "Docs generation",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": ["scripts/docs.cli.mts"],
|
||||
// we'll need to change this to --import in Node.JS v20.6.0 and up
|
||||
"runtimeArgs": ["--loader", "tsx/esm"],
|
||||
"args": ["src/docs.mts"],
|
||||
"runtimeArgs": ["--loader", "ts-node/esm"],
|
||||
"cwd": "${workspaceRoot}/packages/mermaid",
|
||||
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
|
||||
"smartStep": true,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
./packages/mermaid/CHANGELOG.md
|
||||
1005
CHANGELOG.md
Normal file
1005
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -59,8 +59,8 @@ representative at an online or offline event.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at <security@mermaid.live>.
|
||||
|
||||
reported to the community leaders responsible for enforcement at security@mermaid.live
|
||||
.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
./packages/mermaid/src/docs/community/contributing.md
|
||||
71
CONTRIBUTING.md
Normal file
71
CONTRIBUTING.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Contributing
|
||||
|
||||
Please read in detail about how to contribute documentation and code on the [Mermaid documentation site.](https://mermaid-js.github.io/mermaid/#/development)
|
||||
|
||||
---
|
||||
|
||||
# Mermaid contribution cheat-sheet
|
||||
|
||||
## Requirements
|
||||
|
||||
- [volta](https://volta.sh/) to manage node versions.
|
||||
- [Node.js](https://nodejs.org/en/). `volta install node`
|
||||
- [pnpm](https://pnpm.io/) package manager. `volta install pnpm`
|
||||
|
||||
## Development Installation
|
||||
|
||||
If you don't have direct access to push to mermaid repositories, make a fork first. Then clone. Or clone directly from mermaid-js:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:mermaid-js/mermaid.git
|
||||
cd mermaid
|
||||
```
|
||||
|
||||
Install required packages:
|
||||
|
||||
```bash
|
||||
# npx is required for first install as volta support for pnpm is not added yet.
|
||||
npx pnpm install
|
||||
pnpm test
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
If you are using docker and docker-compose, you have self-documented `run` bash script, which is a convenient alias for docker-compose commands:
|
||||
|
||||
```bash
|
||||
./run install # npx pnpm install
|
||||
./run test # pnpm test
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Run unit test
|
||||
pnpm test
|
||||
# Run unit test in watch mode
|
||||
pnpm test:watch
|
||||
# Run E2E test
|
||||
pnpm e2e
|
||||
# Debug E2E tests
|
||||
pnpm dev
|
||||
pnpm cypress:open # in another terminal
|
||||
```
|
||||
|
||||
## Branch name format:
|
||||
|
||||
```text
|
||||
[feature | bug | chore | docs]/[issue number]_[short description using dashes ('-') or underscores ('_') instead of spaces]
|
||||
```
|
||||
|
||||
eg: `feature/2945_state-diagram-new-arrow-florbs`, `bug/1123_fix_random_ugly_red_text`
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is necessary for all non bugfix/refactoring changes.
|
||||
|
||||
Only make changes to files are in [`/packages/mermaid/src/docs`](packages/mermaid/src/docs)
|
||||
|
||||
**_DO NOT CHANGE FILES IN `/docs`_**
|
||||
|
||||
[Join our slack community if you want closer contact!](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
13
Dockerfile
13
Dockerfile
@@ -1,13 +0,0 @@
|
||||
FROM node:22.12.0-alpine3.19@sha256:40dc4b415c17b85bea9be05314b4a753f45a4e1716bb31c01182e6c53d51a654
|
||||
|
||||
USER 0:0
|
||||
|
||||
RUN corepack enable \
|
||||
&& corepack enable pnpm
|
||||
|
||||
RUN apk add --no-cache git~=2.43 \
|
||||
&& git config --add --system safe.directory /mermaid
|
||||
|
||||
ENV NODE_OPTIONS="--max_old_space_size=8192"
|
||||
|
||||
EXPOSE 9000 3333
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"drips": {
|
||||
"ethereum": {
|
||||
"ownedBy": "0x0831DDFe60d009d9448CC976157b539089aB821E"
|
||||
}
|
||||
}
|
||||
}
|
||||
115
README.md
115
README.md
@@ -15,14 +15,11 @@ Generate diagrams from markdown-like text.
|
||||
<a href="https://mermaid.live/"><b>Live Editor!</b></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://mermaid.js.org">📖 Documentation</a> | <a href="https://mermaid.js.org/intro/">🚀 Getting Started</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://discord.gg/sKeNQX4Wtj" title="Discord invite">🙌 Join Us</a>
|
||||
<a href="https://mermaid.js.org">📖 Documentation</a> | <a href="https://mermaid.js.org/intro/">🚀 Getting Started</a> | <a href="https://www.jsdelivr.com/package/npm/mermaid">🌐 CDN</a> | <a href="https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE" title="Slack invite">🙌 Join Us</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="./README.zh-CN.md">简体中文</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
Try Live Editor previews of future releases: <a href="https://develop.git.mermaid.live/" title="Try the mermaid version from the develop branch.">Develop</a> | <a href="https://next.git.mermaid.live/" title="Try the mermaid version from the next branch.">Next</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -33,10 +30,8 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
|
||||
[](https://app.codecov.io/github/mermaid-js/mermaid/tree/develop)
|
||||
[](https://www.jsdelivr.com/package/npm/mermaid)
|
||||
[](https://www.npmjs.com/package/mermaid)
|
||||
[](https://discord.gg/sKeNQX4Wtj)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
[](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/mermaid-js/mermaid)
|
||||
[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
[](https://twitter.com/mermaidjs_)
|
||||
|
||||
<img src="./img/header.png" alt="" />
|
||||
|
||||
@@ -44,23 +39,7 @@ Try Live Editor previews of future releases: <a href="https://develop.git.mermai
|
||||
|
||||
**Thanks to all involved, people committing pull requests, people answering questions! 🙏**
|
||||
|
||||
<a href="https://mermaid.js.org/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt='Banner for "The Official Guide to Mermaid.js" book'></a>
|
||||
|
||||
## Table of content
|
||||
|
||||
<details>
|
||||
<summary>Expand contents</summary>
|
||||
|
||||
- [About](#about)
|
||||
- [Examples](#examples)
|
||||
- [Release](#release)
|
||||
- [Related projects](#related-projects)
|
||||
- [Contributors](#contributors---)
|
||||
- [Security and safe diagrams](#security-and-safe-diagrams)
|
||||
- [Reporting vulnerabilities](#reporting-vulnerabilities)
|
||||
- [Appreciation](#appreciation)
|
||||
|
||||
</details>
|
||||
<a href="https://mermaid-js.github.io/mermaid/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a>
|
||||
|
||||
## About
|
||||
|
||||
@@ -76,16 +55,12 @@ Mermaid addresses this problem by enabling users to create easily modifiable dia
|
||||
<br/>
|
||||
|
||||
Mermaid allows even non-programmers to easily create detailed diagrams through the [Mermaid Live Editor](https://mermaid.live/).<br/>
|
||||
For video tutorials, visit our [Tutorials](https://mermaid.js.org/ecosystem/tutorials.html) page.
|
||||
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](https://mermaid.js.org/ecosystem/integrations-community.html).
|
||||
[Tutorials](./docs/config/Tutorials.md) has video tutorials.
|
||||
Use Mermaid with your favorite applications, check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
|
||||
|
||||
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](https://mermaid.js.org/ecosystem/integrations-community.html).
|
||||
You can also use Mermaid within [GitHub](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) as well many of your other favorite applications—check out the list of [Integrations and Usages of Mermaid](./docs/ecosystem/integrations.md).
|
||||
|
||||
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](https://mermaid.js.org/intro/getting-started.html), [Usage](https://mermaid.js.org/config/usage.html) and [Tutorials](https://mermaid.js.org/ecosystem/tutorials.html).
|
||||
|
||||
Our PR Visual Regression Testing is powered by [Argos](https://argos-ci.com/?utm_source=mermaid&utm_campaign=oss) with their generous Open Source plan. It makes the process of reviewing PRs with visual changes a breeze.
|
||||
|
||||
[](https://argos-ci.com?utm_source=mermaid&utm_campaign=oss)
|
||||
For a more detailed introduction to Mermaid and some of its more basic uses, look to the [Beginner's Guide](./docs/community/n00b-overview.md), [Usage](./docs/config/usage.md) and [Tutorials](./docs/config/Tutorials.md).
|
||||
|
||||
In our release process we rely heavily on visual regression tests using [applitools](https://applitools.com/). Applitools is a great service which has been easy to use and integrate with our tests.
|
||||
|
||||
@@ -97,11 +72,11 @@ In our release process we rely heavily on visual regression tests using [applito
|
||||
|
||||
## Examples
|
||||
|
||||
**The following are some examples of the diagrams, charts and graphs that can be made using Mermaid. Click here to jump into the [text syntax](https://mermaid.js.org/intro/syntax-reference.html).**
|
||||
**The following are some examples of the diagrams, charts and graphs that can be made using Mermaid. Click here to jump into the [text syntax](https://mermaid-js.github.io/mermaid/#/n00b-syntaxReference).**
|
||||
|
||||
<!-- <Flowchart> -->
|
||||
|
||||
### Flowchart [<a href="https://mermaid.js.org/syntax/flowchart.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNpNkMtqwzAQRX9FzKqFJK7t1km8KDQP6KJQSLOLvZhIY1tgS0GWmgbb_165IaFaiXvOFTPqgGtBkEJR6zOv0Fj2scsU8-ft8I5G5Gw6fe339GN7tnrYaafE45WvRsLW3Ya4bKVWwzVe_xU-FfVsc9hR62rLwvw_2591z7Y3FuUwgYZMg1L4ObrRzMBW1FAGqb8KKtCLGWRq8Ko7CbS0FdJqA2mBdUsTQGf110VxSK1xdJM2EkuDzd2qNQrypQ7s5TQuXcrW-ie5VoUsx9yZ2seVtac2DYIRz0ppK3eccd0ErRTjD1XfyyRIomSBUUzJPMaXOBb8GC4XRfQcFmL-FEYIwzD8AggvcHE">live editor</a>]
|
||||
### Flowchart [<a href="https://mermaid-js.github.io/mermaid/#/flowchart">docs</a> - <a href="https://mermaid.live/edit#pako:eNpNkMtqwzAQRX9FzKqFJK7t1km8KDQP6KJQSLOLvZhIY1tgS0GWmgbb_165IaFaiXvOFTPqgGtBkEJR6zOv0Fj2scsU8-ft8I5G5Gw6fe339GN7tnrYaafE45WvRsLW3Ya4bKVWwzVe_xU-FfVsc9hR62rLwvw_2591z7Y3FuUwgYZMg1L4ObrRzMBW1FAGqb8KKtCLGWRq8Ko7CbS0FdJqA2mBdUsTQGf110VxSK1xdJM2EkuDzd2qNQrypQ7s5TQuXcrW-ie5VoUsx9yZ2seVtac2DYIRz0ppK3eccd0ErRTjD1XfyyRIomSBUUzJPMaXOBb8GC4XRfQcFmL-FEYIwzD8AggvcHE">live editor</a>]
|
||||
|
||||
```
|
||||
flowchart LR
|
||||
@@ -121,12 +96,12 @@ C -->|One| D[Result 1]
|
||||
C -->|Two| E[Result 2]
|
||||
```
|
||||
|
||||
### Sequence diagram [<a href="https://mermaid.js.org/syntax/sequenceDiagram.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNo9kMluwjAQhl_F-AykQMuSA1WrbuLQQ3v1ZbAnsVXHkzrjVhHi3etQwKfRv4w-z0FqMihL2eF3wqDxyUEdoVHhwTuNk-12RzaU4g29JzHMY2HpV0BE0VO6V8ETtdkGz1Zb1F8qiPyG5LX84mrLAmpwoWNh-5a0pWCiAxUwGBXeiVHEU4oq8V_6AHYUwAu2lLLTjVQ4bc1rT2yleI0IfJG320faZ9ABbk-Jz3hZnFxBduR9L2oiM5Jj2WBswJn8-cMArSRbbFDJMo8GK0ielVThmKOpNcD4bBxTlGUFvsOxhMT02QctS44JL6HzAS-iJzCYOwfJfTscunYd542aQuXqQU_RZ9kyt11ZFIM9rR3btJ9qaorOGQuR7c9mWSznyzXMF7hcLeBusTB6P9usq_ntrDKrm9kc5PF4_AMJE56Z">live editor</a>]
|
||||
### Sequence diagram [<a href="https://mermaid-js.github.io/mermaid/#/sequenceDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNo9kMluwjAQhl_F-AykQMuSA1WrbuLQQ3v1ZbAnsVXHkzrjVhHi3etQwKfRv4w-z0FqMihL2eF3wqDxyUEdoVHhwTuNk-12RzaU4g29JzHMY2HpV0BE0VO6V8ETtdkGz1Zb1F8qiPyG5LX84mrLAmpwoWNh-5a0pWCiAxUwGBXeiVHEU4oq8V_6AHYUwAu2lLLTjVQ4bc1rT2yleI0IfJG320faZ9ABbk-Jz3hZnFxBduR9L2oiM5Jj2WBswJn8-cMArSRbbFDJMo8GK0ielVThmKOpNcD4bBxTlGUFvsOxhMT02QctS44JL6HzAS-iJzCYOwfJfTscunYd542aQuXqQU_RZ9kyt11ZFIM9rR3btJ9qaorOGQuR7c9mWSznyzXMF7hcLeBusTB6P9usq_ntrDKrm9kc5PF4_AMJE56Z">live editor</a>]
|
||||
|
||||
```
|
||||
sequenceDiagram
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop HealthCheck
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts!
|
||||
@@ -138,7 +113,7 @@ Bob-->>John: Jolly good!
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop HealthCheck
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts!
|
||||
@@ -147,7 +122,7 @@ John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
```
|
||||
|
||||
### Gantt chart [<a href="https://mermaid.js.org/syntax/gantt.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNp90cGOgyAQBuBXIZxtFbG29bbZ3fsmvXKZylhJEAyOTZrGd1_sto3xsHMBhu-HBO689hp5xS_giJQbsCbjHTv9jcp9-q63SKhZpb3DhMXSOIiE5ZkoNpnYZGXynh6U-4jBK7JnVfBYJo9QvgjtEya1cj8QwFq0TMz4lZqxTBg0hOF5m1jifI2Lf7Bc490CyxUu1rhc4GLGPOEdhg6Mjq92V44xxanFDhWv4lRjA6MlxZWbIh17DYTf2pAPvGrADphwGMmfbq7mFYURX-jLwCVA91bWg8YYunO69Y8vMgPFI2vvGnOZ-2Owsd0S9UOVpvP29mKoHc_b2nfpYHQLgdrrsUzLvDxALrHcS9hJqeuzOB6avBCN3mciBz5N0y_wxZ0J">live editor</a>]
|
||||
### Gantt chart [<a href="https://mermaid-js.github.io/mermaid/#/gantt">docs</a> - <a href="https://mermaid.live/edit#pako:eNp90cGOgyAQBuBXIZxtFbG29bbZ3fsmvXKZylhJEAyOTZrGd1_sto3xsHMBhu-HBO689hp5xS_giJQbsCbjHTv9jcp9-q63SKhZpb3DhMXSOIiE5ZkoNpnYZGXynh6U-4jBK7JnVfBYJo9QvgjtEya1cj8QwFq0TMz4lZqxTBg0hOF5m1jifI2Lf7Bc490CyxUu1rhc4GLGPOEdhg6Mjq92V44xxanFDhWv4lRjA6MlxZWbIh17DYTf2pAPvGrADphwGMmfbq7mFYURX-jLwCVA91bWg8YYunO69Y8vMgPFI2vvGnOZ-2Owsd0S9UOVpvP29mKoHc_b2nfpYHQLgdrrsUzLvDxALrHcS9hJqeuzOB6avBCN3mciBz5N0y_wxZ0J">live editor</a>]
|
||||
|
||||
```
|
||||
gantt
|
||||
@@ -171,7 +146,7 @@ gantt
|
||||
Parallel 4 : des6, after des4, 1d
|
||||
```
|
||||
|
||||
### Class diagram [<a href="https://mermaid.js.org/syntax/classDiagram.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkTFPwzAQhf-K5QlQ2zQJJG1UBaGWDYmBgYEwXO1LYuTEwXYqlZL_jt02asXm--690zvfgTLFkWaUSTBmI6DS0BTt2lfzkKx-p1PytEO9f1FtdaQkI2ulZNGuVqK1qEtgmOfk7BitSzKdOhg59XuNGgk0RDxed-_IOr6uf8cZ6UhTZ8bvHqS5ub1mr9svZPbjk6DEBlu7AQuXyBkx4gcvDk9cUMJq0XT_YaW0kNK5j-ufAoRzcihaQvLcoN4Jv50vvVxw_xrnD3RCG9QNCO4-8OgpqK1dpoJm7smxhF7agp6kfcfB4jMXVmmalW4tnFDorXrbt4xmVvc4is53GKFUwNF5DtTuO3-sShjrJjLVlqLyvNfS4drazmRB4NuzSti6386YagIjeA3a1rtlEiRRsoAoxiSN4SGOOduGy0UZ3YclT-dhBHQYhj8dc6_I">live editor</a>]
|
||||
### Class diagram [<a href="https://mermaid-js.github.io/mermaid/#/classDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkTFPwzAQhf-K5QlQ2zQJJG1UBaGWDYmBgYEwXO1LYuTEwXYqlZL_jt02asXm--690zvfgTLFkWaUSTBmI6DS0BTt2lfzkKx-p1PytEO9f1FtdaQkI2ulZNGuVqK1qEtgmOfk7BitSzKdOhg59XuNGgk0RDxed-_IOr6uf8cZ6UhTZ8bvHqS5ub1mr9svZPbjk6DEBlu7AQuXyBkx4gcvDk9cUMJq0XT_YaW0kNK5j-ufAoRzcihaQvLcoN4Jv50vvVxw_xrnD3RCG9QNCO4-8OgpqK1dpoJm7smxhF7agp6kfcfB4jMXVmmalW4tnFDorXrbt4xmVvc4is53GKFUwNF5DtTuO3-sShjrJjLVlqLyvNfS4drazmRB4NuzSti6386YagIjeA3a1rtlEiRRsoAoxiSN4SGOOduGy0UZ3YclT-dhBHQYhj8dc6_I">live editor</a>]
|
||||
|
||||
```
|
||||
classDiagram
|
||||
@@ -190,7 +165,13 @@ class Class10 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
|
||||
namespace Namespace01 {
|
||||
class Class11
|
||||
class Class12 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```mermaid
|
||||
@@ -210,10 +191,16 @@ class Class10 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
|
||||
namespace Namespace01 {
|
||||
class Class11
|
||||
class Class12 {
|
||||
int id
|
||||
size()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### State diagram [<a href="https://mermaid.js.org/syntax/stateDiagram.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkEFvgzAMhf8K8nEqpYSNthx22Xbcqcexg0sCiZQQlDhIFeK_L8A6TfXp6fOz9ewJGssFVOAJSbwr7ByadGR1n8T6evpO0vQ1uZDSekOrXGFsPqJPO6q-2-imH8f_0TeHXm50lfelsAMjnEHFY6xpMdRAUhhRQxUlFy0GTTXU_RytYeAx-AdXZB1ULWovdoCB7OXWN1CRC-Ju-r3uz6UtchGHJqDbsPygU57iysb2reoWHpyOWBINvsqypb3vFMlw3TfWZF5xiY7keC6zkpUnZIUojwW-FAVvrvn51LLnvOXHQ84Q5nn-AVtLcwk">live editor</a>]
|
||||
### State diagram [<a href="https://mermaid-js.github.io/mermaid/#/stateDiagram">docs</a> - <a href="https://mermaid.live/edit#pako:eNpdkEFvgzAMhf8K8nEqpYSNthx22Xbcqcexg0sCiZQQlDhIFeK_L8A6TfXp6fOz9ewJGssFVOAJSbwr7ByadGR1n8T6evpO0vQ1uZDSekOrXGFsPqJPO6q-2-imH8f_0TeHXm50lfelsAMjnEHFY6xpMdRAUhhRQxUlFy0GTTXU_RytYeAx-AdXZB1ULWovdoCB7OXWN1CRC-Ju-r3uz6UtchGHJqDbsPygU57iysb2reoWHpyOWBINvsqypb3vFMlw3TfWZF5xiY7keC6zkpUnZIUojwW-FAVvrvn51LLnvOXHQ84Q5nn-AVtLcwk">live editor</a>]
|
||||
|
||||
```
|
||||
stateDiagram-v2
|
||||
@@ -235,7 +222,7 @@ Moving --> Crash
|
||||
Crash --> [*]
|
||||
```
|
||||
|
||||
### Pie chart [<a href="https://mermaid.js.org/syntax/pie.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNo9jsFugzAMhl8F-VzBgEEh13Uv0F1zcYkTIpEEBadShXj3BU3dzf_n77e8wxQUgYDVkvQSbsFsEgpRtEN_5i_kvzx05XiC-xvUHVzAUXRoVe7v0heFBJ7JkQSRR0Ua08ISpD-ymlaFTN_KcoggNC4bXQATh5-Xn0BwTPSWbhZNRPdvLQEV5dIO_FrPZ43dOJ-cgtfWnDzFJeOZed1EVZ3r0lie06Ocgqs2q2aMPD_HvuqbfsCmpf7aYte2anrU46Cbz1qr60fdIBzH8QvW9lkl">live editor</a>]
|
||||
### Pie chart [<a href="https://mermaid-js.github.io/mermaid/#/pie">docs</a> - <a href="https://mermaid.live/edit#pako:eNo9jsFugzAMhl8F-VzBgEEh13Uv0F1zcYkTIpEEBadShXj3BU3dzf_n77e8wxQUgYDVkvQSbsFsEgpRtEN_5i_kvzx05XiC-xvUHVzAUXRoVe7v0heFBJ7JkQSRR0Ua08ISpD-ymlaFTN_KcoggNC4bXQATh5-Xn0BwTPSWbhZNRPdvLQEV5dIO_FrPZ43dOJ-cgtfWnDzFJeOZed1EVZ3r0lie06Ocgqs2q2aMPD_HvuqbfsCmpf7aYte2anrU46Cbz1qr60fdIBzH8QvW9lkl">live editor</a>]
|
||||
|
||||
```
|
||||
pie
|
||||
@@ -253,35 +240,7 @@ pie
|
||||
|
||||
### Git graph [experimental - <a href="https://mermaid.live/edit#pako:eNqNkMFugzAMhl8F-VyVAR1tOW_aA-zKxSSGRCMJCk6lCvHuNZPKZdM0n-zf3_8r8QIqaIIGMqnB8kfEybQ--y4VnLP8-9RF9Mpkmm40hmlnDKmvkPiH_kfS7nFo_VN0FAf6XwocQGgxa_nGsm1bYEOOWmik1dRjGrmF1q-Cpkkj07u2HCI0PY4zHQATh8-7V9BwTPSE3iwOEd1OjQE1iWkBvk_bzQY7s0Sq4Hs7bHqKo8iGeZqbPN_WR7mpSd1RHpvPVhuMbG7XOq_L-oJlRfW5wteq0qorrpe-PBW9Pr8UJcK6rg-BLYPQ">live editor</a>]
|
||||
|
||||
```
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
```
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
```
|
||||
|
||||
### Bar chart (using gantt chart) [<a href="https://mermaid.js.org/syntax/gantt.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNptkU1vhCAQhv8KIenNugiI4rkf6bmXpvEyFVxJFDYyNt1u9r8X63Z7WQ9m5pknLzieaBeMpQ3dg0dsPUkPOhwteXZIXmJcbCT3xMAxkuh8Z8kIEclyMIB209fqKcwTICFvG4IvFy_oLrZ-g9F26ILfQgvNFN94VaRXQ1iWqpumZBcu1J8p1E1TXDx59eQNr5LyEqjJn6hv5QnGNlxevZJmdLLpy5xJSzut45biYCfb0iaVxvawjNjS1p-TCguG16PvaIPzYjO67e3BwX6GiTY9jPFKH43DMF_hGMDY1J4oHg-_f8hFTJFd8L3br3yZx4QHxENsdrt1nO8dDstH3oVpF50ZYMbhU6ud4qoGLqyqBJRCmO6j0HXPZdGbihUc6Pmc0QP49xD-b5X69ZQv2gjO81IwzWqhC1lKrjJ6pA3nVS7SMiVjrKirWlYp5fs3osgrWeo00lorLWvOzz8JVbXm">live editor</a>]
|
||||
### Bar chart (using gantt chart) [<a href="https://mermaid-js.github.io/mermaid/#/gantt">docs</a> - <a href="https://mermaid.live/edit#pako:eNptkU1vhCAQhv8KIenNugiI4rkf6bmXpvEyFVxJFDYyNt1u9r8X63Z7WQ9m5pknLzieaBeMpQ3dg0dsPUkPOhwteXZIXmJcbCT3xMAxkuh8Z8kIEclyMIB209fqKcwTICFvG4IvFy_oLrZ-g9F26ILfQgvNFN94VaRXQ1iWqpumZBcu1J8p1E1TXDx59eQNr5LyEqjJn6hv5QnGNlxevZJmdLLpy5xJSzut45biYCfb0iaVxvawjNjS1p-TCguG16PvaIPzYjO67e3BwX6GiTY9jPFKH43DMF_hGMDY1J4oHg-_f8hFTJFd8L3br3yZx4QHxENsdrt1nO8dDstH3oVpF50ZYMbhU6ud4qoGLqyqBJRCmO6j0HXPZdGbihUc6Pmc0QP49xD-b5X69ZQv2gjO81IwzWqhC1lKrjJ6pA3nVS7SMiVjrKirWlYp5fs3osgrWeo00lorLWvOzz8JVbXm">live editor</a>]
|
||||
|
||||
```
|
||||
gantt
|
||||
@@ -319,7 +278,7 @@ gantt
|
||||
5 : 0, 5
|
||||
```
|
||||
|
||||
### User Journey diagram [<a href="https://mermaid.js.org/syntax/userJourney.html">docs</a> - <a href="https://mermaid.live/edit#pako:eNplkMFuwjAQRH9l5TMiTVIC-FqqnjhxzWWJN4khsSN7XRSh_HsdKBVt97R6Mzsj-yoqq0hIAXCywRkaSwNxWHNHsB_hYt1ZmwYUfiueKtbWwIcFtjf5zgH2eCZgQgkrCXt64GgMg2fUzkvIn5Xd_V5COtMFvCH_62ht_5yk7MU8sn61HDTfxD8VYiF6cj1qFd94nWkpuKWYKWRcFdUYOi5FaaZoDYNCpnel2Toha-w8LQQGtofRVEKyC_Qw7TQ2DvsfV2dRUTy6Ch6H-UMb7TlGVtbUupl5cF3ELfPgZZLM8rLR3IbjsrJ94rVq0XH7uS2SIis2mOVUrHNc5bmqjul2U2evaa3WL2mGYpqmL2BGiho">live editor</a>]
|
||||
### User Journey diagram [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">docs</a> - <a href="https://mermaid.live/edit#pako:eNplkMFuwjAQRH9l5TMiTVIC-FqqnjhxzWWJN4khsSN7XRSh_HsdKBVt97R6Mzsj-yoqq0hIAXCywRkaSwNxWHNHsB_hYt1ZmwYUfiueKtbWwIcFtjf5zgH2eCZgQgkrCXt64GgMg2fUzkvIn5Xd_V5COtMFvCH_62ht_5yk7MU8sn61HDTfxD8VYiF6cj1qFd94nWkpuKWYKWRcFdUYOi5FaaZoDYNCpnel2Toha-w8LQQGtofRVEKyC_Qw7TQ2DvsfV2dRUTy6Ch6H-UMb7TlGVtbUupl5cF3ELfPgZZLM8rLR3IbjsrJ94rVq0XH7uS2SIis2mOVUrHNc5bmqjul2U2evaa3WL2mGYpqmL2BGiho">live editor</a>]
|
||||
|
||||
```
|
||||
journey
|
||||
@@ -345,7 +304,7 @@ gantt
|
||||
Sit down: 3: Me
|
||||
```
|
||||
|
||||
### C4 diagram [<a href="https://mermaid.js.org/syntax/c4.html">docs</a>]
|
||||
### C4 diagram [<a href="https://mermaid-js.github.io/mermaid/#/c4c">docs</a>]
|
||||
|
||||
```
|
||||
C4Context
|
||||
@@ -439,7 +398,7 @@ The above command generates files into the `dist` folder and publishes them to <
|
||||
|
||||
Mermaid is a growing community and is always accepting new contributors. There's a lot of different ways to help out and we're always looking for extra hands! Look at [this issue](https://github.com/mermaid-js/mermaid/issues/866) if you want to know where to start helping out.
|
||||
|
||||
Detailed information about how to contribute can be found in the [contribution guide](https://mermaid.js.org/community/contributing.html)
|
||||
Detailed information about how to contribute can be found in the [contribution guide](CONTRIBUTING.md)
|
||||
|
||||
## Security and safe diagrams
|
||||
|
||||
@@ -447,7 +406,7 @@ For public sites, it can be precarious to retrieve text from users on the intern
|
||||
|
||||
As an extra level of security for sites with external users we are happy to introduce a new security level in which the diagram is rendered in a sandboxed iframe preventing javascript in the code from being executed. This is a great step forward for better security.
|
||||
|
||||
_Unfortunately you cannot have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code._
|
||||
_Unfortunately you can not have a cake and eat it at the same time which in this case means that some of the interactive functionality gets blocked along with the possible malicious code._
|
||||
|
||||
## Reporting vulnerabilities
|
||||
|
||||
@@ -463,7 +422,7 @@ A quick note from Knut Sveidqvist:
|
||||
>
|
||||
> _Thank you to [Tyler Long](https://github.com/tylerlong) who has been a collaborator since April 2017._
|
||||
>
|
||||
> _Thank you to the ever-growing list of [contributors](https://github.com/mermaid-js/mermaid/graphs/contributors) that brought the project this far!_
|
||||
> _Thank you to the ever-growing list of [contributors](https://github.com/knsv/mermaid/graphs/contributors) that brought the project this far!_
|
||||
|
||||
---
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user