website/scripts/check-mermaid.mjs parses every mermaid block in docs/ with the Mermaid the site draws with, under jsdom (pinned, 26.1.0 for Node 20), and names the page, line and error. npm run build runs it first, so make site catches a diagram the site would draw as an error.
- commit
- bf09e5926e048b223d3ecd9f6a239fc7facc8ca5
- parent
- 94c36efa970d2f83db47d325cf39776c23a2e5b9
- author
- Dami <dami.etoile@gmail.com>
- kind
- build risk score 1.5 of 3, Jev's estimate
| M | docs/ | 9 | +- |
| M | website/ | 311 | ++++++++++++++++++++++++++++++ |
| M | website/ | 4 | +- |
| A | website/ | 79 | ++++++++ |
@@ -44,7 +44,8 @@ The target keeps its policies (public, no expiry) between pushes.
4444 ## The build is a docs check4545 4646 `make site` fails on a link to a page that doesn't exist, an anchor no47-heading makes, a missing image or two pages at one address. It checks47+heading makes, a missing image, two pages at one address or a48+[diagram](#diagrams) Mermaid can't parse. It checks4849 the same links `scripts/doccheck` does, as the site resolves them, so a4950 page that passes `make lint` but breaks on the site shows up here.5051 @@ -123,6 +124,12 @@ picture ([Pictures of 012](#pictures-of-012)).
123124 (Docusaurus's component, swizzled) draws with Mermaid's `base` theme124125 and variables read from the palette in `custom.css` for the color125126 mode shown, so diagrams don't set their own styles.127+- `make site` fails on a diagram Mermaid can't parse, which the site128+ would draw as an error: `website/scripts/check-mermaid.mjs` (`npm run129+ diagrams` in `website/`) parses every `mermaid` block in `docs/` with130+ the Mermaid the site draws with, and names the page, the line and131+ Mermaid's error. A `;` in a sequence diagram's message ends the132+ statement, so write a comma, or `#59;` where the semicolon matters.126133 127134 ## Pictures of 012128135 @@ -23,6 +23,7 @@
2323 "@docusaurus/module-type-aliases": "3.10.2",2424 "@docusaurus/tsconfig": "3.10.2",2525 "@docusaurus/types": "3.10.2",26+ "jsdom": "26.1.0",2627 "typescript": "5.9.3"2728 },2829 "engines": {@@ -290,6 +291,27 @@
290291 "url": "https://github.com/sponsors/antfu"291292 }292293 },294+ "node_modules/@asamuzakjp/css-color": {295+ "version": "3.2.0",296+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",297+ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",298+ "dev": true,299+ "license": "MIT",300+ "dependencies": {301+ "@csstools/css-calc": "^2.1.3",302+ "@csstools/css-color-parser": "^3.0.9",303+ "@csstools/css-parser-algorithms": "^3.0.4",304+ "@csstools/css-tokenizer": "^3.0.3",305+ "lru-cache": "^10.4.3"306+ }307+ },308+ "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {309+ "version": "10.4.3",310+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",311+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",312+ "dev": true,313+ "license": "ISC"314+ },293315 "node_modules/@babel/code-frame": {294316 "version": "7.29.7",295317 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",@@ -6759,6 +6781,16 @@
67596781 "node": ">= 16.0.0"67606782 }67616783 },6784+ "node_modules/agent-base": {6785+ "version": "7.1.4",6786+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",6787+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",6788+ "dev": true,6789+ "license": "MIT",6790+ "engines": {6791+ "node": ">= 14"6792+ }6793+ },67626794 "node_modules/aggregate-error": {67636795 "version": "3.1.0",67646796 "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",@@ -8597,6 +8629,20 @@
85978629 "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",85988630 "license": "CC0-1.0"85998631 },8632+ "node_modules/cssstyle": {8633+ "version": "4.6.0",8634+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",8635+ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",8636+ "dev": true,8637+ "license": "MIT",8638+ "dependencies": {8639+ "@asamuzakjp/css-color": "^3.2.0",8640+ "rrweb-cssom": "^0.8.0"8641+ },8642+ "engines": {8643+ "node": ">=18"8644+ }8645+ },86008646 "node_modules/csstype": {86018647 "version": "3.2.3",86028648 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",@@ -9123,6 +9169,20 @@
91239169 "lodash-es": "^4.17.21"91249170 }91259171 },9172+ "node_modules/data-urls": {9173+ "version": "5.0.0",9174+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",9175+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",9176+ "dev": true,9177+ "license": "MIT",9178+ "dependencies": {9179+ "whatwg-mimetype": "^4.0.0",9180+ "whatwg-url": "^14.0.0"9181+ },9182+ "engines": {9183+ "node": ">=18"9184+ }9185+ },91269186 "node_modules/dayjs": {91279187 "version": "1.11.23",91289188 "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz",@@ -9152,6 +9212,13 @@
91529212 }91539213 }91549214 },9215+ "node_modules/decimal.js": {9216+ "version": "10.6.0",9217+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",9218+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",9219+ "dev": true,9220+ "license": "MIT"9221+ },91559222 "node_modules/decode-named-character-reference": {91569223 "version": "1.3.0",91579224 "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",@@ -10908,6 +10975,19 @@
1090810975 "safe-buffer": "~5.1.0"1090910976 }1091010977 },10978+ "node_modules/html-encoding-sniffer": {10979+ "version": "4.0.0",10980+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",10981+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",10982+ "dev": true,10983+ "license": "MIT",10984+ "dependencies": {10985+ "whatwg-encoding": "^3.1.1"10986+ },10987+ "engines": {10988+ "node": ">=18"10989+ }10990+ },1091110991 "node_modules/html-escaper": {1091210992 "version": "2.0.2",1091310993 "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",@@ -11099,6 +11179,20 @@
1109911179 "node": ">=8.0.0"1110011180 }1110111181 },11182+ "node_modules/http-proxy-agent": {11183+ "version": "7.0.2",11184+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",11185+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",11186+ "dev": true,11187+ "license": "MIT",11188+ "dependencies": {11189+ "agent-base": "^7.1.0",11190+ "debug": "^4.3.4"11191+ },11192+ "engines": {11193+ "node": ">= 14"11194+ }11195+ },1110211196 "node_modules/http-proxy-middleware": {1110311197 "version": "2.0.10",1110411198 "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz",@@ -11148,6 +11242,20 @@
1114811242 "node": ">=10.19.0"1114911243 }1115011244 },11245+ "node_modules/https-proxy-agent": {11246+ "version": "7.0.6",11247+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",11248+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",11249+ "dev": true,11250+ "license": "MIT",11251+ "dependencies": {11252+ "agent-base": "^7.1.2",11253+ "debug": "4"11254+ },11255+ "engines": {11256+ "node": ">= 14"11257+ }11258+ },1115111259 "node_modules/human-signals": {1115211260 "version": "2.1.0",1115311261 "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",@@ -11594,6 +11702,13 @@
1159411702 "node": ">=0.10.0"1159511703 }1159611704 },11705+ "node_modules/is-potential-custom-element-name": {11706+ "version": "1.0.1",11707+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",11708+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",11709+ "dev": true,11710+ "license": "MIT"11711+ },1159711712 "node_modules/is-regexp": {1159811713 "version": "1.0.0",1159911714 "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",@@ -11760,6 +11875,68 @@
1176011875 "js-yaml": "bin/js-yaml.js"1176111876 }1176211877 },11878+ "node_modules/jsdom": {11879+ "version": "26.1.0",11880+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",11881+ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",11882+ "dev": true,11883+ "license": "MIT",11884+ "dependencies": {11885+ "cssstyle": "^4.2.1",11886+ "data-urls": "^5.0.0",11887+ "decimal.js": "^10.5.0",11888+ "html-encoding-sniffer": "^4.0.0",11889+ "http-proxy-agent": "^7.0.2",11890+ "https-proxy-agent": "^7.0.6",11891+ "is-potential-custom-element-name": "^1.0.1",11892+ "nwsapi": "^2.2.16",11893+ "parse5": "^7.2.1",11894+ "rrweb-cssom": "^0.8.0",11895+ "saxes": "^6.0.0",11896+ "symbol-tree": "^3.2.4",11897+ "tough-cookie": "^5.1.1",11898+ "w3c-xmlserializer": "^5.0.0",11899+ "webidl-conversions": "^7.0.0",11900+ "whatwg-encoding": "^3.1.1",11901+ "whatwg-mimetype": "^4.0.0",11902+ "whatwg-url": "^14.1.1",11903+ "ws": "^8.18.0",11904+ "xml-name-validator": "^5.0.0"11905+ },11906+ "engines": {11907+ "node": ">=18"11908+ },11909+ "peerDependencies": {11910+ "canvas": "^3.0.0"11911+ },11912+ "peerDependenciesMeta": {11913+ "canvas": {11914+ "optional": true11915+ }11916+ }11917+ },11918+ "node_modules/jsdom/node_modules/ws": {11919+ "version": "8.22.0",11920+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",11921+ "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==",11922+ "dev": true,11923+ "license": "MIT",11924+ "engines": {11925+ "node": ">=10.0.0"11926+ },11927+ "peerDependencies": {11928+ "bufferutil": "^4.0.1",11929+ "utf-8-validate": ">=5.0.2"11930+ },11931+ "peerDependenciesMeta": {11932+ "bufferutil": {11933+ "optional": true11934+ },11935+ "utf-8-validate": {11936+ "optional": true11937+ }11938+ }11939+ },1176311940 "node_modules/jsesc": {1176411941 "version": "3.1.0",1176511942 "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",@@ -14900,6 +15077,13 @@
1490015077 "url": "https://opencollective.com/webpack"1490115078 }1490215079 },15080+ "node_modules/nwsapi": {15081+ "version": "2.2.28",15082+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.28.tgz",15083+ "integrity": "sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==",15084+ "dev": true,15085+ "license": "MIT"15086+ },1490315087 "node_modules/object-assign": {1490415088 "version": "4.1.1",1490515089 "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",@@ -17856,6 +18040,13 @@
1785618040 "points-on-path": "^0.2.1"1785718041 }1785818042 },18043+ "node_modules/rrweb-cssom": {18044+ "version": "0.8.0",18045+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",18046+ "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",18047+ "dev": true,18048+ "license": "MIT"18049+ },1785918050 "node_modules/rtlcss": {1786018051 "version": "4.3.0",1786118052 "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz",@@ -17950,6 +18141,19 @@
1795018141 "node": ">=11.0.0"1795118142 }1795218143 },18144+ "node_modules/saxes": {18145+ "version": "6.0.0",18146+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",18147+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",18148+ "dev": true,18149+ "license": "ISC",18150+ "dependencies": {18151+ "xmlchars": "^2.2.0"18152+ },18153+ "engines": {18154+ "node": ">=v12.22.7"18155+ }18156+ },1795318157 "node_modules/scheduler": {1795418158 "version": "0.28.0",1795518159 "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz",@@ -18814,6 +19018,13 @@
1881419018 "node": ">= 10"1881519019 }1881619020 },19021+ "node_modules/symbol-tree": {19022+ "version": "3.2.4",19023+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",19024+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",19025+ "dev": true,19026+ "license": "MIT"19027+ },1881719028 "node_modules/tapable": {1881819029 "version": "2.3.3",1881919030 "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",@@ -18992,6 +19203,26 @@
1899219203 "node": "^18.0.0 || >=20.0.0"1899319204 }1899419205 },19206+ "node_modules/tldts": {19207+ "version": "6.1.86",19208+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",19209+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",19210+ "dev": true,19211+ "license": "MIT",19212+ "dependencies": {19213+ "tldts-core": "^6.1.86"19214+ },19215+ "bin": {19216+ "tldts": "bin/cli.js"19217+ }19218+ },19219+ "node_modules/tldts-core": {19220+ "version": "6.1.86",19221+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",19222+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",19223+ "dev": true,19224+ "license": "MIT"19225+ },1899519226 "node_modules/to-regex-range": {1899619227 "version": "5.0.1",1899719228 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",@@ -19022,6 +19253,32 @@
1902219253 "node": ">=6"1902319254 }1902419255 },19256+ "node_modules/tough-cookie": {19257+ "version": "5.1.2",19258+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",19259+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",19260+ "dev": true,19261+ "license": "BSD-3-Clause",19262+ "dependencies": {19263+ "tldts": "^6.1.32"19264+ },19265+ "engines": {19266+ "node": ">=16"19267+ }19268+ },19269+ "node_modules/tr46": {19270+ "version": "5.1.1",19271+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",19272+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",19273+ "dev": true,19274+ "license": "MIT",19275+ "dependencies": {19276+ "punycode": "^2.3.1"19277+ },19278+ "engines": {19279+ "node": ">=18"19280+ }19281+ },1902519282 "node_modules/tree-dump": {1902619283 "version": "1.1.0",1902719284 "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.1.0.tgz",@@ -19664,6 +19921,19 @@
1966419921 "url": "https://opencollective.com/unified"1966519922 }1966619923 },19924+ "node_modules/w3c-xmlserializer": {19925+ "version": "5.0.0",19926+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",19927+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",19928+ "dev": true,19929+ "license": "MIT",19930+ "dependencies": {19931+ "xml-name-validator": "^5.0.0"19932+ },19933+ "engines": {19934+ "node": ">=18"19935+ }19936+ },1966719937 "node_modules/watchpack": {1966819938 "version": "2.5.2",1966919939 "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz",@@ -19695,6 +19965,16 @@
1969519965 "url": "https://github.com/sponsors/wooorm"1969619966 }1969719967 },19968+ "node_modules/webidl-conversions": {19969+ "version": "7.0.0",19970+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",19971+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",19972+ "dev": true,19973+ "license": "BSD-2-Clause",19974+ "engines": {19975+ "node": ">=12"19976+ }19977+ },1969819978 "node_modules/webpack": {1969919979 "version": "5.111.1",1970019980 "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.111.1.tgz",@@ -20061,6 +20341,20 @@
2006120341 "node": ">=18"2006220342 }2006320343 },20344+ "node_modules/whatwg-url": {20345+ "version": "14.2.0",20346+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",20347+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",20348+ "dev": true,20349+ "license": "MIT",20350+ "dependencies": {20351+ "tr46": "^5.1.0",20352+ "webidl-conversions": "^7.0.0"20353+ },20354+ "engines": {20355+ "node": ">=18"20356+ }20357+ },2006420358 "node_modules/which": {2006520359 "version": "2.0.2",2006620360 "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",@@ -20240,6 +20534,23 @@
2024020534 "xml-js": "bin/cli.js"2024120535 }2024220536 },20537+ "node_modules/xml-name-validator": {20538+ "version": "5.0.0",20539+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",20540+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",20541+ "dev": true,20542+ "license": "Apache-2.0",20543+ "engines": {20544+ "node": ">=18"20545+ }20546+ },20547+ "node_modules/xmlchars": {20548+ "version": "2.2.0",20549+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",20550+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",20551+ "dev": true,20552+ "license": "MIT"20553+ },2024320554 "node_modules/yallist": {2024420555 "version": "3.1.1",2024520556 "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",@@ -4,7 +4,8 @@
44 "description": "The 012 documentation site: Docusaurus over ../docs",55 "scripts": {66 "start": "docusaurus start",7- "build": "docusaurus build",7+ "build": "npm run diagrams && docusaurus build",8+ "diagrams": "node scripts/check-mermaid.mjs",89 "serve": "docusaurus serve",910 "clear": "docusaurus clear",1011 "typecheck": "tsc"@@ -27,6 +28,7 @@
2728 "@docusaurus/module-type-aliases": "3.10.2",2829 "@docusaurus/tsconfig": "3.10.2",2930 "@docusaurus/types": "3.10.2",31+ "jsdom": "26.1.0",3032 "typescript": "5.9.3"3133 },3234 "engines": {@@ -0,0 +1,79 @@
1+// Parses every mermaid code block in docs/ with the Mermaid the site2+// draws with, and fails on one it can't read: in the browser such a3+// block is drawn as an error, not a diagram. `npm run build` (and so4+// `make site`) runs it first.5+//6+// Mermaid's parser runs in Node, but it sanitizes labels with DOMPurify,7+// which needs a DOM: jsdom gives it one, set up before Mermaid loads.8+9+import {readFileSync, readdirSync} from 'node:fs';10+import {join, relative} from 'node:path';11+import {fileURLToPath} from 'node:url';12+import {JSDOM} from 'jsdom';13+14+const docs = fileURLToPath(new URL('../../docs', import.meta.url));15+16+const {window} = new JSDOM('<!doctype html><html><body></body></html>');17+globalThis.window = window;18+globalThis.document = window.document;19+globalThis.DOMParser = window.DOMParser;20+globalThis.Element = window.Element;21+const {default: mermaid} = await import('mermaid');22+mermaid.initialize({startOnLoad: false});23+24+function markdownFiles(dir) {25+ return readdirSync(dir, {withFileTypes: true}).flatMap((e) => {26+ const path = join(dir, e.name);27+ if (e.isDirectory()) {28+ return markdownFiles(path);29+ }30+ return /\.mdx?$/.test(e.name) ? [path] : [];31+ });32+}33+34+// The mermaid blocks of a page, as CommonMark reads its fences: a block35+// inside a longer fence (an example of Markdown) is text, not a diagram.36+function mermaidBlocks(text) {37+ const blocks = [];38+ let fence = null;39+ text.split('\n').forEach((line, i) => {40+ const m = /^(\s*)(`{3,}|~{3,})\s*([^`\s]*)/.exec(line);41+ if (fence === null) {42+ if (m) {43+ fence = {marker: m[2], indent: m[1].length, mermaid: m[3] === 'mermaid', line: i + 1, body: []};44+ }45+ return;46+ }47+ const close = /^\s*(`{3,}|~{3,})\s*$/.exec(line);48+ if (close && close[1][0] === fence.marker[0] && close[1].length >= fence.marker.length) {49+ if (fence.mermaid) {50+ blocks.push({line: fence.line, text: fence.body.join('\n')});51+ }52+ fence = null;53+ return;54+ }55+ fence.body.push(line.slice(Math.min(fence.indent, line.search(/\S|$/))));56+ });57+ return blocks;58+}59+60+let count = 0;61+const failures = [];62+for (const file of markdownFiles(docs).sort()) {63+ for (const block of mermaidBlocks(readFileSync(file, 'utf8'))) {64+ count++;65+ try {66+ await mermaid.parse(block.text);67+ } catch (err) {68+ const first = block.text.trim().split('\n')[0];69+ failures.push(`docs/${relative(docs, file)}:${block.line}: ${first}\n${String(err.message ?? err)}`);70+ }71+ }72+}73+74+if (failures.length > 0) {75+ console.error(`${failures.length} of ${count} mermaid diagrams don't parse:\n`);76+ console.error(failures.join('\n\n'));77+ process.exit(1);78+}79+console.log(`${count} mermaid diagrams parse`);