@riebeckite/plugin-mermaid 0.0.22 → 0.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/README_ja.md +4 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +74 -19
- package/dist/src/rehype.d.ts +7 -1
- package/dist/src/render-static.d.ts +1 -0
- package/dist/src/types.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Mermaid diagram rendering for ` ```mermaid ` code blocks.
|
|
|
10
10
|
|
|
11
11
|
`mermaid()` replaces mermaid code blocks with a `<figure class="rr-mermaid">`
|
|
12
12
|
that renders to SVG. Diagrams are rendered at build time with a headless browser by
|
|
13
|
-
default
|
|
13
|
+
default. It runs with `order: -10`.
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
@@ -44,10 +44,11 @@ export default defineConfig({
|
|
|
44
44
|
Rendering uses Chromium's layout engine, not JSDOM polyfills or custom
|
|
45
45
|
`getBBox` / text-width estimation
|
|
46
46
|
- Mermaid runs with `securityLevel: "strict"`, the selected theme, transparent
|
|
47
|
-
background, and a unique SVG id per
|
|
47
|
+
background, deterministic IDs seeded by the diagram ID, and a unique SVG id per
|
|
48
|
+
diagram
|
|
48
49
|
- Invalid diagrams report `ruleId: "invalid-diagram"`; Chromium renderer
|
|
49
|
-
failures report `ruleId: "renderer-error"`.
|
|
50
|
-
|
|
50
|
+
failures report `ruleId: "renderer-error"`. Build rendering failures stop the
|
|
51
|
+
build by default, so a failed render cannot be stored as successful content
|
|
51
52
|
|
|
52
53
|
### Client (`initMermaidDiagrams`)
|
|
53
54
|
|
|
@@ -63,16 +64,21 @@ export default defineConfig({
|
|
|
63
64
|
| Option | Type | Default | Description |
|
|
64
65
|
| ------ | ---- | ------- | ----------- |
|
|
65
66
|
| `render` | `"build" \| "client"` | `"build"` | When diagrams are rendered |
|
|
67
|
+
| `onRenderError` | `"error" \| "client"` | `"error"` | Behavior when build rendering fails |
|
|
66
68
|
| `theme` | `string \| { light: string; dark: string }` | `{ light: "default", dark: "dark" }` | Mermaid theme |
|
|
67
69
|
| `caption` | `boolean` | `true` | Show title / `%% caption:` as `figcaption` |
|
|
68
70
|
| `fallback` | `boolean` | `true` | Show the diagram source in `<details>` |
|
|
69
71
|
|
|
70
72
|
`render` modes:
|
|
71
73
|
|
|
72
|
-
- `"build"` — render SVG at build time;
|
|
73
|
-
|
|
74
|
+
- `"build"` — render SVG at build time; failures stop the build unless
|
|
75
|
+
`onRenderError` is `"client"`
|
|
74
76
|
- `"client"` — skip build-time rendering, render in the browser only
|
|
75
77
|
|
|
78
|
+
Set `onRenderError: "client"` only when client-side retry is an intentional
|
|
79
|
+
availability trade-off. This makes the generated HTML depend on the renderer's
|
|
80
|
+
availability.
|
|
81
|
+
|
|
76
82
|
## Exports
|
|
77
83
|
|
|
78
84
|
- `mermaid(options?)` — plugin factory
|
package/README_ja.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Generated from docs/docs/plugins/mermaid.ja.md. Edit the canonical documentation in docs/docs/plugins and run `pnpm docs:sync`. -->
|
|
4
4
|
|
|
5
|
-
`mermaid` コードブロックを SVG
|
|
5
|
+
`mermaid` コードブロックを SVG の図として表示するプラグインです。既定ではビルド時に描画します。
|
|
6
6
|
|
|
7
7
|
[English](./README.md)
|
|
8
8
|
|
|
@@ -31,7 +31,7 @@ export default defineConfig({
|
|
|
31
31
|
|
|
32
32
|
`render: "build"` では、Puppeteer が起動するヘッドレス Chromium 上で Mermaid を実行し、静的な SVG を生成します。簡易な DOM 実装や寸法の推測には頼らないため、ブラウザと同じレイアウトエンジンで図を作れます。Mermaid は `securityLevel: "strict"` で実行されます。
|
|
33
33
|
|
|
34
|
-
構文エラーは `invalid-diagram`、描画環境の問題は `renderer-error`
|
|
34
|
+
構文エラーは `invalid-diagram`、描画環境の問題は `renderer-error` として区別して診断します。ビルド時の描画に失敗した場合、既定ではビルドを失敗させます。失敗した図を成功したコンテンツとして保存しません。SVG ID は図の ID をシードにして決定的に生成します。
|
|
35
35
|
|
|
36
36
|
## クライアント側の再試行
|
|
37
37
|
|
|
@@ -44,11 +44,12 @@ export default defineConfig({
|
|
|
44
44
|
| 項目 | 既定値 | 説明 |
|
|
45
45
|
| --- | --- | --- |
|
|
46
46
|
| `render` | `"build"` | `"build"` または `"client"` のいずれで描画するか |
|
|
47
|
+
| `onRenderError` | `"error"` | ビルド時の描画失敗時に `"error"` または `"client"` のどちらにするか |
|
|
47
48
|
| `theme` | `{ light: "default", dark: "dark" }` | Mermaid のテーマ名、または明暗別のテーマ |
|
|
48
49
|
| `caption` | `true` | タイトルまたは `%% caption:` をキャプションとして表示する |
|
|
49
50
|
| `fallback` | `true` | 元の Mermaid 記法を `<details>` に残す |
|
|
50
51
|
|
|
51
|
-
`"client"`
|
|
52
|
+
`"client"` はビルド時の描画を行いません。`render: "build"` で `onRenderError: "client"` を指定するときだけ、ビルド時の描画失敗をクライアント側へ明示的に委譲します。この設定では生成HTMLが描画環境の可用性に依存します。
|
|
52
53
|
|
|
53
54
|
## 主なエクスポート
|
|
54
55
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { MermaidOptions } from "./src/types.js";
|
|
2
|
-
export type { MermaidClientOptions, MermaidOptions, MermaidRenderMode, MermaidRenderSession, MermaidTheme, } from "./src/types.js";
|
|
2
|
+
export type { MermaidClientOptions, MermaidOptions, MermaidRenderFailureMode, MermaidRenderMode, MermaidRenderSession, MermaidTheme, } from "./src/types.js";
|
|
3
3
|
export declare function mermaid(options?: MermaidOptions): import("@riebeckite/core").RiebeckitePlugin<MermaidOptions>;
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,7 @@ var init_hast = __esm({
|
|
|
66
66
|
// src/rehype.ts
|
|
67
67
|
var rehype_exports = {};
|
|
68
68
|
__export(rehype_exports, {
|
|
69
|
+
MermaidBuildRenderError: () => MermaidBuildRenderError,
|
|
69
70
|
rehypeMermaid: () => rehypeMermaid
|
|
70
71
|
});
|
|
71
72
|
function rehypeMermaid(options = {}, session) {
|
|
@@ -81,6 +82,7 @@ function rehypeMermaid(options = {}, session) {
|
|
|
81
82
|
theme,
|
|
82
83
|
caption: options.caption !== false,
|
|
83
84
|
fallback: options.fallback !== false,
|
|
85
|
+
onRenderError: options.onRenderError ?? "error",
|
|
84
86
|
session
|
|
85
87
|
})
|
|
86
88
|
);
|
|
@@ -94,6 +96,9 @@ async function replaceMermaidBlock(parent, index, pre, file, options) {
|
|
|
94
96
|
const caption = options.caption ? extractCaption(source, pre, code) : null;
|
|
95
97
|
const id = `rr-mermaid-${hashSource(source)}`;
|
|
96
98
|
const renderResult = shouldRenderAtBuild(options.render) ? await renderStaticSvg(id, source, options.theme, file, options.session) : null;
|
|
99
|
+
if (renderResult?.ok === false && options.onRenderError === "error") {
|
|
100
|
+
throw new MermaidBuildRenderError(renderResult);
|
|
101
|
+
}
|
|
97
102
|
const staticSvg = renderResult?.ok ? renderResult.svg : null;
|
|
98
103
|
parent.children = parent.children ?? [];
|
|
99
104
|
parent.children[index] = buildFigure({
|
|
@@ -102,7 +107,7 @@ async function replaceMermaidBlock(parent, index, pre, file, options) {
|
|
|
102
107
|
caption,
|
|
103
108
|
staticSvg,
|
|
104
109
|
fallback: options.fallback,
|
|
105
|
-
clientFallback: shouldRenderAtClient(options.render) || renderResult?.ok === false
|
|
110
|
+
clientFallback: shouldRenderAtClient(options.render) || renderResult?.ok === false && options.onRenderError === "client"
|
|
106
111
|
});
|
|
107
112
|
}
|
|
108
113
|
function buildFigure(input) {
|
|
@@ -154,14 +159,11 @@ async function renderStaticSvg(id, source, theme, file, session) {
|
|
|
154
159
|
);
|
|
155
160
|
if (result.ok === false) {
|
|
156
161
|
reportMermaidDiagnostic(file, result.message, result.kind);
|
|
157
|
-
const label = result.kind === "invalid-diagram" ? "invalid diagram" : "renderer failed";
|
|
158
|
-
console.warn(`[mermaid] ${label}: ${result.message}`);
|
|
159
162
|
}
|
|
160
163
|
return result;
|
|
161
164
|
} catch (error) {
|
|
162
165
|
const message = formatError2(error);
|
|
163
166
|
reportMermaidDiagnostic(file, message, "renderer-error");
|
|
164
|
-
console.warn(`[mermaid] renderer failed: ${message}`);
|
|
165
167
|
return { ok: false, kind: "renderer-error", message };
|
|
166
168
|
}
|
|
167
169
|
}
|
|
@@ -212,12 +214,20 @@ function formatError2(error) {
|
|
|
212
214
|
if (error instanceof Error) return error.message;
|
|
213
215
|
return String(error);
|
|
214
216
|
}
|
|
215
|
-
var DEFAULT_THEME, CAPTION_PATTERN;
|
|
217
|
+
var DEFAULT_THEME, CAPTION_PATTERN, MermaidBuildRenderError;
|
|
216
218
|
var init_rehype = __esm({
|
|
217
219
|
"src/rehype.ts"() {
|
|
218
220
|
init_hast();
|
|
219
221
|
DEFAULT_THEME = { light: "default", dark: "dark" };
|
|
220
222
|
CAPTION_PATTERN = /^%%\s*caption\s*:\s*(.+)$/im;
|
|
223
|
+
MermaidBuildRenderError = class extends Error {
|
|
224
|
+
kind;
|
|
225
|
+
constructor(result) {
|
|
226
|
+
super(`Mermaid build rendering failed: ${result.message}`);
|
|
227
|
+
this.name = "MermaidBuildRenderError";
|
|
228
|
+
this.kind = result.kind;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
221
231
|
}
|
|
222
232
|
});
|
|
223
233
|
|
|
@@ -230,12 +240,15 @@ import {
|
|
|
230
240
|
|
|
231
241
|
// src/render-static.ts
|
|
232
242
|
import { access } from "node:fs/promises";
|
|
243
|
+
import { createRequire } from "node:module";
|
|
233
244
|
import { join } from "node:path";
|
|
234
|
-
import { fileURLToPath } from "node:url";
|
|
245
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
235
246
|
var RENDER_TIMEOUT_MS = 3e4;
|
|
236
247
|
var MERMAID_SCRIPT_SUBPATH = "mermaid/dist/mermaid.min.js";
|
|
248
|
+
var PUPPETEER_PACKAGE = "puppeteer";
|
|
237
249
|
function createMermaidRenderSession(dependencies = {}) {
|
|
238
250
|
const launchBrowser = dependencies.launch ?? defaultLaunchBrowser;
|
|
251
|
+
const timeoutMs = dependencies.timeoutMs ?? RENDER_TIMEOUT_MS;
|
|
239
252
|
let state = null;
|
|
240
253
|
let queue = Promise.resolve();
|
|
241
254
|
let exitListenerAttached = false;
|
|
@@ -264,7 +277,7 @@ function createMermaidRenderSession(dependencies = {}) {
|
|
|
264
277
|
attachExitListener();
|
|
265
278
|
try {
|
|
266
279
|
const page = await browser.newPage();
|
|
267
|
-
page.setDefaultTimeout(
|
|
280
|
+
page.setDefaultTimeout(timeoutMs);
|
|
268
281
|
await page.setContent("<!doctype html><html><body></body></html>");
|
|
269
282
|
await page.addScriptTag({ path: await resolveMermaidScriptPath() });
|
|
270
283
|
state = { browser, page };
|
|
@@ -288,11 +301,10 @@ function createMermaidRenderSession(dependencies = {}) {
|
|
|
288
301
|
if (current) await teardownBrowser(current.browser);
|
|
289
302
|
}
|
|
290
303
|
async function evaluateRender(session, id, source, theme) {
|
|
291
|
-
const result = await
|
|
292
|
-
renderMermaidInPage,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
theme
|
|
304
|
+
const result = await withTimeout(
|
|
305
|
+
session.page.evaluate(renderMermaidInPage, id, source, theme),
|
|
306
|
+
timeoutMs,
|
|
307
|
+
"Mermaid rendering timed out."
|
|
296
308
|
);
|
|
297
309
|
return normalizeRenderResult(result);
|
|
298
310
|
}
|
|
@@ -347,7 +359,9 @@ async function renderMermaidInPage(id, source, theme) {
|
|
|
347
359
|
mermaid2.initialize({
|
|
348
360
|
startOnLoad: false,
|
|
349
361
|
securityLevel: "strict",
|
|
350
|
-
theme
|
|
362
|
+
theme,
|
|
363
|
+
deterministicIds: true,
|
|
364
|
+
deterministicIDSeed: id
|
|
351
365
|
});
|
|
352
366
|
const { svg } = await mermaid2.render(id, source);
|
|
353
367
|
return { ok: true, svg };
|
|
@@ -371,7 +385,21 @@ async function importPuppeteer() {
|
|
|
371
385
|
"specifier",
|
|
372
386
|
"return import(specifier)"
|
|
373
387
|
);
|
|
374
|
-
|
|
388
|
+
try {
|
|
389
|
+
return await importModule(PUPPETEER_PACKAGE);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
const fallback = resolvePuppeteerFromWorkingDirectory();
|
|
392
|
+
if (!fallback) throw error;
|
|
393
|
+
return importModule(fallback);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function resolvePuppeteerFromWorkingDirectory() {
|
|
397
|
+
try {
|
|
398
|
+
const require2 = createRequire(join(process.cwd(), "package.json"));
|
|
399
|
+
return pathToFileURL(require2.resolve(PUPPETEER_PACKAGE)).href;
|
|
400
|
+
} catch {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
375
403
|
}
|
|
376
404
|
async function resolveMermaidScriptPath() {
|
|
377
405
|
const candidates = [
|
|
@@ -420,7 +448,7 @@ function normalizeRenderResult(value) {
|
|
|
420
448
|
};
|
|
421
449
|
}
|
|
422
450
|
const result = value;
|
|
423
|
-
if (result.ok === true && typeof result.svg === "string") {
|
|
451
|
+
if (result.ok === true && typeof result.svg === "string" && result.svg.trimStart().startsWith("<svg")) {
|
|
424
452
|
return { ok: true, svg: result.svg };
|
|
425
453
|
}
|
|
426
454
|
if (result.ok === false) {
|
|
@@ -436,6 +464,15 @@ function normalizeRenderResult(value) {
|
|
|
436
464
|
message: "Mermaid renderer returned an invalid result."
|
|
437
465
|
};
|
|
438
466
|
}
|
|
467
|
+
function withTimeout(promise, timeoutMs, message) {
|
|
468
|
+
let timer;
|
|
469
|
+
const timeout = new Promise((_, reject) => {
|
|
470
|
+
timer = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
471
|
+
});
|
|
472
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
473
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
474
|
+
});
|
|
475
|
+
}
|
|
439
476
|
function normalizeErrorKind(value) {
|
|
440
477
|
return value === "invalid-diagram" || value === "renderer-error" ? value : "renderer-error";
|
|
441
478
|
}
|
|
@@ -451,8 +488,9 @@ function mermaid(options = {}) {
|
|
|
451
488
|
name: "mermaid",
|
|
452
489
|
order: -10,
|
|
453
490
|
processedContentCache: {
|
|
454
|
-
version: "mermaid-
|
|
455
|
-
dependencyMode: "
|
|
491
|
+
version: "mermaid-v3",
|
|
492
|
+
dependencyMode: "unsafe",
|
|
493
|
+
appliesToContent: ({ source }) => containsMermaidFence(source) ? "applies" : "does-not-apply"
|
|
456
494
|
},
|
|
457
495
|
options,
|
|
458
496
|
validateOptions: validateMermaidOptions,
|
|
@@ -465,10 +503,21 @@ function mermaid(options = {}) {
|
|
|
465
503
|
dispose: async () => {
|
|
466
504
|
await session.dispose();
|
|
467
505
|
},
|
|
468
|
-
assets: [
|
|
469
|
-
|
|
506
|
+
assets: [
|
|
507
|
+
createStyleAsset("mermaid", "@riebeckite/plugin-mermaid/style.css")
|
|
508
|
+
],
|
|
509
|
+
clientEntries: [
|
|
510
|
+
createClientEntry(
|
|
511
|
+
"mermaid",
|
|
512
|
+
"@riebeckite/plugin-mermaid/client",
|
|
513
|
+
"initMermaidDiagrams"
|
|
514
|
+
)
|
|
515
|
+
]
|
|
470
516
|
});
|
|
471
517
|
}
|
|
518
|
+
function containsMermaidFence(source) {
|
|
519
|
+
return /^\s*(?:`{3,}|~{3,})[^\n`~]*\bmermaid\b[^\n`~]*$/im.test(source);
|
|
520
|
+
}
|
|
472
521
|
function validateMermaidOptions(options) {
|
|
473
522
|
if (!options) return [];
|
|
474
523
|
const issues = [];
|
|
@@ -478,6 +527,12 @@ function validateMermaidOptions(options) {
|
|
|
478
527
|
message: 'Expected "build" or "client".'
|
|
479
528
|
});
|
|
480
529
|
}
|
|
530
|
+
if (options.onRenderError !== void 0 && options.onRenderError !== "error" && options.onRenderError !== "client") {
|
|
531
|
+
issues.push({
|
|
532
|
+
path: "onRenderError",
|
|
533
|
+
message: 'Expected "error" or "client".'
|
|
534
|
+
});
|
|
535
|
+
}
|
|
481
536
|
if (!isMermaidTheme(options.theme)) {
|
|
482
537
|
issues.push({
|
|
483
538
|
path: "theme",
|
package/dist/src/rehype.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
import type { HastNode, MermaidOptions, MermaidRenderSession } from "./types.js";
|
|
1
|
+
import type { HastNode, MermaidBuildRenderErrorKind, MermaidBuildRenderResult, MermaidOptions, MermaidRenderSession } from "./types.js";
|
|
2
|
+
export declare class MermaidBuildRenderError extends Error {
|
|
3
|
+
readonly kind: MermaidBuildRenderErrorKind;
|
|
4
|
+
constructor(result: Extract<MermaidBuildRenderResult, {
|
|
5
|
+
ok: false;
|
|
6
|
+
}>);
|
|
7
|
+
}
|
|
2
8
|
export declare function rehypeMermaid(options: MermaidOptions, session: MermaidRenderSession): (tree: HastNode, file: unknown) => Promise<void>;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export type MermaidRenderMode = "build" | "client";
|
|
2
|
+
export type MermaidRenderFailureMode = "error" | "client";
|
|
2
3
|
export type MermaidTheme = string | {
|
|
3
4
|
light: string;
|
|
4
5
|
dark: string;
|
|
5
6
|
};
|
|
6
7
|
export type MermaidOptions = {
|
|
7
8
|
render?: MermaidRenderMode;
|
|
9
|
+
onRenderError?: MermaidRenderFailureMode;
|
|
8
10
|
theme?: MermaidTheme;
|
|
9
11
|
caption?: boolean;
|
|
10
12
|
fallback?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-mermaid",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"mermaid": "^11.12.2",
|
|
25
25
|
"puppeteer": "^25.11.0",
|
|
26
|
-
"@riebeckite/core": "0.0.
|
|
26
|
+
"@riebeckite/core": "0.0.23"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|