mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-18 07:49:26 +02:00
Merge branch 'develop' into feature/6314_state-click
This commit is contained in:
6
.changeset/quiet-hotels-shine.md
Normal file
6
.changeset/quiet-hotels-shine.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
'mermaid': minor
|
||||||
|
'@mermaid-js/parser': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: Add shorter `+<count>: Label` syntax in packet diagram
|
@@ -1,5 +1,5 @@
|
|||||||
import { build } from 'esbuild';
|
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 { packageOptions } from '../.build/common.js';
|
||||||
import { generateLangium } from '../.build/generateLangium.js';
|
import { generateLangium } from '../.build/generateLangium.js';
|
||||||
import type { MermaidBuildOptions } from './util.js';
|
import type { MermaidBuildOptions } from './util.js';
|
||||||
@@ -31,7 +31,15 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
|
|||||||
// mermaid.js
|
// mermaid.js
|
||||||
{ ...iifeOptions },
|
{ ...iifeOptions },
|
||||||
// mermaid.min.js
|
// 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') {
|
if (entryName === 'mermaid-zenuml') {
|
||||||
@@ -70,6 +78,20 @@ const handler = (e) => {
|
|||||||
process.exit(1);
|
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 () => {
|
const main = async () => {
|
||||||
await generateLangium();
|
await generateLangium();
|
||||||
await mkdir('stats', { recursive: true });
|
await mkdir('stats', { recursive: true });
|
||||||
@@ -78,6 +100,7 @@ const main = async () => {
|
|||||||
for (const pkg of packageNames) {
|
for (const pkg of packageNames) {
|
||||||
await buildPackage(pkg).catch(handler);
|
await buildPackage(pkg).catch(handler);
|
||||||
}
|
}
|
||||||
|
await buildTinyMermaid();
|
||||||
};
|
};
|
||||||
|
|
||||||
void main();
|
void main();
|
||||||
|
@@ -14,6 +14,7 @@ export interface MermaidBuildOptions extends BuildOptions {
|
|||||||
metafile: boolean;
|
metafile: boolean;
|
||||||
format: 'esm' | 'iife';
|
format: 'esm' | 'iife';
|
||||||
options: PackageOptions;
|
options: PackageOptions;
|
||||||
|
includeLargeFeatures: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'> = {
|
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'> = {
|
||||||
@@ -21,6 +22,7 @@ export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'>
|
|||||||
metafile: false,
|
metafile: false,
|
||||||
core: false,
|
core: false,
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
|
includeLargeFeatures: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const buildOptions = (override: BuildOptions): BuildOptions => {
|
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) {
|
if (core) {
|
||||||
fileName += '.core';
|
fileName += '.core';
|
||||||
} else if (format === 'esm') {
|
} else if (format === 'esm') {
|
||||||
fileName += '.esm';
|
fileName += '.esm';
|
||||||
}
|
}
|
||||||
|
if (!includeLargeFeatures) {
|
||||||
|
fileName += '.tiny';
|
||||||
|
}
|
||||||
if (minify) {
|
if (minify) {
|
||||||
fileName += '.min';
|
fileName += '.min';
|
||||||
}
|
}
|
||||||
@@ -54,25 +62,27 @@ const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOpt
|
|||||||
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
|
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
|
||||||
const {
|
const {
|
||||||
core,
|
core,
|
||||||
metafile,
|
|
||||||
format,
|
format,
|
||||||
minify,
|
|
||||||
options: { name, file, packageName },
|
options: { name, file, packageName },
|
||||||
globalName = 'mermaid',
|
globalName = 'mermaid',
|
||||||
|
includeLargeFeatures,
|
||||||
|
...rest
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const external: string[] = ['require', 'fs', 'path'];
|
const external: string[] = ['require', 'fs', 'path'];
|
||||||
const outFileName = getFileName(name, options);
|
const outFileName = getFileName(name, options);
|
||||||
const output: BuildOptions = buildOptions({
|
const output: BuildOptions = buildOptions({
|
||||||
|
...rest,
|
||||||
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
|
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
|
||||||
entryPoints: {
|
entryPoints: {
|
||||||
[outFileName]: `src/${file}`,
|
[outFileName]: `src/${file}`,
|
||||||
},
|
},
|
||||||
metafile,
|
|
||||||
minify,
|
|
||||||
globalName,
|
globalName,
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
|
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
|
||||||
define: {
|
define: {
|
||||||
|
// This needs to be stringified for esbuild
|
||||||
|
includeLargeFeatures: `${includeLargeFeatures}`,
|
||||||
'import.meta.vitest': 'undefined',
|
'import.meta.vitest': 'undefined',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
2
.github/workflows/e2e-timings.yml
vendored
2
.github/workflows/e2e-timings.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
|||||||
echo "EOF" >> $GITHUB_OUTPUT
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Commit and create pull request
|
- name: Commit and create pull request
|
||||||
uses: peter-evans/create-pull-request@3b1f4bffdc97d7b055dd96732d7348e585ad2c4e
|
uses: peter-evans/create-pull-request@889dce9eaba7900ce30494f5e1ac7220b27e5c81
|
||||||
with:
|
with:
|
||||||
add-paths: |
|
add-paths: |
|
||||||
cypress/timings.json
|
cypress/timings.json
|
||||||
|
@@ -94,6 +94,10 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
|||||||
}),
|
}),
|
||||||
...visualizerOptions(packageName, core),
|
...visualizerOptions(packageName, core),
|
||||||
],
|
],
|
||||||
|
define: {
|
||||||
|
// Needs to be string
|
||||||
|
includeLargeFeatures: 'true',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (watch && config.build) {
|
if (watch && config.build) {
|
||||||
|
@@ -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.
|
> Try it out, save this code as HTML and load it using any browser.
|
||||||
> (Except Internet Explorer, please don't use Internet Explorer.)
|
> (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
|
## 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.
|
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.
|
||||||
|
@@ -354,6 +354,7 @@ To Deploy Mermaid:
|
|||||||
|
|
||||||
- [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
|
- [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
|
||||||
- [Mermaid CLI](https://github.com/mermaid-js/mermaid-cli)
|
- [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 Webpack Demo](https://github.com/mermaidjs/mermaid-webpack-demo)
|
||||||
- [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo)
|
- [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo)
|
||||||
|
|
||||||
|
@@ -16,13 +16,25 @@ This diagram type is particularly useful for developers, network engineers, educ
|
|||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
```md
|
```
|
||||||
packet-beta
|
packet-beta
|
||||||
start: "Block name" %% Single-bit block
|
start: "Block name" %% Single-bit block
|
||||||
start-end: "Block name" %% Multi-bit blocks
|
start-end: "Block name" %% Multi-bit blocks
|
||||||
... More Fields ...
|
... 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
|
## Examples
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
@@ -76,8 +88,8 @@ packet-beta
|
|||||||
```mermaid-example
|
```mermaid-example
|
||||||
packet-beta
|
packet-beta
|
||||||
title UDP Packet
|
title UDP Packet
|
||||||
0-15: "Source Port"
|
+16: "Source Port"
|
||||||
16-31: "Destination Port"
|
+16: "Destination Port"
|
||||||
32-47: "Length"
|
32-47: "Length"
|
||||||
48-63: "Checksum"
|
48-63: "Checksum"
|
||||||
64-95: "Data (variable length)"
|
64-95: "Data (variable length)"
|
||||||
@@ -86,8 +98,8 @@ title UDP Packet
|
|||||||
```mermaid
|
```mermaid
|
||||||
packet-beta
|
packet-beta
|
||||||
title UDP Packet
|
title UDP Packet
|
||||||
0-15: "Source Port"
|
+16: "Source Port"
|
||||||
16-31: "Destination Port"
|
+16: "Destination Port"
|
||||||
32-47: "Length"
|
32-47: "Length"
|
||||||
48-63: "Checksum"
|
48-63: "Checksum"
|
||||||
64-95: "Data (variable length)"
|
64-95: "Data (variable length)"
|
||||||
|
@@ -69,7 +69,7 @@
|
|||||||
"@changesets/cli": "^2.27.12",
|
"@changesets/cli": "^2.27.12",
|
||||||
"@cspell/eslint-plugin": "^8.19.3",
|
"@cspell/eslint-plugin": "^8.19.3",
|
||||||
"@cypress/code-coverage": "^3.12.49",
|
"@cypress/code-coverage": "^3.12.49",
|
||||||
"@eslint/js": "^9.25.1",
|
"@eslint/js": "^9.26.0",
|
||||||
"@rollup/plugin-typescript": "^12.1.2",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
"cypress-image-snapshot": "^4.0.1",
|
"cypress-image-snapshot": "^4.0.1",
|
||||||
"cypress-split": "^1.24.14",
|
"cypress-split": "^1.24.14",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"eslint": "^9.25.1",
|
"eslint": "^9.26.0",
|
||||||
"eslint-config-prettier": "^10.1.1",
|
"eslint-config-prettier": "^10.1.1",
|
||||||
"eslint-plugin-cypress": "^4.3.0",
|
"eslint-plugin-cypress": "^4.3.0",
|
||||||
"eslint-plugin-html": "^8.1.2",
|
"eslint-plugin-html": "^8.1.2",
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"tsx": "^4.7.3",
|
"tsx": "^4.7.3",
|
||||||
"typescript": "~5.7.3",
|
"typescript": "~5.7.3",
|
||||||
"typescript-eslint": "^8.31.1",
|
"typescript-eslint": "^8.32.0",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
"vite-plugin-istanbul": "^7.0.0",
|
"vite-plugin-istanbul": "^7.0.0",
|
||||||
"vitest": "^3.0.6"
|
"vitest": "^3.0.6"
|
||||||
|
@@ -27,6 +27,7 @@ import block from '../diagrams/block/blockDetector.js';
|
|||||||
import architecture from '../diagrams/architecture/architectureDetector.js';
|
import architecture from '../diagrams/architecture/architectureDetector.js';
|
||||||
import { registerLazyLoadedDiagrams } from './detectType.js';
|
import { registerLazyLoadedDiagrams } from './detectType.js';
|
||||||
import { registerDiagram } from './diagramAPI.js';
|
import { registerDiagram } from './diagramAPI.js';
|
||||||
|
import '../type.d.ts';
|
||||||
|
|
||||||
let hasLoadedDiagrams = false;
|
let hasLoadedDiagrams = false;
|
||||||
export const addDiagrams = () => {
|
export const addDiagrams = () => {
|
||||||
@@ -69,6 +70,11 @@ export const addDiagrams = () => {
|
|||||||
return text.toLowerCase().trimStart().startsWith('---');
|
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.
|
// Ordering of detectors is important. The first one to return true will be used.
|
||||||
registerLazyLoadedDiagrams(
|
registerLazyLoadedDiagrams(
|
||||||
c4,
|
c4,
|
||||||
@@ -81,10 +87,8 @@ export const addDiagrams = () => {
|
|||||||
pie,
|
pie,
|
||||||
requirement,
|
requirement,
|
||||||
sequence,
|
sequence,
|
||||||
flowchartElk,
|
|
||||||
flowchartV2,
|
flowchartV2,
|
||||||
flowchart,
|
flowchart,
|
||||||
mindmap,
|
|
||||||
timeline,
|
timeline,
|
||||||
git,
|
git,
|
||||||
stateV2,
|
stateV2,
|
||||||
@@ -95,7 +99,6 @@ export const addDiagrams = () => {
|
|||||||
packet,
|
packet,
|
||||||
xychart,
|
xychart,
|
||||||
block,
|
block,
|
||||||
architecture,
|
|
||||||
radar
|
radar
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -341,29 +341,36 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
|
|||||||
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
|
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { default: katex } = await import('katex');
|
if (includeLargeFeatures) {
|
||||||
const outputMode =
|
const { default: katex } = await import('katex');
|
||||||
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)
|
const outputMode =
|
||||||
? 'htmlAndMathml'
|
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)
|
||||||
: 'mathml';
|
? 'htmlAndMathml'
|
||||||
return text
|
: 'mathml';
|
||||||
.split(lineBreakRegex)
|
return text
|
||||||
.map((line) =>
|
.split(lineBreakRegex)
|
||||||
hasKatex(line)
|
.map((line) =>
|
||||||
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">${line}</div>`
|
hasKatex(line)
|
||||||
: `<div>${line}</div>`
|
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">${line}</div>`
|
||||||
)
|
: `<div>${line}</div>`
|
||||||
.join('')
|
)
|
||||||
.replace(katexRegex, (_, c) =>
|
.join('')
|
||||||
katex
|
.replace(katexRegex, (_, c) =>
|
||||||
.renderToString(c, {
|
katex
|
||||||
throwOnError: true,
|
.renderToString(c, {
|
||||||
displayMode: true,
|
throwOnError: true,
|
||||||
output: outputMode,
|
displayMode: true,
|
||||||
})
|
output: outputMode,
|
||||||
.replace(/\n/g, ' ')
|
})
|
||||||
.replace(/<annotation.*<\/annotation>/g, '')
|
.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 {
|
export default {
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
import type { InfoFields, InfoDB } from './infoTypes.js';
|
import type { InfoFields, InfoDB } from './infoTypes.js';
|
||||||
import packageJson from '../../../package.json' assert { type: 'json' };
|
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;
|
export const getVersion = (): string => DEFAULT_INFO_DB.version;
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ describe('packet diagrams', () => {
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 11,
|
||||||
"end": 10,
|
"end": 10,
|
||||||
"label": "test",
|
"label": "test",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
@@ -49,11 +50,13 @@ describe('packet diagrams', () => {
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 11,
|
||||||
"end": 10,
|
"end": 10,
|
||||||
"label": "test",
|
"label": "test",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"bits": 1,
|
||||||
"end": 11,
|
"end": 11,
|
||||||
"label": "single",
|
"label": "single",
|
||||||
"start": 11,
|
"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 () => {
|
it('should split into multiple rows', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-10: "test"
|
0-10: "test"
|
||||||
@@ -73,11 +128,13 @@ describe('packet diagrams', () => {
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 11,
|
||||||
"end": 10,
|
"end": 10,
|
||||||
"label": "test",
|
"label": "test",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"bits": 20,
|
||||||
"end": 31,
|
"end": 31,
|
||||||
"label": "multiple",
|
"label": "multiple",
|
||||||
"start": 11,
|
"start": 11,
|
||||||
@@ -85,6 +142,7 @@ describe('packet diagrams', () => {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 31,
|
||||||
"end": 63,
|
"end": 63,
|
||||||
"label": "multiple",
|
"label": "multiple",
|
||||||
"start": 32,
|
"start": 32,
|
||||||
@@ -92,6 +150,7 @@ describe('packet diagrams', () => {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 26,
|
||||||
"end": 90,
|
"end": 90,
|
||||||
"label": "multiple",
|
"label": "multiple",
|
||||||
"start": 64,
|
"start": 64,
|
||||||
@@ -111,11 +170,13 @@ describe('packet diagrams', () => {
|
|||||||
[
|
[
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 17,
|
||||||
"end": 16,
|
"end": 16,
|
||||||
"label": "test",
|
"label": "test",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"bits": 14,
|
||||||
"end": 31,
|
"end": 31,
|
||||||
"label": "multiple",
|
"label": "multiple",
|
||||||
"start": 17,
|
"start": 17,
|
||||||
@@ -123,6 +184,7 @@ describe('packet diagrams', () => {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"bits": 31,
|
||||||
"end": 63,
|
"end": 63,
|
||||||
"label": "multiple",
|
"label": "multiple",
|
||||||
"start": 32,
|
"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 () => {
|
it('should throw error if numbers are not continuous for single packets', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
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 () => {
|
it('should throw error if numbers are not continuous for single packets - 2', async () => {
|
||||||
const str = `packet-beta
|
const str = `packet-beta
|
||||||
0-16: "test"
|
0-16: "test"
|
||||||
@@ -172,4 +254,13 @@ describe('packet diagrams', () => {
|
|||||||
`[Error: Packet block 25 - 20 is invalid. End must be greater than start.]`
|
`[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.]`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -10,26 +10,33 @@ const maxPacketSize = 10_000;
|
|||||||
|
|
||||||
const populate = (ast: Packet) => {
|
const populate = (ast: Packet) => {
|
||||||
populateCommonDb(ast, db);
|
populateCommonDb(ast, db);
|
||||||
let lastByte = -1;
|
let lastBit = -1;
|
||||||
let word: PacketWord = [];
|
let word: PacketWord = [];
|
||||||
let row = 1;
|
let row = 1;
|
||||||
const { bitsPerRow } = db.getConfig();
|
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.`);
|
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(
|
throw new Error(
|
||||||
`Packet block ${start} - ${end ?? start} is not contiguous. It should start from ${
|
`Packet block ${start} - ${end ?? start} is not contiguous. It should start from ${
|
||||||
lastByte + 1
|
lastBit + 1
|
||||||
}.`
|
}.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
lastByte = end ?? start;
|
if (bits === 0) {
|
||||||
log.debug(`Packet block ${start} - ${lastByte} with label ${label}`);
|
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) {
|
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);
|
word.push(block);
|
||||||
if (block.end + 1 === row * bitsPerRow) {
|
if (block.end + 1 === row * bitsPerRow) {
|
||||||
db.pushWord(word);
|
db.pushWord(word);
|
||||||
@@ -39,7 +46,7 @@ const populate = (ast: Packet) => {
|
|||||||
if (!nextBlock) {
|
if (!nextBlock) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
({ start, end, label } = nextBlock);
|
({ start, end, bits, label } = nextBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.pushWord(word);
|
db.pushWord(word);
|
||||||
@@ -50,8 +57,11 @@ const getNextFittingBlock = (
|
|||||||
row: number,
|
row: number,
|
||||||
bitsPerRow: number
|
bitsPerRow: number
|
||||||
): [Required<PacketBlock>, PacketBlock | undefined] => {
|
): [Required<PacketBlock>, PacketBlock | undefined] => {
|
||||||
|
if (block.start === undefined) {
|
||||||
|
throw new Error('start should have been set during first phase');
|
||||||
|
}
|
||||||
if (block.end === undefined) {
|
if (block.end === undefined) {
|
||||||
block.end = block.start;
|
throw new Error('end should have been set during first phase');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block.start > block.end) {
|
if (block.start > block.end) {
|
||||||
@@ -62,16 +72,20 @@ const getNextFittingBlock = (
|
|||||||
return [block as Required<PacketBlock>, undefined];
|
return [block as Required<PacketBlock>, undefined];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rowEnd = row * bitsPerRow - 1;
|
||||||
|
const rowStart = row * bitsPerRow;
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
start: block.start,
|
start: block.start,
|
||||||
end: row * bitsPerRow - 1,
|
end: rowEnd,
|
||||||
label: block.label,
|
label: block.label,
|
||||||
|
bits: rowEnd - block.start,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: row * bitsPerRow,
|
start: rowStart,
|
||||||
end: block.end,
|
end: block.end,
|
||||||
label: block.label,
|
label: block.label,
|
||||||
|
bits: block.end - rowStart,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
@@ -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.
|
> Try it out, save this code as HTML and load it using any browser.
|
||||||
> (Except Internet Explorer, please don't use Internet Explorer.)
|
> (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
|
## 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.
|
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.
|
||||||
|
@@ -109,6 +109,7 @@ To Deploy Mermaid:
|
|||||||
|
|
||||||
- [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
|
- [Mermaid Live Editor](https://github.com/mermaid-js/mermaid-live-editor)
|
||||||
- [Mermaid CLI](https://github.com/mermaid-js/mermaid-cli)
|
- [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 Webpack Demo](https://github.com/mermaidjs/mermaid-webpack-demo)
|
||||||
- [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo)
|
- [Mermaid Parcel Demo](https://github.com/mermaidjs/mermaid-parcel-demo)
|
||||||
|
|
||||||
|
@@ -10,13 +10,25 @@ This diagram type is particularly useful for developers, network engineers, educ
|
|||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
```md
|
```
|
||||||
packet-beta
|
packet-beta
|
||||||
start: "Block name" %% Single-bit block
|
start: "Block name" %% Single-bit block
|
||||||
start-end: "Block name" %% Multi-bit blocks
|
start-end: "Block name" %% Multi-bit blocks
|
||||||
... More Fields ...
|
... 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
|
## Examples
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
@@ -46,8 +58,8 @@ packet-beta
|
|||||||
```mermaid-example
|
```mermaid-example
|
||||||
packet-beta
|
packet-beta
|
||||||
title UDP Packet
|
title UDP Packet
|
||||||
0-15: "Source Port"
|
+16: "Source Port"
|
||||||
16-31: "Destination Port"
|
+16: "Destination Port"
|
||||||
32-47: "Length"
|
32-47: "Length"
|
||||||
48-63: "Checksum"
|
48-63: "Checksum"
|
||||||
64-95: "Data (variable length)"
|
64-95: "Data (variable length)"
|
||||||
|
@@ -5,7 +5,6 @@
|
|||||||
// @ts-ignore TODO: Investigate D3 issue
|
// @ts-ignore TODO: Investigate D3 issue
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { compile, serialize, stringify } from 'stylis';
|
import { compile, serialize, stringify } from 'stylis';
|
||||||
// @ts-ignore: TODO Fix ts errors
|
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import isEmpty from 'lodash-es/isEmpty.js';
|
import isEmpty from 'lodash-es/isEmpty.js';
|
||||||
import packageJson from '../package.json' assert { type: 'json' };
|
import packageJson from '../package.json' assert { type: 'json' };
|
||||||
|
2
packages/mermaid/src/type.d.ts
vendored
Normal file
2
packages/mermaid/src/type.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// eslint-disable-next-line no-var
|
||||||
|
declare var includeLargeFeatures: boolean;
|
@@ -12,5 +12,10 @@ entry Packet:
|
|||||||
;
|
;
|
||||||
|
|
||||||
PacketBlock:
|
PacketBlock:
|
||||||
start=INT('-' end=INT)? ':' label=STRING EOL
|
(
|
||||||
;
|
start=INT('-' end=INT)?
|
||||||
|
| '+' bits=INT
|
||||||
|
)
|
||||||
|
':' label=STRING
|
||||||
|
EOL
|
||||||
|
;
|
||||||
|
35
packages/tiny/README.md
Normal file
35
packages/tiny/README.md
Normal 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>
|
||||||
|
```
|
25
packages/tiny/package.json
Normal file
25
packages/tiny/package.json
Normal 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
614
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,8 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
|
// Needs to be string
|
||||||
|
includeLargeFeatures: 'true',
|
||||||
'import.meta.vitest': 'undefined',
|
'import.meta.vitest': 'undefined',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user