@helping-ai-workflow/md2doc 2.2.0 → 2.3.1
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 +6 -1
- package/bin/md2doc.js +7 -2
- package/lib/md2doc.js +151 -58
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ A single global CLI (`md2doc`) you can call from any directory.
|
|
|
8
8
|
|
|
9
9
|
Requires Node.js 18 or higher. The first install pulls puppeteer (≈ 170 MB Chromium download); subsequent installs reuse it.
|
|
10
10
|
|
|
11
|
+
Chromium is only needed for **PDF export** and the optional `--bake-svg` flag. HTML diagram rendering needs nothing extra — Graphviz runs in-process via WebAssembly, and Mermaid / WaveDrom are bundled and inlined, so diagrams render **offline with no system Graphviz and no CDN**.
|
|
12
|
+
|
|
11
13
|
### Recommended: install via nvm
|
|
12
14
|
|
|
13
15
|
If you do not yet have Node.js — or your system Node lives under `/usr/local` and `npm install -g` fails with `EACCES` — install Node through [nvm](https://github.com/nvm-sh/nvm) first. nvm puts Node under `~/.nvm`, so global packages never need `sudo`.
|
|
@@ -69,6 +71,7 @@ also pass `--open`.
|
|
|
69
71
|
| `--open` | Launch the platform viewer (`xdg-open` / `open` / `start`) after render. Default when `--out` is absent. |
|
|
70
72
|
| `--no-open` | Skip the viewer launch. |
|
|
71
73
|
| `--quiet` | Suppress per-file progress messages. |
|
|
74
|
+
| `--bake-svg` | Pre-render Mermaid / WaveDrom to inert SVG at generation time (HTML output only; needs Chromium). The output then contains no diagram JavaScript. |
|
|
72
75
|
| `--version`, `-v` | Print version. |
|
|
73
76
|
| `--help`, `-h` | Print help. |
|
|
74
77
|
|
|
@@ -101,7 +104,9 @@ digraph G { A -> B }
|
|
|
101
104
|
```
|
|
102
105
|
````
|
|
103
106
|
|
|
104
|
-
|
|
107
|
+
All three render directly in the output (HTML or PDF), **offline and with no system dependencies**: Graphviz `dot` runs in-process via WebAssembly (no system `dot` binary required), and Mermaid / WaveDrom are bundled and inlined (no CDN). Each engine's runtime is embedded only when the document actually uses that diagram type.
|
|
108
|
+
|
|
109
|
+
By default, Mermaid and WaveDrom render in the browser when the HTML is opened; pass `--bake-svg` to pre-render them to inert SVG at generation time instead (Graphviz is always pre-rendered to SVG).
|
|
105
110
|
|
|
106
111
|
## Why a global CLI
|
|
107
112
|
|
package/bin/md2doc.js
CHANGED
|
@@ -32,6 +32,7 @@ function printHelp() {
|
|
|
32
32
|
' --open Launch the platform viewer after render (default when --out is absent).',
|
|
33
33
|
' --no-open Skip the viewer launch.',
|
|
34
34
|
' --quiet Suppress per-file progress messages.',
|
|
35
|
+
' --bake-svg Pre-render mermaid/wavedrom to inert SVG (HTML output only; needs Chromium).',
|
|
35
36
|
' --version, -v Print version.',
|
|
36
37
|
' --help, -h Print this help.',
|
|
37
38
|
''
|
|
@@ -45,6 +46,7 @@ function parseArgs(argv) {
|
|
|
45
46
|
let out = null;
|
|
46
47
|
let openExplicit = null; // null = unset; true/false = user-specified
|
|
47
48
|
let quiet = false;
|
|
49
|
+
let bakeSvg = false;
|
|
48
50
|
|
|
49
51
|
for (let i = 0; i < argv.length; i++) {
|
|
50
52
|
const a = argv[i];
|
|
@@ -61,6 +63,7 @@ function parseArgs(argv) {
|
|
|
61
63
|
if (a === '--open') { openExplicit = true; continue; }
|
|
62
64
|
if (a === '--no-open') { openExplicit = false; continue; }
|
|
63
65
|
if (a === '--quiet') { quiet = true; continue; }
|
|
66
|
+
if (a === '--bake-svg') { bakeSvg = true; continue; }
|
|
64
67
|
if (a === '--out') {
|
|
65
68
|
if (out !== null) {
|
|
66
69
|
process.stderr.write('error: --out specified more than once\n');
|
|
@@ -102,7 +105,7 @@ function parseArgs(argv) {
|
|
|
102
105
|
open = (out === null);
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
return { inputs, formats, formatsExplicit, out, open, quiet };
|
|
108
|
+
return { inputs, formats, formatsExplicit, out, open, quiet, bakeSvg };
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
function shortHash(absPath) {
|
|
@@ -244,7 +247,9 @@ function main() {
|
|
|
244
247
|
const outputs = [];
|
|
245
248
|
for (const { input, output } of pairs) {
|
|
246
249
|
const stdio = args.quiet ? ['inherit', 'ignore', 'inherit'] : 'inherit';
|
|
247
|
-
const
|
|
250
|
+
const childArgs = [LIB, input, output];
|
|
251
|
+
if (args.bakeSvg) childArgs.push('--bake-svg');
|
|
252
|
+
const r = spawnSync(process.execPath, childArgs, { stdio });
|
|
248
253
|
if (r.status !== 0) {
|
|
249
254
|
process.stderr.write('error: render failed for ' + input + ' (exit ' + r.status + ')\n');
|
|
250
255
|
process.exit(r.status || 1);
|
package/lib/md2doc.js
CHANGED
|
@@ -20,9 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
22
|
const path = require('path');
|
|
23
|
-
const { spawnSync } = require('child_process');
|
|
24
23
|
|
|
25
24
|
const [,, src, dst] = process.argv;
|
|
25
|
+
const BAKE_SVG = process.argv.slice(4).includes('--bake-svg');
|
|
26
|
+
|
|
27
|
+
// How long to let client-side WaveDrom / Mermaid scripts render before we
|
|
28
|
+
// snapshot the DOM (PDF print, or --bake-svg inert-SVG bake).
|
|
29
|
+
const DIAGRAM_RENDER_WAIT_MS = 2500;
|
|
26
30
|
if (!src || !dst) {
|
|
27
31
|
console.error('Usage: node md2doc.js <input.md> <output.html|pdf>');
|
|
28
32
|
process.exit(1);
|
|
@@ -55,56 +59,57 @@ function safeResolve(modulePath) {
|
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
function inlineScriptTag(sourcePath) {
|
|
62
|
+
function inlineScriptTag(sourcePath, engine) {
|
|
59
63
|
if (!sourcePath) {
|
|
60
64
|
return null;
|
|
61
65
|
}
|
|
62
|
-
|
|
66
|
+
const marker = engine ? ` data-md2doc-diagram-engine="${engine}"` : '';
|
|
67
|
+
return `<script type="text/javascript"${marker}>\n${fs.readFileSync(sourcePath, 'utf8')}\n</script>`;
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
// wavedrom is pinned to 3.5.0 in package.json: 3.6.1+ adds an `exports` map
|
|
71
|
+
// that no longer exposes the `wavedrom/wavedrom.min.js` / `wavedrom/skins/default.js`
|
|
72
|
+
// subpaths resolved below, so a bump past 3.5.x must switch to an exports-blessed
|
|
73
|
+
// entry (or a vendored copy) or these safeResolve() calls return null and the
|
|
74
|
+
// hard guard further down exits 1.
|
|
65
75
|
const localWaveDromSkin = firstExistingPath([
|
|
66
76
|
process.env.WAVEDROM_SKIN_JS,
|
|
67
77
|
safeResolve('wavedrom/skins/default.js'),
|
|
68
|
-
'/home/user/.vscode-server/extensions/shd101wyy.markdown-preview-enhanced-0.8.22/crossnote/dependencies/wavedrom/skins/default.js',
|
|
69
78
|
]);
|
|
70
79
|
|
|
71
80
|
const localWaveDromJs = firstExistingPath([
|
|
72
81
|
process.env.WAVEDROM_JS,
|
|
73
82
|
safeResolve('wavedrom/wavedrom.min.js'),
|
|
74
|
-
'/home/user/.vscode-server/extensions/shd101wyy.markdown-preview-enhanced-0.8.22/crossnote/dependencies/wavedrom/wavedrom.min.js',
|
|
75
83
|
]);
|
|
76
84
|
|
|
77
85
|
const localMermaidJs = firstExistingPath([
|
|
78
86
|
process.env.MERMAID_JS,
|
|
79
87
|
safeResolve('mermaid/dist/mermaid.min.js'),
|
|
80
|
-
'/home/user/.vscode-server/extensions/shd101wyy.markdown-preview-enhanced-0.8.22/crossnote/dependencies/mermaid/mermaid.min.js',
|
|
81
88
|
]);
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|| '<script src="https://cdn.jsdelivr.net/npm/wavedrom/wavedrom.min.js" type="text/javascript"></script>';
|
|
90
|
+
if (!localMermaidJs || !localWaveDromJs || !localWaveDromSkin) {
|
|
91
|
+
console.error('[ERROR] bundled diagram runtime missing — reinstall dependencies with `npm install`');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
88
94
|
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
mermaid.initialize({ startOnLoad: true, theme: 'default' });
|
|
93
|
-
</script>`;
|
|
95
|
+
const waveDromSkinTag = inlineScriptTag(localWaveDromSkin, 'wavedrom');
|
|
96
|
+
const waveDromTag = inlineScriptTag(localWaveDromJs, 'wavedrom');
|
|
97
|
+
const mermaidScriptTag = inlineScriptTag(localMermaidJs, 'mermaid');
|
|
94
98
|
|
|
95
|
-
const mermaidInitTag =
|
|
96
|
-
? `<script type="text/javascript">
|
|
99
|
+
const mermaidInitTag = `<script type="text/javascript" data-md2doc-diagram-engine="mermaid">
|
|
97
100
|
if (typeof mermaid !== 'undefined') {
|
|
98
101
|
mermaid.initialize({ startOnLoad: true, theme: 'default' });
|
|
99
102
|
}
|
|
100
|
-
</script
|
|
101
|
-
: '';
|
|
103
|
+
</script>`;
|
|
102
104
|
|
|
103
105
|
// ── Markdown → HTML body ─────────────────────────────────────────────────────
|
|
104
106
|
// Use a custom renderer to intercept fenced code blocks before marked escapes
|
|
105
107
|
// their content. This is the correct approach — pre-processing the raw markdown
|
|
106
108
|
// string causes marked to re-parse the injected HTML and mangle indented lines.
|
|
107
109
|
|
|
110
|
+
let usesMermaid = false;
|
|
111
|
+
let usesWaveDrom = false;
|
|
112
|
+
|
|
108
113
|
let bodyHtml;
|
|
109
114
|
let tocHtml = '';
|
|
110
115
|
let serializedSections = '[]';
|
|
@@ -306,9 +311,11 @@ ${itemsHtml}
|
|
|
306
311
|
const code = (typeof token === 'object') ? token.text : token;
|
|
307
312
|
|
|
308
313
|
if (lang === 'wavedrom') {
|
|
314
|
+
usesWaveDrom = true;
|
|
309
315
|
return `\n<script type="WaveDrom">\n${code}\n</script>\n`;
|
|
310
316
|
}
|
|
311
317
|
if (lang === 'mermaid') {
|
|
318
|
+
usesMermaid = true;
|
|
312
319
|
// Escape so the browser delivers the literal source to mermaid. Raw
|
|
313
320
|
// injection lets the HTML parser consume entities and tags first —
|
|
314
321
|
// an author's <IP> became an <IP> element mermaid sanitized
|
|
@@ -316,19 +323,12 @@ ${itemsHtml}
|
|
|
316
323
|
return `\n<div class="mermaid">\n${escapeHtml(code)}\n</div>\n`;
|
|
317
324
|
}
|
|
318
325
|
if (lang === 'dot' || lang === 'graphviz') {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
.replace(/<\?xml[^>]*\?>/g, '')
|
|
326
|
-
.replace(/<!DOCTYPE[^>]*>/g, '')
|
|
327
|
-
.trim();
|
|
328
|
-
return `\n<div class="graphviz">${svg}</div>\n`;
|
|
329
|
-
}
|
|
330
|
-
console.error('[WARN] dot render failed:', r.stderr);
|
|
331
|
-
// Fall through to default code block
|
|
326
|
+
// Defer rendering to the async bakeGraphviz() post-pass so the
|
|
327
|
+
// synchronous marked() pass stays sync. The dot source is carried as
|
|
328
|
+
// base64 in a data attribute — safe for arbitrary dot syntax (quotes,
|
|
329
|
+
// angle brackets, newlines) inside an HTML attribute.
|
|
330
|
+
const b64 = Buffer.from(code, 'utf8').toString('base64');
|
|
331
|
+
return `\n<div class="graphviz" data-graphviz-src="${b64}"></div>\n`;
|
|
332
332
|
}
|
|
333
333
|
// Default: syntax-highlighted code block
|
|
334
334
|
const escaped = code.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
@@ -572,6 +572,20 @@ const html = `<!DOCTYPE html>
|
|
|
572
572
|
body[data-toc-collapsed] #toc-collapse-toggle {
|
|
573
573
|
transform: rotate(180deg);
|
|
574
574
|
}
|
|
575
|
+
/* In the 36px collapsed rail only the restore toggle remains: hide the
|
|
576
|
+
TOC-list buttons (no list to act on) and shrink padding so the toggle
|
|
577
|
+
fits inside the rail instead of being clipped by overflow:hidden. */
|
|
578
|
+
body[data-toc-collapsed] #toc-expand-all,
|
|
579
|
+
body[data-toc-collapsed] #toc-collapse-all {
|
|
580
|
+
display: none;
|
|
581
|
+
}
|
|
582
|
+
body[data-toc-collapsed] .toc {
|
|
583
|
+
padding: 12px 3px;
|
|
584
|
+
}
|
|
585
|
+
body[data-toc-collapsed] .toc-header {
|
|
586
|
+
margin-bottom: 0;
|
|
587
|
+
justify-content: center;
|
|
588
|
+
}
|
|
575
589
|
#toc-collapse-toggle {
|
|
576
590
|
margin-left: 0;
|
|
577
591
|
padding: 2px 8px;
|
|
@@ -1054,9 +1068,9 @@ ${bodyHtml}
|
|
|
1054
1068
|
</div>
|
|
1055
1069
|
|
|
1056
1070
|
<!-- WaveDrom -->
|
|
1057
|
-
${waveDromSkinTag}
|
|
1071
|
+
${usesWaveDrom ? `${waveDromSkinTag}
|
|
1058
1072
|
${waveDromTag}
|
|
1059
|
-
<script type="text/javascript">
|
|
1073
|
+
<script type="text/javascript" data-md2doc-diagram-engine="wavedrom">
|
|
1060
1074
|
function renderWaveDrom() {
|
|
1061
1075
|
if (typeof WaveDrom !== 'undefined') {
|
|
1062
1076
|
WaveDrom.ProcessAll();
|
|
@@ -1066,11 +1080,11 @@ ${waveDromTag}
|
|
|
1066
1080
|
window.addEventListener('load', renderWaveDrom);
|
|
1067
1081
|
setTimeout(renderWaveDrom, 250);
|
|
1068
1082
|
setTimeout(renderWaveDrom, 1000);
|
|
1069
|
-
</script
|
|
1083
|
+
</script>` : ''}
|
|
1070
1084
|
|
|
1071
1085
|
<!-- Mermaid -->
|
|
1072
|
-
${mermaidScriptTag}
|
|
1073
|
-
${mermaidInitTag}
|
|
1086
|
+
${usesMermaid ? `${mermaidScriptTag}
|
|
1087
|
+
${mermaidInitTag}` : ''}
|
|
1074
1088
|
|
|
1075
1089
|
<!-- Reader runtime -->
|
|
1076
1090
|
<script id="reader-section-data" type="application/json">${serializedSections}</script>
|
|
@@ -1540,13 +1554,90 @@ ${mermaidInitTag}
|
|
|
1540
1554
|
</body>
|
|
1541
1555
|
</html>`;
|
|
1542
1556
|
|
|
1557
|
+
// Render every deferred dot/graphviz placeholder to inline SVG using the
|
|
1558
|
+
// in-process WASM engine. Loads the WASM module only when at least one dot
|
|
1559
|
+
// block exists, so text-only docs pay nothing.
|
|
1560
|
+
async function bakeGraphviz(htmlStr) {
|
|
1561
|
+
if (!/data-graphviz-src=/.test(htmlStr)) return htmlStr;
|
|
1562
|
+
let Graphviz;
|
|
1563
|
+
try {
|
|
1564
|
+
({ Graphviz } = require('@hpcc-js/wasm-graphviz'));
|
|
1565
|
+
} catch (e) {
|
|
1566
|
+
console.error('[ERROR] @hpcc-js/wasm-graphviz not installed — run `npm install`:', e.message);
|
|
1567
|
+
return htmlStr;
|
|
1568
|
+
}
|
|
1569
|
+
const gv = await Graphviz.load();
|
|
1570
|
+
return htmlStr.replace(/<div class="graphviz" data-graphviz-src="([^"]*)"><\/div>/g, (m, b64) => {
|
|
1571
|
+
const dotSrc = Buffer.from(b64, 'base64').toString('utf8');
|
|
1572
|
+
try {
|
|
1573
|
+
const svg = gv.dot(dotSrc)
|
|
1574
|
+
.replace(/<\?xml[^>]*\?>/g, '')
|
|
1575
|
+
.replace(/<!DOCTYPE[\s\S]*?>/g, '')
|
|
1576
|
+
// drop graphviz's "Generated by graphviz" banner comment so the div and <svg> are adjacent
|
|
1577
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
1578
|
+
.trim();
|
|
1579
|
+
return `<div class="graphviz">${svg}</div>`;
|
|
1580
|
+
} catch (e) {
|
|
1581
|
+
console.error('[WARN] graphviz render failed:', e.message);
|
|
1582
|
+
const escaped = dotSrc.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1583
|
+
return `<pre><code class="language-dot">${escaped}</code></pre>\n`;
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
function launchBrowser(puppeteer) {
|
|
1589
|
+
return puppeteer.launch({
|
|
1590
|
+
headless: 'new',
|
|
1591
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-crash-reporter', '--disable-dev-shm-usage'],
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// Pre-render mermaid/wavedrom to inert SVG using headless Chromium, then strip
|
|
1596
|
+
// the diagram-engine runtime scripts so the output HTML carries no diagram JS.
|
|
1597
|
+
async function bakeDiagrams(htmlStr) {
|
|
1598
|
+
if (!/data-md2doc-diagram-engine/.test(htmlStr)) return htmlStr;
|
|
1599
|
+
let puppeteer;
|
|
1600
|
+
try {
|
|
1601
|
+
puppeteer = require('puppeteer');
|
|
1602
|
+
} catch (e) {
|
|
1603
|
+
console.error('[ERROR] --bake-svg requires puppeteer/Chromium — install it, or drop --bake-svg:', e.message);
|
|
1604
|
+
process.exit(1);
|
|
1605
|
+
}
|
|
1606
|
+
const tmp = dst.replace(/\.html$/i, '._bake.html');
|
|
1607
|
+
fs.writeFileSync(tmp, htmlStr, 'utf8');
|
|
1608
|
+
let browser;
|
|
1609
|
+
try {
|
|
1610
|
+
browser = await launchBrowser(puppeteer);
|
|
1611
|
+
} catch (e) {
|
|
1612
|
+
fs.unlinkSync(tmp);
|
|
1613
|
+
console.error('[ERROR] --bake-svg requires Chromium but it failed to launch — drop --bake-svg or install Chromium:', e.message);
|
|
1614
|
+
process.exit(1);
|
|
1615
|
+
}
|
|
1616
|
+
try {
|
|
1617
|
+
const page = await browser.newPage();
|
|
1618
|
+
await page.goto('file://' + path.resolve(tmp), { waitUntil: 'load' });
|
|
1619
|
+
await new Promise(r => setTimeout(r, DIAGRAM_RENDER_WAIT_MS));
|
|
1620
|
+
await page.evaluate(() => {
|
|
1621
|
+
document.querySelectorAll('script[data-md2doc-diagram-engine]').forEach((s) => s.remove());
|
|
1622
|
+
});
|
|
1623
|
+
return await page.content();
|
|
1624
|
+
} finally {
|
|
1625
|
+
await browser.close();
|
|
1626
|
+
fs.unlinkSync(tmp);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1543
1630
|
// ── Output ───────────────────────────────────────────────────────────────────
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1631
|
+
(async () => {
|
|
1632
|
+
let finalHtml = await bakeGraphviz(html);
|
|
1633
|
+
|
|
1634
|
+
if (ext === '.html') {
|
|
1635
|
+
if (BAKE_SVG) finalHtml = await bakeDiagrams(finalHtml);
|
|
1636
|
+
fs.writeFileSync(dst, finalHtml, 'utf8');
|
|
1637
|
+
console.log(`[HTML] ${src} → ${dst}`);
|
|
1547
1638
|
|
|
1548
|
-
} else if (ext === '.pdf') {
|
|
1549
|
-
|
|
1639
|
+
} else if (ext === '.pdf') {
|
|
1640
|
+
if (BAKE_SVG) console.log('[INFO] --bake-svg is redundant for PDF output (already static); ignoring');
|
|
1550
1641
|
let puppeteer;
|
|
1551
1642
|
try {
|
|
1552
1643
|
puppeteer = require('puppeteer');
|
|
@@ -1555,26 +1646,25 @@ if (ext === '.html') {
|
|
|
1555
1646
|
process.exit(1);
|
|
1556
1647
|
}
|
|
1557
1648
|
|
|
1558
|
-
// Write temporary HTML, launch headless Chromium, export PDF
|
|
1649
|
+
// Write temporary HTML, launch headless Chromium, export PDF.
|
|
1559
1650
|
// Case-insensitive: an uppercase .PDF dst must not make tmp === dst, or the
|
|
1560
1651
|
// unlinkSync below deletes the freshly written PDF.
|
|
1561
1652
|
const tmp = dst.replace(/\.pdf$/i, '._tmp.html');
|
|
1562
|
-
fs.writeFileSync(tmp,
|
|
1653
|
+
fs.writeFileSync(tmp, finalHtml, 'utf8');
|
|
1563
1654
|
|
|
1564
|
-
const browser = await puppeteer
|
|
1565
|
-
|
|
1566
|
-
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-crash-reporter', '--disable-dev-shm-usage'],
|
|
1567
|
-
});
|
|
1568
|
-
const page = await browser.newPage();
|
|
1655
|
+
const browser = await launchBrowser(puppeteer);
|
|
1656
|
+
const page = await browser.newPage();
|
|
1569
1657
|
|
|
1570
1658
|
await page.goto('file://' + path.resolve(tmp), { waitUntil: 'load' });
|
|
1571
1659
|
|
|
1572
|
-
// Allow WaveDrom / Mermaid scripts time to render diagrams
|
|
1573
|
-
|
|
1660
|
+
// Allow WaveDrom / Mermaid scripts time to render diagrams.
|
|
1661
|
+
// NOTE: this sleep is load-bearing for the DEFAULT (view-time) render path.
|
|
1662
|
+
// It is only safe to drop under --bake-svg, where the DOM is already final SVG.
|
|
1663
|
+
await new Promise(r => setTimeout(r, DIAGRAM_RENDER_WAIT_MS));
|
|
1574
1664
|
|
|
1575
1665
|
await page.pdf({
|
|
1576
|
-
path:
|
|
1577
|
-
format:
|
|
1666
|
+
path: dst,
|
|
1667
|
+
format: 'A4',
|
|
1578
1668
|
printBackground: true,
|
|
1579
1669
|
outline: true,
|
|
1580
1670
|
tagged: true,
|
|
@@ -1584,9 +1674,12 @@ if (ext === '.html') {
|
|
|
1584
1674
|
await browser.close();
|
|
1585
1675
|
fs.unlinkSync(tmp);
|
|
1586
1676
|
console.log(`[PDF] ${src} → ${dst}`);
|
|
1587
|
-
})();
|
|
1588
1677
|
|
|
1589
|
-
} else {
|
|
1590
|
-
|
|
1678
|
+
} else {
|
|
1679
|
+
console.error('[ERROR] Output extension must be .html or .pdf');
|
|
1680
|
+
process.exit(1);
|
|
1681
|
+
}
|
|
1682
|
+
})().catch((e) => {
|
|
1683
|
+
console.error('[ERROR]', (e && e.stack) || e);
|
|
1591
1684
|
process.exit(1);
|
|
1592
|
-
}
|
|
1685
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@helping-ai-workflow/md2doc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"description": "Markdown → HTML / PDF renderer with WaveDrom, Mermaid, and Graphviz support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -26,8 +26,11 @@
|
|
|
26
26
|
"node": ">=18"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
+
"@hpcc-js/wasm-graphviz": "1.22.0",
|
|
29
30
|
"marked": "^14.1.0",
|
|
30
|
-
"
|
|
31
|
+
"mermaid": "11.15.0",
|
|
32
|
+
"puppeteer": "^24.15.0",
|
|
33
|
+
"wavedrom": "3.5.0"
|
|
31
34
|
},
|
|
32
35
|
"scripts": {
|
|
33
36
|
"preinstall": "node scripts/preinstall.js",
|