Compare commits

..

1 Commits

Author SHA1 Message Date
Sidharth Vinod
fed9d3568d Refactor GitHub Actions workflow for lockfile validation
Removed Node.js setup step and pnpm action version.

Co-Authored-By: alois@aloisklink.com
2025-11-03 23:29:28 -08:00
38 changed files with 1420 additions and 2479 deletions

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Prevent HTML tags from being escaped in sandbox label rendering

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Support ComponentQueue_Ext to prevent parsing error

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Correct tooltip placement to appear near hovered element

View File

@@ -1,5 +1,3 @@
!viewbox
# It should be viewBox
# This file contains coding related terms
ALPHANUM
antiscript

View File

@@ -64,7 +64,6 @@ rscratch
shiki
Slidev
sparkline
speccharts
sphinxcontrib
ssim
stylis

View File

@@ -71,9 +71,6 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
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}`),
@@ -85,13 +82,15 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
'injected.version': `'${version}'`,
includeLargeFeatures: `${includeLargeFeatures}`,
'import.meta.vitest': 'undefined',
},
});
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

View File

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

View File

@@ -42,4 +42,5 @@ jobs:
publish: pnpm changeset:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

View File

@@ -20,7 +20,7 @@ jobs:
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif

View File

@@ -78,8 +78,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
},
define: {
'import.meta.vitest': 'undefined',
'injected.includeLargeFeatures': 'true',
'injected.version': `'0.0.0'`,
},
resolve: {
extensions: [],
@@ -96,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,21 +98,11 @@ export const openURLAndVerifyRendering = (
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
cy.get('svg').should('not.have.attr', 'viewbox');
// Handle sandbox mode where SVG is inside an iframe
if (options.securityLevel === 'sandbox') {
cy.get('iframe').should('be.visible');
if (validation) {
cy.get('iframe').should(validation);
}
} else {
cy.get('svg').should('be.visible');
// cspell:ignore viewbox
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) {
cy.get('svg').should(validation);
}
if (validation) {
cy.get('svg').should(validation);
}
if (screenshot) {

View File

@@ -114,28 +114,4 @@ describe('C4 diagram', () => {
{}
);
});
it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
imgSnapshotTest(
`
C4Context
title System Context diagram with ComponentQueue_Ext
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, msgQueue, "Sends messages to")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
`,
{}
);
});
});

View File

@@ -445,7 +445,7 @@ ORDER ||--|{ LINE-ITEM : contains
{ logLevel: 1 }
);
});
it('should render ER diagram with standalone numeric entities', () => {
it('should render ER diagram with numeric entity names and attributes', () => {
imgSnapshotTest(
`erDiagram
PRODUCT ||--o{ ORDER-ITEM : has

View File

@@ -79,18 +79,6 @@ describe('Flowchart v2', () => {
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('6a: should render complex HTML in labels with sandbox security', () => {
imgSnapshotTest(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
);
});
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD

View File

@@ -2,227 +2,227 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 6099
"duration": 5841
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2236
"duration": 2138
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3405
"duration": 3370
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 2176
"duration": 2052
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 12300
"duration": 12243
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 2089
"duration": 2065
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 32033
"duration": 31288
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3672
"duration": 3421
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 103
"duration": 97
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 18135
"duration": 18500
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 5661
"duration": 5793
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 41456
"duration": 40966
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 38910
"duration": 39176
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 24120
"duration": 23468
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 38454
"duration": 38291
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 17099
"duration": 16949
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9844
"duration": 9480
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2951
"duration": 2753
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 90081
"duration": 88028
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 19496
"duration": 15615
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3829
"duration": 3706
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 42517
"duration": 43905
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 31541
"duration": 31217
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7749
"duration": 7531
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 25230
"duration": 25423
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 49359
"duration": 49664
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 33028
"duration": 32525
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 22271
"duration": 20915
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 51837
"duration": 53556
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 285060
"duration": 283038
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 59517
"duration": 59434
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3501
"duration": 3101
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7405
"duration": 7099
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7975
"duration": 7567
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 4312
"duration": 3817
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2630
"duration": 2624
},
{
"spec": "cypress/integration/rendering/mindmap-tidy-tree.spec.js",
"duration": 4541
"duration": 4246
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 12134
"duration": 11967
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 151160
"duration": 151914
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 118044
"duration": 116698
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 5166
"duration": 4967
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 7074
"duration": 6700
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 9518
"duration": 8963
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5846
"duration": 5540
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 3089
"duration": 2782
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 55361
"duration": 54797
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 7236
"duration": 6914
},
{
"spec": "cypress/integration/rendering/sequencediagram-v2.spec.js",
"duration": 26057
"duration": 20481
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 48401
"duration": 38490
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 30364
"duration": 30766
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16862
"duration": 16705
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30553
"duration": 30928
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8962
"duration": 8424
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 12486
"duration": 12533
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 21718
"duration": 21197
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3882
"duration": 3455
}
]
}

