From a13baa9c90313967324b91ce349b10c20d9f7c55 Mon Sep 17 00:00:00 2001 From: OfirHaf Date: Sun, 10 May 2026 07:28:09 +0300 Subject: [PATCH 1/7] fix: read block padding and sanitize config at call time instead of module load block/layout.ts captured padding via getConfig() at import time, so changes to block.padding config had no effect during rendering. Move the read into layout() and thread it into setBlockSizes/layoutBlocks as a parameter. blockDB.ts and quadrantDb.ts had the same problem with sanitization config: both captured config at module scope, so dompurifyConfig updates applied after import were silently ignored. Fixes #7621, #7622, #7623 --- packages/mermaid/src/diagrams/block/blockDB.ts | 4 +--- packages/mermaid/src/diagrams/block/layout.ts | 17 ++++++++--------- .../src/diagrams/quadrant-chart/quadrantDb.ts | 4 +--- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/mermaid/src/diagrams/block/blockDB.ts b/packages/mermaid/src/diagrams/block/blockDB.ts index 0fae938ce52..3ab269177e5 100644 --- a/packages/mermaid/src/diagrams/block/blockDB.ts +++ b/packages/mermaid/src/diagrams/block/blockDB.ts @@ -16,12 +16,10 @@ const COLOR_KEYWORD = 'color'; const FILL_KEYWORD = 'fill'; const BG_FILL = 'bgFill'; const STYLECLASS_SEP = ','; -const config = getConfig(); - let classes = new Map(); let diagramId = ''; -const sanitizeText = (txt: string) => common.sanitizeText(txt, config); +const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig()); /** * Called when the parser comes across a (style) class definition diff --git a/packages/mermaid/src/diagrams/block/layout.ts b/packages/mermaid/src/diagrams/block/layout.ts index 14ef1200446..e9197508725 100644 --- a/packages/mermaid/src/diagrams/block/layout.ts +++ b/packages/mermaid/src/diagrams/block/layout.ts @@ -2,8 +2,6 @@ import type { BlockDB } from './blockDB.js'; import type { Block } from './blockTypes.js'; import { log } from '../../logger.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; -// TODO: This means the number we provide in diagram's config will never be used. Should fix. -const padding = getConfig()?.block?.padding ?? 8; interface BlockPosition { px: number; @@ -71,7 +69,7 @@ const getMaxChildSize = (block: Block) => { return { width: maxWidth, height: maxHeight }; }; -function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeight = 0) { +function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeight = 0, padding = 8) { log.debug( 'setBlockSizes abc95 (start)', block.id, @@ -94,7 +92,7 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh if (block.children?.length > 0) { for (const child of block.children) { - setBlockSizes(child, db); + setBlockSizes(child, db, 0, 0, padding); } // find max width of children const childSize = getMaxChildSize(block); @@ -120,7 +118,7 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh } } for (const child of block.children) { - setBlockSizes(child, db, maxWidth, maxHeight); + setBlockSizes(child, db, maxWidth, maxHeight, padding); } const columns = block.columns ?? -1; @@ -202,7 +200,7 @@ function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeigh ); } -function layoutBlocks(block: Block, db: BlockDB) { +function layoutBlocks(block: Block, db: BlockDB, padding = 8) { log.debug( `abc85 layout blocks (=>layoutBlocks) ${block.id} x: ${block?.size?.x} y: ${block?.size?.y} width: ${block?.size?.width}` ); @@ -301,7 +299,7 @@ function layoutBlocks(block: Block, db: BlockDB) { ); } if (child.children) { - layoutBlocks(child, db); + layoutBlocks(child, db, padding); } let columnsFilled = child?.widthInColumns ?? 1; if (columns > 0) { @@ -350,8 +348,9 @@ export function layout(db: BlockDB) { return; } - setBlockSizes(root, db, 0, 0); - layoutBlocks(root, db); + const padding = getConfig()?.block?.padding ?? 8; + setBlockSizes(root, db, 0, 0, padding); + layoutBlocks(root, db, padding); // Position blocks relative to parents // positionBlock(root, root, db); log.debug('getBlocks', JSON.stringify(root, null, 2)); diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts index 9e16defa1eb..f2a134661d1 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.ts @@ -18,10 +18,8 @@ import { InvalidStyleError, } from './utils.js'; -const config = getConfig(); - function textSanitizer(text: string) { - return sanitizeText(text.trim(), config); + return sanitizeText(text.trim(), getConfig()); } interface LexTextObj { From 97b3806ba1bda99a2e04ae44bad4acf3e8812093 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 04:34:49 +0000 Subject: [PATCH 2/7] [autofix.ci] apply automated fixes --- packages/mermaid/src/diagrams/block/layout.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/block/layout.ts b/packages/mermaid/src/diagrams/block/layout.ts index e9197508725..81f739fc952 100644 --- a/packages/mermaid/src/diagrams/block/layout.ts +++ b/packages/mermaid/src/diagrams/block/layout.ts @@ -69,7 +69,13 @@ const getMaxChildSize = (block: Block) => { return { width: maxWidth, height: maxHeight }; }; -function setBlockSizes(block: Block, db: BlockDB, siblingWidth = 0, siblingHeight = 0, padding = 8) { +function setBlockSizes( + block: Block, + db: BlockDB, + siblingWidth = 0, + siblingHeight = 0, + padding = 8 +) { log.debug( 'setBlockSizes abc95 (start)', block.id, From a4c1e507a347256f1f3a42be3feb5b6ddc7257f2 Mon Sep 17 00:00:00 2001 From: OfirHaf Date: Sun, 10 May 2026 08:11:13 +0300 Subject: [PATCH 3/7] chore: add changeset for runtime config fix --- .changeset/fix-block-runtime-config.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fix-block-runtime-config.md diff --git a/.changeset/fix-block-runtime-config.md b/.changeset/fix-block-runtime-config.md new file mode 100644 index 00000000000..711a04ceab8 --- /dev/null +++ b/.changeset/fix-block-runtime-config.md @@ -0,0 +1,5 @@ +--- +"mermaid": patch +--- + +fix: read block padding and sanitize config dynamically instead of at module load time From 67abfc3a4b3533c49dec32528fe868facb382458 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 05:17:22 +0000 Subject: [PATCH 4/7] [autofix.ci] apply automated fixes --- .changeset/fix-block-runtime-config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fix-block-runtime-config.md b/.changeset/fix-block-runtime-config.md index 711a04ceab8..c5f4c75029f 100644 --- a/.changeset/fix-block-runtime-config.md +++ b/.changeset/fix-block-runtime-config.md @@ -1,5 +1,5 @@ --- -"mermaid": patch +'mermaid': patch --- fix: read block padding and sanitize config dynamically instead of at module load time From ce49f76ea100cb68c5d75f3ebff31521fdf8b9a3 Mon Sep 17 00:00:00 2001 From: OfirHaf Date: Mon, 11 May 2026 13:31:27 +0300 Subject: [PATCH 5/7] test: add regression tests for call-time config reads in block and quadrant diagrams Verify that getConfig() is invoked during function execution (not cached at module load time) for blockDB sanitizeText, quadrantDb textSanitizer, and layout() block.padding reads. --- .../src/diagrams/block/blockDB.spec.ts | 19 +++++++++ .../mermaid/src/diagrams/block/layout.spec.ts | 39 ++++++++++++++++++- .../quadrant-chart/quadrantDb.spec.ts | 19 +++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/block/blockDB.spec.ts b/packages/mermaid/src/diagrams/block/blockDB.spec.ts index c3924034820..c15c40133c2 100644 --- a/packages/mermaid/src/diagrams/block/blockDB.spec.ts +++ b/packages/mermaid/src/diagrams/block/blockDB.spec.ts @@ -1,3 +1,5 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import * as diagramAPI from '../../diagram-api/diagramAPI.js'; import db from './blockDB.js'; describe('block db edge styles', () => { @@ -27,3 +29,20 @@ describe('block db edge styles', () => { expect(db.edgeStrToEdgeData('--o')).toBe('arrow_circle'); }); }); + +describe('block db runtime config', () => { + afterEach(() => { + vi.restoreAllMocks(); + db.clear(); + }); + + it('should call getConfig at sanitization time, not at module load time', () => { + const spy = vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({} as any); + + db.setHierarchy([{ id: 'a', type: 'rect', label: 'hello', children: [] }]); + + // getConfig must have been called during setHierarchy (call-time read), + // not only once at module import time. + expect(spy).toHaveBeenCalled(); + }); +}); diff --git a/packages/mermaid/src/diagrams/block/layout.spec.ts b/packages/mermaid/src/diagrams/block/layout.spec.ts index e704920ca19..c61ffdebf16 100644 --- a/packages/mermaid/src/diagrams/block/layout.spec.ts +++ b/packages/mermaid/src/diagrams/block/layout.spec.ts @@ -1,4 +1,8 @@ -import { calculateBlockPosition } from './layout.js'; +import { describe, it, expect, vi, afterEach } from 'vitest'; +import * as diagramAPI from '../../diagram-api/diagramAPI.js'; +import type { BlockDB } from './blockDB.js'; +import type { Block } from './blockTypes.js'; +import { calculateBlockPosition, layout } from './layout.js'; describe('Layout', function () { it('should calculate position correctly', () => { @@ -10,3 +14,36 @@ describe('Layout', function () { expect(calculateBlockPosition(1, 3)).toEqual({ px: 0, py: 3 }); }); }); + +describe('layout runtime config', () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should read block.padding from config at call time, not at import time', () => { + const makeRoot = (): Block => ({ + id: 'root', + type: 'rect', + columns: 2, + children: [ + { id: 'b1', type: 'rect', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, + { id: 'b2', type: 'rect', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, + ], + }); + + const makeDb = (root: Block): BlockDB => + ({ getBlock: (id: string) => (id === 'root' ? root : undefined) }) as unknown as BlockDB; + + vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({ block: { padding: 4 } } as any); + const result1 = layout(makeDb(makeRoot())); + + vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({ block: { padding: 20 } } as any); + const result2 = layout(makeDb(makeRoot())); + + // padding=4: width = 2*(100+4)+4 = 212 + // padding=20: width = 2*(100+20)+20 = 260 + // If padding were cached at module load time both calls would return the same value. + expect(result1).not.toEqual(result2); + expect(result1!.width).toBeLessThan(result2!.width); + }); +}); diff --git a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.spec.ts b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.spec.ts index 2a604304ad5..e1022d59da2 100644 --- a/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.spec.ts +++ b/packages/mermaid/src/diagrams/quadrant-chart/quadrantDb.spec.ts @@ -1,3 +1,5 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import * as diagramAPI from '../../diagram-api/diagramAPI.js'; import quadrantDb from './quadrantDb.js'; describe('quadrant unit tests', () => { @@ -48,3 +50,20 @@ describe('quadrant unit tests', () => { ); }); }); + +describe('quadrant db runtime config', () => { + afterEach(() => { + vi.restoreAllMocks(); + quadrantDb.clear(); + }); + + it('should call getConfig at sanitization time, not at module load time', () => { + const spy = vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({} as any); + + quadrantDb.setXAxisLeftText({ text: 'left label', type: 'text' }); + + // getConfig must have been called during setXAxisLeftText (call-time read), + // not only once at module import time. + expect(spy).toHaveBeenCalled(); + }); +}); From c05f86ac933af9d8c962b4c5e8b6bb630cec74cc Mon Sep 17 00:00:00 2001 From: OfirHaf Date: Mon, 11 May 2026 14:49:15 +0300 Subject: [PATCH 6/7] fix: use valid BlockType 'square' in regression test mocks --- packages/mermaid/src/diagrams/block/blockDB.spec.ts | 2 +- packages/mermaid/src/diagrams/block/layout.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/block/blockDB.spec.ts b/packages/mermaid/src/diagrams/block/blockDB.spec.ts index c15c40133c2..0af205917e0 100644 --- a/packages/mermaid/src/diagrams/block/blockDB.spec.ts +++ b/packages/mermaid/src/diagrams/block/blockDB.spec.ts @@ -39,7 +39,7 @@ describe('block db runtime config', () => { it('should call getConfig at sanitization time, not at module load time', () => { const spy = vi.spyOn(diagramAPI, 'getConfig').mockReturnValue({} as any); - db.setHierarchy([{ id: 'a', type: 'rect', label: 'hello', children: [] }]); + db.setHierarchy([{ id: 'a', type: 'square', label: 'hello', children: [] }]); // getConfig must have been called during setHierarchy (call-time read), // not only once at module import time. diff --git a/packages/mermaid/src/diagrams/block/layout.spec.ts b/packages/mermaid/src/diagrams/block/layout.spec.ts index c61ffdebf16..3e953243c49 100644 --- a/packages/mermaid/src/diagrams/block/layout.spec.ts +++ b/packages/mermaid/src/diagrams/block/layout.spec.ts @@ -23,7 +23,7 @@ describe('layout runtime config', () => { it('should read block.padding from config at call time, not at import time', () => { const makeRoot = (): Block => ({ id: 'root', - type: 'rect', + type: 'square', columns: 2, children: [ { id: 'b1', type: 'rect', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, From 3d0011b627cea662f4bd1726185a2f11a2537bc9 Mon Sep 17 00:00:00 2001 From: OfirHaf Date: Mon, 11 May 2026 14:53:06 +0300 Subject: [PATCH 7/7] fix: replace remaining 'rect' type literals with 'square' in layout spec --- packages/mermaid/src/diagrams/block/layout.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/block/layout.spec.ts b/packages/mermaid/src/diagrams/block/layout.spec.ts index 3e953243c49..df04d67af88 100644 --- a/packages/mermaid/src/diagrams/block/layout.spec.ts +++ b/packages/mermaid/src/diagrams/block/layout.spec.ts @@ -26,8 +26,8 @@ describe('layout runtime config', () => { type: 'square', columns: 2, children: [ - { id: 'b1', type: 'rect', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, - { id: 'b2', type: 'rect', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, + { id: 'b1', type: 'square', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, + { id: 'b2', type: 'square', children: [], size: { width: 100, height: 50, x: 0, y: 0 } }, ], });