Merge branch 'develop' into feature/6314_state-click

This commit is contained in:
BambioGaming
2025-05-09 13:51:51 +01:00
committed by GitHub
24 changed files with 676 additions and 326 deletions

View File

@@ -0,0 +1,6 @@
---
'mermaid': minor
'@mermaid-js/parser': minor
---
feat: Add shorter `+<count>: Label` syntax in packet diagram

View File

@@ -1,5 +1,5 @@
import { build } from 'esbuild';
import { mkdir, writeFile } from 'node:fs/promises';
import { 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';
@@ -31,7 +31,15 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
// mermaid.js
{ ...iifeOptions },
// mermaid.min.js
{ ...iifeOptions, minify: true, metafile: shouldVisualize }
{ ...iifeOptions, minify: true, metafile: shouldVisualize },
// mermaid.tiny.min.js
{
...iifeOptions,
minify: true,
includeLargeFeatures: false,
metafile: shouldVisualize,
sourcemap: false,
}
);
}
if (entryName === 'mermaid-zenuml') {
@@ -70,6 +78,20 @@ const handler = (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');
};
const main = async () => {
await generateLangium();
await mkdir('stats', { recursive: true });
@@ -78,6 +100,7 @@ const main = async () => {
for (const pkg of packageNames) {
await buildPackage(pkg).catch(handler);
}
await buildTinyMermaid();
};
void main();

View File

@@ -14,6 +14,7 @@ export interface MermaidBuildOptions extends BuildOptions {
metafile: boolean;
format: 'esm' | 'iife';
options: PackageOptions;
includeLargeFeatures: boolean;
}
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'> = {
@@ -21,6 +22,7 @@ export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'>
metafile: false,
core: false,
format: 'esm',
includeLargeFeatures: true,
} as const;
const buildOptions = (override: BuildOptions): BuildOptions => {
@@ -39,12 +41,18 @@ const buildOptions = (override: BuildOptions): BuildOptions => {
};
};
const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOptions) => {
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';
}
@@ -54,25 +62,27 @@ const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOpt
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const {
core,
metafile,
format,
minify,
options: { name, file, packageName },
globalName = 'mermaid',
includeLargeFeatures,
...rest
} = options;
const external: string[] = ['require', 'fs', 'path'];
const outFileName = getFileName(name, options);
const output: BuildOptions = buildOptions({
...rest,
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
entryPoints: {
[outFileName]: `src/${file}`,
},
metafile,
minify,
globalName,
logLevel: 'info',
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
includeLargeFeatures: `${includeLargeFeatures}`,
'import.meta.vitest': 'undefined',
},
});

View File

@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
uses: peter-evans/create-pull-request@3b1f4bffdc97d7b055dd96732d7348e585ad2c4e
uses: peter-evans/create-pull-request@889dce9eaba7900ce30494f5e1ac7220b27e5c81
with:
add-paths: |
cypress/timings.json

View File