View File

@@ -57,8 +57,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅
- [GNU Octave](https://octave.org/) ✅
- [octave_mermaid_js](https://github.com/CNOCTAVE/octave_mermaid_js) ✅
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [MonsterWriter](https://www.monsterwriter.com/) ✅
- [Joplin](https://joplinapp.org) ✅
@@ -274,7 +272,6 @@ Communication tools and platforms
- [reveal.js-mermaid-plugin](https://github.com/ludwick/reveal.js-mermaid-plugin)
- [Reveal CK](https://github.com/jedcn/reveal-ck)
- [reveal-ck-mermaid-plugin](https://github.com/tmtm/reveal-ck-mermaid-plugin)
- [speccharts: Turn your test suites into specification diagrams](https://github.com/arnaudrenaud/speccharts)
- [Vitepress Plugin](https://github.com/sametcn99/vitepress-mermaid-renderer)
<!--- cspell:ignore Blazorade HueHive --->

View File

@@ -63,21 +63,21 @@
]
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.56.3",
"@argos-ci/cypress": "^6.2.1",
"@applitools/eyes-cypress": "^3.55.2",
"@argos-ci/cypress": "^6.1.3",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.7",
"@cspell/eslint-plugin": "^9.3.0",
"@cypress/code-coverage": "^3.14.7",
"@cspell/eslint-plugin": "^8.19.4",
"@cypress/code-coverage": "^3.14.6",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.4",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.5",
"@types/express": "^5.0.3",
"@types/js-yaml": "^4.0.9",
"@types/jsdom": "^21.1.7",
"@types/lodash": "^4.17.20",
"@types/mdast": "^4.0.4",
"@types/node": "^22.19.0",
"@types/node": "^22.18.6",
"@types/rollup-plugin-visualizer": "^5.0.3",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/spy": "^3.2.4",
@@ -88,23 +88,23 @@
"cors": "^2.8.5",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cspell": "^9.3.0",
"cspell": "^9.2.1",
"cypress": "^14.5.4",
"cypress-image-snapshot": "^4.0.1",
"cypress-split": "^1.24.25",
"esbuild": "^0.25.12",
"cypress-split": "^1.24.23",
"esbuild": "^0.25.10",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^5.2.0",
"eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-html": "^8.1.3",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsdoc": "^61.1.12",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-jsdoc": "^50.8.0",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^62.0.0",
"eslint-plugin-unicorn": "^59.0.1",
"express": "^5.1.0",
"globals": "^16.4.0",
"globby": "^14.1.0",
@@ -121,13 +121,13 @@
"prettier": "^3.6.2",
"prettier-plugin-jsdoc": "^1.3.3",
"rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.5",
"rollup-plugin-visualizer": "^6.0.3",
"start-server-and-test": "^2.1.2",
"tslib": "^2.8.1",
"tsx": "^4.20.6",
"tsx": "^4.20.5",
"typescript": "~5.7.3",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.8",
"vite": "^7.0.7",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.2.4"
},

View File

@@ -18,9 +18,7 @@
"elk",
"mermaid"
],
"scripts": {
"clean": "rimraf dist"
},
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"

View File

@@ -19,9 +19,7 @@
"mermaid",
"layout"
],
"scripts": {
"clean": "rimraf dist"
},
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"

View File

@@ -33,7 +33,7 @@
],
"license": "MIT",
"dependencies": {
"@zenuml/core": "^3.41.6"
"@zenuml/core": "^3.41.4"
},
"devDependencies": {
"mermaid": "workspace:^"

View File

@@ -77,9 +77,9 @@
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.13",
"dayjs": "^1.11.19",
"dompurify": "^3.3.0",
"katex": "^0.16.25",
"dayjs": "^1.11.18",
"dompurify": "^3.2.5",
"katex": "^0.16.22",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
"marked": "^16.3.0",
@@ -89,10 +89,10 @@
"uuid": "^11.1.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.7",
"@adobe/jsonschema2md": "^8.0.5",
"@iconify/types": "^2.0.0",
"@types/cytoscape": "^3.21.9",
"@types/cytoscape-fcose": "^2.2.5",
"@types/cytoscape-fcose": "^2.2.4",
"@types/d3-sankey": "^0.12.4",
"@types/d3-scale": "^4.0.9",
"@types/d3-scale-chromatic": "^3.1.0",
@@ -101,7 +101,7 @@
"@types/jsdom": "^21.1.7",
"@types/katex": "^0.16.7",
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.10",
"@types/micromatch": "^4.0.9",
"@types/stylis": "^4.2.7",
"@types/uuid": "^10.0.0",
"ajv": "^8.17.1",
@@ -123,7 +123,7 @@
"rimraf": "^6.0.1",
"start-server-and-test": "^2.1.2",
"type-fest": "^4.41.0",
"typedoc": "^0.28.14",
"typedoc": "^0.28.13",
"typedoc-plugin-markdown": "^4.8.1",
"typescript": "~5.7.3",
"unist-util-flatmap": "^1.0.0",

View File

@@ -72,7 +72,7 @@ export const addDiagrams = () => {
}
);
if (injected.includeLargeFeatures) {
if (includeLargeFeatures) {
registerLazyLoadedDiagrams(flowchartElk, mindmap, architecture);
}

View File

@@ -1,58 +0,0 @@
import c4Db from '../c4Db.js';
import c4 from './c4Diagram.jison';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe.each([
['Component', 'component'],
['ComponentDb', 'component_db'],
['ComponentQueue', 'component_queue'],
['Component_Ext', 'external_component'],
['ComponentDb_Ext', 'external_component_db'],
['ComponentQueue_Ext', 'external_component_queue'],
])('parsing a C4 %s', function (macroName, elementName) {
beforeEach(function () {
c4.parser.yy = c4Db;
c4.parser.yy.clear();
});
it('should parse a C4 diagram with one Component correctly', function () {
c4.parser.parse(`C4Component
title Component diagram for Internet Banking Component
${macroName}(ComponentAA, "Internet Banking Component", "Technology", "Allows customers to view information about their bank accounts, and make payments.")`);
const yy = c4.parser.yy;
const shapes = yy.getC4ShapeArray();
expect(shapes.length).toBe(1);
const onlyShape = shapes[0];
expect(onlyShape).toMatchObject({
alias: 'ComponentAA',
descr: {
text: 'Allows customers to view information about their bank accounts, and make payments.',
},
label: {
text: 'Internet Banking Component',
},
techn: {
text: 'Technology',
},
typeC4Shape: {
text: elementName,
},
});
});
it('should handle a trailing whitespaces after Component', function () {
const whitespace = ' ';
const rendered = c4.parser.parse(`C4Component${whitespace}
title Component diagram for Internet Banking Component${whitespace}
${macroName}(ComponentAA, "Internet Banking Component", "Technology", "Allows customers to view information about their bank accounts, and make payments.")${whitespace}`);
expect(rendered).toBe(true);
});
});

View File

@@ -158,10 +158,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
"UpdateRelStyle" { this.begin("update_rel_style"); return 'UPDATE_REL_STYLE';}
"UpdateLayoutConfig" { this.begin("update_layout_config"); return 'UPDATE_LAYOUT_CONFIG';}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config><<EOF>> return "EOF_IN_STRUCT";
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(][ ]*[,] { this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(] { this.begin("attribute"); }
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config,attribute>[)] { this.popState();this.popState();}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config><<EOF>> return "EOF_IN_STRUCT";
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(][ ]*[,] { this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(] { this.begin("attribute"); }
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config,attribute>[)] { this.popState();this.popState();}
<attribute>",," { return 'ATTRIBUTE_EMPTY';}
<attribute>"," { }

View File

@@ -1,4 +1,4 @@
import { select } from 'd3';
import { select, type Selection } from 'd3';
import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import common from '../common/common.js';
@@ -12,7 +12,6 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../common/commonDb.js';
import { createTooltip } from '../common/svgDrawCommon.js';
import { ClassMember } from './classTypes.js';
import type {
ClassRelation,
@@ -27,7 +26,6 @@ import type {
} from './classTypes.js';
import type { Node, Edge } from '../../rendering-util/types.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import DOMPurify from 'dompurify';
const MERMAID_DOM_ID_PREFIX = 'classId-';
let classCounter = 0;
@@ -485,45 +483,43 @@ export class ClassDB implements DiagramDB {
LOLLIPOP: 4,
};
// Utility function to escape HTML meta-characters
private escapeHtml(str: string): string {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
private readonly setupToolTips = (element: Element) => {
const tooltipElem = createTooltip();
let tooltipElem: Selection<HTMLDivElement, unknown, HTMLElement, unknown> =
select('.mermaidTooltip');
// @ts-expect-error - Incorrect types
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0);
}
const svg = select(element).select('svg');
const nodes = svg.selectAll('g').filter(function () {
return select(this).attr('title') !== null;
});
const nodes = svg.selectAll('g.node');
nodes
.on('mouseover', (event: MouseEvent) => {
const el = select(event.currentTarget as HTMLElement);
const title = el.attr('title');
if (!title) {
// Don't try to draw a tooltip if no data is provided
if (title === null) {
return;
}
// @ts-ignore - getBoundingClientRect is not part of the d3 type definition
const rect = this.getBoundingClientRect();
const rect = (event.currentTarget as Element).getBoundingClientRect();
tooltipElem.transition().duration(200).style('opacity', '.9');
tooltipElem
.html(DOMPurify.sanitize(title))
.style('left', `${window.scrollX + rect.left + rect.width / 2}px`)
.style('top', `${window.scrollY + rect.bottom + 4}px`);
.text(el.attr('title'))
.style('left', window.scrollX + rect.left + (rect.right - rect.left) / 2 + 'px')
.style('top', window.scrollY + rect.top - 14 + document.body.scrollTop + 'px');
tooltipElem.html(tooltipElem.html().replace(/&lt;br\/&gt;/g, '<br/>'));
el.classed('hover', true);
})
.on('mouseout', (event: MouseEvent) => {
tooltipElem.transition().duration(500).style('opacity', 0);
select(event.currentTarget as HTMLElement).classed('hover', false);
const el = select(event.currentTarget as HTMLElement);
el.classed('hover', false);
});
};

View File

@@ -70,31 +70,6 @@ describe('Sanitize text', () => {
});
expect(result).not.toContain('javascript:alert(1)');
});
it('should allow HTML tags in sandbox mode', () => {
const htmlStr = '<p>This is a <strong>bold</strong> text</p>';
const result = sanitizeText(htmlStr, {
securityLevel: 'sandbox',
flowchart: { htmlLabels: true },
});
expect(result).toContain('<p>');
expect(result).toContain('<strong>');
expect(result).toContain('</strong>');
expect(result).toContain('</p>');
});
it('should remove script tags in sandbox mode', () => {
const maliciousStr = '<p>Hello <script>alert(1)</script> world</p>';
const result = sanitizeText(maliciousStr, {
securityLevel: 'sandbox',
flowchart: { htmlLabels: true },
});
expect(result).not.toContain('<script>');
expect(result).not.toContain('alert(1)');
expect(result).toContain('<p>');
expect(result).toContain('Hello');
expect(result).toContain('world');
});
});
describe('generic parser', () => {

View File

@@ -66,7 +66,7 @@ export const removeScript = (txt: string): string => {
const sanitizeMore = (text: string, config: MermaidConfig) => {
if (config.flowchart?.htmlLabels !== false) {
const level = config.securityLevel;
if (level === 'antiscript' || level === 'strict' || level === 'sandbox') {
if (level === 'antiscript' || level === 'strict') {
text = removeScript(text);
} else if (level !== 'loose') {
text = breakToPlaceholder(text);
@@ -333,7 +333,7 @@ const renderKatexUnsanitized = async (text: string, config: MermaidConfig): Prom
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
}
if (injected.includeLargeFeatures) {
if (includeLargeFeatures) {
const { default: katex } = await import('katex');
const outputMode =
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)

View File

@@ -1,5 +1,4 @@
import { sanitizeUrl } from '@braintree/sanitize-url';
import { select } from 'd3';
import type { SVG, SVGGroup } from '../../diagram-api/types.js';
import { lineBreakRegex } from './common.js';
import type {
@@ -136,24 +135,3 @@ export const getTextObj = (): TextObject => {
};
return testObject;
};
export const createTooltip = () => {
let tooltipElem = select<HTMLDivElement, unknown>('.mermaidTooltip');
if (tooltipElem.empty()) {
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0)
.style('position', 'absolute')
.style('text-align', 'center')
.style('max-width', '200px')
.style('padding', '2px')
.style('font-size', '12px')
.style('background', '#ffffde')
.style('border', '1px solid #333')
.style('border-radius', '2px')
.style('pointer-events', 'none')
.style('z-index', '100');
}
return tooltipElem;
};

View File

@@ -17,7 +17,6 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../common/commonDb.js';
import { createTooltip } from '../common/svgDrawCommon.js';
import type {
FlowClass,
FlowEdge,
@@ -27,7 +26,7 @@ import type {
FlowVertex,
FlowVertexTypeParam,
} from './types.js';
import DOMPurify from 'dompurify';
interface LinkData {
id: string;
}
@@ -575,7 +574,15 @@ You have to call mermaid.initialize.`
}
private setupToolTips(element: Element) {
const tooltipElem = createTooltip();
let tooltipElem = select('.mermaidTooltip');
// @ts-ignore TODO: fix this
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
// @ts-ignore TODO: fix this
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0);
}
const svg = select(element).select('svg');
@@ -596,7 +603,7 @@ You have to call mermaid.initialize.`
.text(el.attr('title'))
.style('left', window.scrollX + rect.left + (rect.right - rect.left) / 2 + 'px')
.style('top', window.scrollY + rect.bottom + 'px');
tooltipElem.html(DOMPurify.sanitize(title));
tooltipElem.html(tooltipElem.html().replace(/&lt;br\/&gt;/g, '<br/>'));
el.classed('hover', true);
})
.on('mouseout', (e: MouseEvent) => {

View File

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

View File

@@ -1,6 +1,6 @@
import type { MarkdownOptions } from 'vitepress';
import { defineConfig } from 'vitepress';
import packageJson from '../../../package.json' with { type: 'json' };
import packageJson from '../../../package.json' assert { type: 'json' };
import { addCanonicalUrls } from './canonical-urls.js';
import MermaidExample from './mermaid-markdown-all.js';

View File

@@ -52,8 +52,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅
- [GNU Octave](https://octave.org/) ✅
- [octave_mermaid_js](https://github.com/CNOCTAVE/octave_mermaid_js) ✅
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [MonsterWriter](https://www.monsterwriter.com/) ✅
- [Joplin](https://joplinapp.org) ✅
@@ -269,7 +267,6 @@ Communication tools and platforms
- [reveal.js-mermaid-plugin](https://github.com/ludwick/reveal.js-mermaid-plugin)
- [Reveal CK](https://github.com/jedcn/reveal-ck)
- [reveal-ck-mermaid-plugin](https://github.com/tmtm/reveal-ck-mermaid-plugin)
- [speccharts: Turn your test suites into specification diagrams](https://github.com/arnaudrenaud/speccharts)
- [Vitepress Plugin](https://github.com/sametcn99/vitepress-mermaid-renderer)
<!--- cspell:ignore Blazorade HueHive --->

View File

@@ -21,19 +21,19 @@
"font-awesome": "^4.7.0",
"jiti": "^2.4.2",
"mermaid": "workspace:^",
"vue": "^3.5.24"
"vue": "^3.5.21"
},
"devDependencies": {
"@iconify-json/carbon": "^1.2.14",
"@unocss/reset": "^66.5.5",
"@vite-pwa/vitepress": "^1.0.1",
"@iconify-json/carbon": "^1.2.13",
"@unocss/reset": "^66.5.1",
"@vite-pwa/vitepress": "^1.0.0",
"@vitejs/plugin-vue": "^6.0.1",
"fast-glob": "^3.3.3",
"https-localhost": "^4.7.1",
"pathe": "^2.0.3",
"unocss": "^66.5.5",
"unplugin-vue-components": "^28.8.0",
"vite": "^7.0.8",
"unocss": "^66.5.1",
"unplugin-vue-components": "^28.4.1",
"vite": "^7.0.7",
"vite-plugin-pwa": "^1.0.3",
"vitepress": "1.6.4",
"workbox-window": "^7.3.0"

View File

@@ -7,6 +7,7 @@ import { select } from 'd3';
import { compile, serialize, stringify } from 'stylis';
import DOMPurify from 'dompurify';
import isEmpty from 'lodash-es/isEmpty.js';
import packageJson from '../package.json' assert { type: 'json' };
import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js';
import assignWithDepth from './assignWithDepth.js';
import * as configApi from './config.js';
@@ -420,12 +421,12 @@ const render = async function (
// -------------------------------------------------------------------------------
// Draw the diagram with the renderer
try {
await diag.renderer.draw(text, id, injected.version, diag);
await diag.renderer.draw(text, id, packageJson.version, diag);
} catch (e) {
if (config.suppressErrorRendering) {
removeTempElements();
} else {
errorRenderer.draw(text, id, injected.version);
errorRenderer.draw(text, id, packageJson.version);
}
throw e;
}

View File

@@ -42,7 +42,7 @@ const registerDefaultLayoutLoaders = () => {
name: 'dagre',
loader: async () => await import('./layout-algorithms/dagre/index.js'),
},
...(injected.includeLargeFeatures
...(includeLargeFeatures
? [
{
name: 'cose-bilkent',

View File

@@ -1,5 +1,2 @@
// eslint-disable-next-line no-var
declare var injected: {
version: string;
includeLargeFeatures: boolean;
};
declare var includeLargeFeatures: boolean;

3417
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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