@@ -94,6 +94,10 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
}),
...visualizerOptions(packageName, core),
],
define: {
// Needs to be string
includeLargeFeatures: 'true',
},
};
if (watch && config.build) {

View File

@@ -98,6 +98,12 @@ Mermaid can load multiple diagrams, in the same page.
> Try it out, save this code as HTML and load it using any browser.
> (Except Internet Explorer, please don't use Internet Explorer.)
## Tiny Mermaid
We offer a smaller version of Mermaid that's approximately half the size of the full library. This tiny version doesn't support Mindmap Diagrams, Architecture Diagrams, KaTeX rendering, or lazy loading.
If you need a more lightweight version without these features, you can use [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny).
## Enabling Click Event and Tags in Nodes
A `securityLevel` configuration has to first be cleared. `securityLevel` sets the level of trust for the parsed diagrams and limits click functionality. This was introduced in version 8.2 as a security improvement, aimed at preventing malicious use.

View File

@@ -354,6 +354,7 @@ To Deploy Mermaid:
- [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
- [Mermaid CLI](https://github.com/mermaid-js/mermaid-cli)
- [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny)
- [Mermaid Webpack Demo](https://github.com/mermaidjs/mermaid-webpack-demo)
- [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo)

View File

@@ -16,13 +16,25 @@ This diagram type is particularly useful for developers, network engineers, educ
## Syntax
```md
```
packet-beta
start: "Block name" %% Single-bit block
start-end: "Block name" %% Multi-bit blocks
... More Fields ...
```
### Bits Syntax (v\<MERMAID_RELEASE_VERSION>+)
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:
```
packet-beta
+1: "Block name" %% Single-bit block
+8: "Block name" %% 8-bit block
9-15: "Manually set start and end, it's fine to mix and match"
... More Fields ...
```
## Examples
```mermaid-example
@@ -76,8 +88,8 @@ packet-beta
```mermaid-example
packet-beta
title UDP Packet
0-15: "Source Port"
16-31: "Destination Port"
+16: "Source Port"
+16: "Destination Port"
32-47: "Length"
48-63: "Checksum"
64-95: "Data (variable length)"
@@ -86,8 +98,8 @@ title UDP Packet
```mermaid
packet-beta
title UDP Packet
0-15: "Source Port"
16-31: "Destination Port"
+16: "Source Port"
+16: "Destination Port"
32-47: "Length"
48-63: "Checksum"
64-95: "Data (variable length)"

View File

@@ -69,7 +69,7 @@
"@changesets/cli": "^2.27.12",
"@cspell/eslint-plugin": "^8.19.3",
"@cypress/code-coverage": "^3.12.49",
"@eslint/js": "^9.25.1",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.2",
"@types/cors": "^2.8.17",
"@types/express": "^5.0.0",
@@ -93,7 +93,7 @@
"cypress-image-snapshot": "^4.0.1",
"cypress-split": "^1.24.14",
"esbuild": "^0.25.0",
"eslint": "^9.25.1",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-html": "^8.1.2",
@@ -126,7 +126,7 @@
"tslib": "^2.8.1",
"tsx": "^4.7.3",
"typescript": "~5.7.3",
"typescript-eslint": "^8.31.1",
"typescript-eslint": "^8.32.0",
"vite": "^6.1.1",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.0.6"

View File

@@ -27,6 +27,7 @@ import block from '../diagrams/block/blockDetector.js';
import architecture from '../diagrams/architecture/architectureDetector.js';
import { registerLazyLoadedDiagrams } from './detectType.js';
import { registerDiagram } from './diagramAPI.js';
import '../type.d.ts';
let hasLoadedDiagrams = false;
export const addDiagrams = () => {
@@ -69,6 +70,11 @@ export const addDiagrams = () => {
return text.toLowerCase().trimStart().startsWith('---');
}
);
if (includeLargeFeatures) {
registerLazyLoadedDiagrams(flowchartElk, mindmap, architecture);
}
// Ordering of detectors is important. The first one to return true will be used.
registerLazyLoadedDiagrams(
c4,
@@ -81,10 +87,8 @@ export const addDiagrams = () => {
pie,
requirement,
sequence,
flowchartElk,
flowchartV2,
flowchart,
mindmap,
timeline,
git,
stateV2,
@@ -95,7 +99,6 @@ export const addDiagrams = () => {
packet,
xychart,
block,
architecture,
radar
);
};

View File

@@ -341,6 +341,7 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
}
if (includeLargeFeatures) {
const { default: katex } = await import('katex');
const outputMode =
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)
@@ -364,6 +365,12 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
.replace(/\n/g, ' ')
.replace(/<annotation.*<\/annotation>/g, '')
);
}
return text.replace(
katexRegex,
'Katex is not supported in @mermaid-js/tiny. Please use the full mermaid library.'
);
};
export default {

View File

@@ -1,7 +1,9 @@
import type { InfoFields, InfoDB } from './infoTypes.js';
import packageJson from '../../../package.json' assert { type: 'json' };
export const DEFAULT_INFO_DB: InfoFields = { version: packageJson.version } as const;
export const DEFAULT_INFO_DB: InfoFields = {
version: packageJson.version + (includeLargeFeatures ? '' : '-tiny'),
} as const;
export const getVersion = (): string => DEFAULT_INFO_DB.version;

View File

@@ -30,6 +30,7 @@ describe('packet diagrams', () => {
[
[
{
"bits": 11,
"end": 10,
"label": "test",
"start": 0,
@@ -49,11 +50,13 @@ describe('packet diagrams', () => {
[
[
{
"bits": 11,
"end": 10,
"label": "test",
"start": 0,
},
{
"bits": 1,
"end": 11,
"label": "single",
"start": 11,
@@ -63,6 +66,58 @@ describe('packet diagrams', () => {
`);
});
it('should handle bit counts', async () => {
const str = `packet-beta
+8: "byte"
+16: "word"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
[
[
{
"bits": 8,
"end": 7,
"label": "byte",
"start": 0,
},
{
"bits": 16,
"end": 23,
"label": "word",
"start": 8,
},
],
]
`);
});
it('should handle bit counts with bit or bits', async () => {
const str = `packet-beta
+8: "byte"
+16: "word"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
[
[
{
"bits": 8,
"end": 7,
"label": "byte",
"start": 0,
},
{
"bits": 16,
"end": 23,
"label": "word",
"start": 8,
},
],
]
`);
});
it('should split into multiple rows', async () => {
const str = `packet-beta
0-10: "test"
@@ -73,11 +128,13 @@ describe('packet diagrams', () => {
[
[
{
"bits": 11,
"end": 10,
"label": "test",
"start": 0,
},
{
"bits": 20,
"end": 31,
"label": "multiple",
"start": 11,
@@ -85,6 +142,7 @@ describe('packet diagrams', () => {
],
[
{
"bits": 31,
"end": 63,
"label": "multiple",
"start": 32,
@@ -92,6 +150,7 @@ describe('packet diagrams', () => {
],
[
{
"bits": 26,
"end": 90,
"label": "multiple",
"start": 64,
@@ -111,11 +170,13 @@ describe('packet diagrams', () => {
[
[
{
"bits": 17,
"end": 16,
"label": "test",
"start": 0,
},
{
"bits": 14,
"end": 31,
"label": "multiple",
"start": 17,
@@ -123,6 +184,7 @@ describe('packet diagrams', () => {
],
[
{
"bits": 31,
"end": 63,
"label": "multiple",
"start": 32,
@@ -142,6 +204,16 @@ describe('packet diagrams', () => {
);
});
it('should throw error if numbers are not continuous with bit counts', async () => {
const str = `packet-beta
+16: "test"
18-20: "error"
`;
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Packet block 18 - 20 is not contiguous. It should start from 16.]`
);
});
it('should throw error if numbers are not continuous for single packets', async () => {
const str = `packet-beta
0-16: "test"
@@ -152,6 +224,16 @@ describe('packet diagrams', () => {
);
});
it('should throw error if numbers are not continuous for single packets with bit counts', async () => {
const str = `packet-beta
+16: "test"
18: "error"
`;
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Packet block 18 - 18 is not contiguous. It should start from 16.]`
);
});
it('should throw error if numbers are not continuous for single packets - 2', async () => {
const str = `packet-beta
0-16: "test"
@@ -172,4 +254,13 @@ describe('packet diagrams', () => {
`[Error: Packet block 25 - 20 is invalid. End must be greater than start.]`
);
});
it('should throw error if bit count is 0', async () => {
const str = `packet-beta
+0: "test"
`;
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Packet block 0 is invalid. Cannot have a zero bit field.]`
);
});
});

View File

@@ -10,26 +10,33 @@ const maxPacketSize = 10_000;
const populate = (ast: Packet) => {
populateCommonDb(ast, db);
let lastByte = -1;
let lastBit = -1;
let word: PacketWord = [];
let row = 1;
const { bitsPerRow } = db.getConfig();
for (let { start, end, label } of ast.blocks) {
if (end && end < start) {
for (let { start, end, bits, label } of ast.blocks) {
if (start !== undefined && end !== undefined && end < start) {
throw new Error(`Packet block ${start} - ${end} is invalid. End must be greater than start.`);
}
if (start !== lastByte + 1) {
start ??= lastBit + 1;
if (start !== lastBit + 1) {
throw new Error(
`Packet block ${start} - ${end ?? start} is not contiguous. It should start from ${
lastByte + 1
lastBit + 1
}.`
);
}
lastByte = end ?? start;
log.debug(`Packet block ${start} - ${lastByte} with label ${label}`);
if (bits === 0) {
throw new Error(`Packet block ${start} is invalid. Cannot have a zero bit field.`);
}
end ??= start + (bits ?? 1) - 1;
bits ??= end - start + 1;
lastBit = end;
log.debug(`Packet block ${start} - ${lastBit} with label ${label}`);
while (word.length <= bitsPerRow + 1 && db.getPacket().length < maxPacketSize) {
const [block, nextBlock] = getNextFittingBlock({ start, end, label }, row, bitsPerRow);
const [block, nextBlock] = getNextFittingBlock({ start, end, bits, label }, row, bitsPerRow);
word.push(block);
if (block.end + 1 === row * bitsPerRow) {
db.pushWord(word);
@@ -39,7 +46,7 @@ const populate = (ast: Packet) => {
if (!nextBlock) {
break;
}
({ start, end, label } = nextBlock);
({ start, end, bits, label } = nextBlock);
}
}
db.pushWord(word);
@@ -50,8 +57,11 @@ const getNextFittingBlock = (
row: number,
bitsPerRow: number
): [Required<PacketBlock>, PacketBlock | undefined] => {
if (block.start === undefined) {
throw new Error('start should have been set during first phase');
}
if (block.end === undefined) {
block.end = block.start;
throw new Error('end should have been set during first phase');
}
if (block.start > block.end) {
@@ -62,16 +72,20 @@ const getNextFittingBlock = (
return [block as Required<PacketBlock>, undefined];
}
const rowEnd = row * bitsPerRow - 1;
const rowStart = row * bitsPerRow;
return [
{
start: block.start,
end: row * bitsPerRow - 1,
end: rowEnd,
label: block.label,
bits: rowEnd - block.start,
},
{
start: row * bitsPerRow,
start: rowStart,
end: block.end,
label: block.label,
bits: block.end - rowStart,
},
];
};

View File

@@ -92,6 +92,12 @@ Mermaid can load multiple diagrams, in the same page.
> Try it out, save this code as HTML and load it using any browser.
> (Except Internet Explorer, please don't use Internet Explorer.)
## Tiny Mermaid
We offer a smaller version of Mermaid that's approximately half the size of the full library. This tiny version doesn't support Mindmap Diagrams, Architecture Diagrams, KaTeX rendering, or lazy loading.
If you need a more lightweight version without these features, you can use [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny).
## Enabling Click Event and Tags in Nodes
A `securityLevel` configuration has to first be cleared. `securityLevel` sets the level of trust for the parsed diagrams and limits click functionality. This was introduced in version 8.2 as a security improvement, aimed at preventing malicious use.

View File

@@ -109,6 +109,7 @@ To Deploy Mermaid:
- [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
- [Mermaid CLI](https://github.com/mermaid-js/mermaid-cli)
- [Mermaid Tiny](https://github.com/mermaid-js/mermaid/tree/develop/packages/tiny)
- [Mermaid Webpack Demo](https://github.com/mermaidjs/mermaid-webpack-demo)
- [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo)

View File

@@ -10,13 +10,25 @@ This diagram type is particularly useful for developers, network engineers, educ
## Syntax
```md
```
packet-beta
start: "Block name" %% Single-bit block
start-end: "Block name" %% Multi-bit blocks
... More Fields ...
```
### Bits Syntax (v<MERMAID_RELEASE_VERSION>+)
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:
```
packet-beta
+1: "Block name" %% Single-bit block
+8: "Block name" %% 8-bit block
9-15: "Manually set start and end, it's fine to mix and match"
... More Fields ...
```
## Examples
```mermaid-example
@@ -46,8 +58,8 @@ packet-beta
```mermaid-example
packet-beta
title UDP Packet
0-15: "Source Port"
16-31: "Destination Port"
+16: "Source Port"
+16: "Destination Port"
32-47: "Length"
48-63: "Checksum"
64-95: "Data (variable length)"

View File

@@ -5,7 +5,6 @@
// @ts-ignore TODO: Investigate D3 issue
import { select } from 'd3';
import { compile, serialize, stringify } from 'stylis';
// @ts-ignore: TODO Fix ts errors
import DOMPurify from 'dompurify';
import isEmpty from 'lodash-es/isEmpty.js';
import packageJson from '../package.json' assert { type: 'json' };

2
packages/mermaid/src/type.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
// eslint-disable-next-line no-var
declare var includeLargeFeatures: boolean;

View File

@@ -12,5 +12,10 @@ entry Packet:
;
PacketBlock:
start=INT('-' end=INT)? ':' label=STRING EOL
(
start=INT('-' end=INT)?
| '+' bits=INT
)
':' label=STRING
EOL
;

35
packages/tiny/README.md Normal file
View File

@@ -0,0 +1,35 @@
# Tiny Mermaid
This is a tiny version of mermaid that is optimized for the web. It is a subset of the mermaid library and is designed to be used in the browser via CDN.
## Lazy loading
The original mermaid library supports lazy loading, so it will be faster on the initial load, and only load the required diagrams.
This is not supported in the tiny mermaid library. So it's always recommended to use the full mermaid library unless you have a very specific reason to reduce the bundle size.
## Removals from mermaid
This does not support
- Mindmap Diagram
- Architecture Diagram
- Katex rendering
- Lazy loading
## Usage via NPM
This package is not meant to be installed directly from npm. It is designed to be used via CDN.
If you need to use mermaid in your project, please install the full [`mermaid` package](https://www.npmjs.com/package/mermaid) instead.
## Usage via CDN
```html
<!-- Format -->
<script src="https://cdn.jsdelivr.net/npm/@mermaid-js/tiny@<MERMAID_MAJOR_VERSION>/dist/mermaid.tiny.js"></script>
<!-- Pinning major version -->
<script src="https://cdn.jsdelivr.net/npm/@mermaid-js/tiny@11/dist/mermaid.tiny.js"></script>
<!-- Pinning specific version -->
<script src="https://cdn.jsdelivr.net/npm/@mermaid-js/tiny@11.6.0/dist/mermaid.tiny.js"></script>
```

View File

@@ -0,0 +1,25 @@
{
"name": "@mermaid-js/tiny",
"version": "11.6.0",
"description": "Tiny version of mermaid",
"type": "commonjs",
"main": "./dist/mermaid.tiny.js",
"scripts": {
"clean": "rimraf dist"
},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"
},
"author": "Sidharth Vinod",
"license": "MIT",
"dependencies": {},
"devDependencies": {},
"files": [
"dist/",
"README.md"
],
"publishConfig": {
"access": "public"
}
}

614
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,6 +35,8 @@ export default defineConfig({
},
},
define: {
// Needs to be string
includeLargeFeatures: 'true',
'import.meta.vitest': 'undefined',
},
});