@docubook/flame 1.5.0 → 1.5.2
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/.docu/lib/build.deno.js +4 -4
- package/.docu/lib/build.impl-FA57P3VC.js +12 -0
- package/.docu/lib/build.node.js +4 -4
- package/.docu/lib/{chunk-654THQOR.js → chunk-7J3N2MCP.js} +26 -119
- package/.docu/lib/{chunk-RE4NGTMT.js → chunk-EOK6KATZ.js} +9 -3
- package/.docu/lib/{chunk-HH4YXWEF.js → chunk-GLOJMRT4.js} +1 -3
- package/.docu/lib/{chunk-C6RZ2KBH.js → chunk-NXO2BF64.js} +1 -1
- package/.docu/lib/{chunk-ZOWTASXL.js → chunk-Q3AXTF2V.js} +2 -2
- package/.docu/lib/{chunk-2QHMGZIL.js → chunk-QGEFJH47.js} +2 -10
- package/.docu/lib/{chunk-X6GYOIYZ.js → chunk-TEXSBNMR.js} +3 -3
- package/.docu/lib/clean.js +1 -1
- package/.docu/lib/deploy.deno.js +1 -1
- package/.docu/lib/deploy.node.js +1 -1
- package/.docu/lib/preview.deno.js +3 -3
- package/.docu/lib/preview.node.js +3 -3
- package/.docu/lib/server.deno.js +4 -4
- package/.docu/lib/server.node.js +4 -4
- package/.docu/node/build.impl.ts +0 -8
- package/.docu/node/hydrate.node.ts +1 -12
- package/.docu/node/hydrate.ts +1 -12
- package/.docu/node/security.ts +9 -3
- package/.docu/node/sentry.ts +1 -0
- package/bin/cli.js +12 -2
- package/package.json +4 -6
- package/template/docs/guide/deployment.mdx +2 -2
- package/.docu/lib/build.impl-ST63VRTV.js +0 -12
- package/.docu/node/build-summary.ts +0 -126
package/.docu/lib/build.deno.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-7J3N2MCP.js";
|
|
4
|
+
import "./chunk-QGEFJH47.js";
|
|
5
|
+
import "./chunk-EOK6KATZ.js";
|
|
6
|
+
import "./chunk-GLOJMRT4.js";
|
|
7
7
|
import "./chunk-J5NMYSBJ.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.deno.ts
|
package/.docu/lib/build.node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-7J3N2MCP.js";
|
|
4
|
+
import "./chunk-QGEFJH47.js";
|
|
5
|
+
import "./chunk-EOK6KATZ.js";
|
|
6
|
+
import "./chunk-GLOJMRT4.js";
|
|
7
7
|
import "./chunk-J5NMYSBJ.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.node.ts
|
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
htmlShell,
|
|
14
14
|
initSentry,
|
|
15
15
|
loadPlugins
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-QGEFJH47.js";
|
|
17
17
|
import {
|
|
18
18
|
generateNonce,
|
|
19
19
|
scanMdxFiles
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-EOK6KATZ.js";
|
|
21
21
|
import {
|
|
22
22
|
logger
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-GLOJMRT4.js";
|
|
24
24
|
import {
|
|
25
25
|
ASSETS_DIR,
|
|
26
26
|
CACHE_FILE,
|
|
@@ -32,99 +32,12 @@ import {
|
|
|
32
32
|
} from "./chunk-J5NMYSBJ.js";
|
|
33
33
|
|
|
34
34
|
// .docu/node/build.impl.ts
|
|
35
|
-
import { readFile, writeFile, mkdir, readdir
|
|
36
|
-
import { existsSync
|
|
35
|
+
import { readFile, writeFile, mkdir, readdir, copyFile } from "node:fs/promises";
|
|
36
|
+
import { existsSync } from "node:fs";
|
|
37
37
|
import { createHash } from "node:crypto";
|
|
38
|
-
import { join
|
|
38
|
+
import { join, dirname } from "node:path";
|
|
39
39
|
import React from "react";
|
|
40
40
|
import { renderToString } from "react-dom/server";
|
|
41
|
-
|
|
42
|
-
// .docu/node/build-summary.ts
|
|
43
|
-
import { readdir, stat } from "node:fs/promises";
|
|
44
|
-
import { join, relative, extname } from "node:path";
|
|
45
|
-
import { existsSync } from "node:fs";
|
|
46
|
-
var MAX_FILES_SHOWN = 10;
|
|
47
|
-
var SEP = "\u2500".repeat(60);
|
|
48
|
-
function formatSize(bytes) {
|
|
49
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
50
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
51
|
-
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
52
|
-
}
|
|
53
|
-
async function walkFiles(dir) {
|
|
54
|
-
const out = [];
|
|
55
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
56
|
-
for (const entry of entries) {
|
|
57
|
-
const full = join(dir, entry.name);
|
|
58
|
-
if (entry.isDirectory()) {
|
|
59
|
-
out.push(...await walkFiles(full));
|
|
60
|
-
} else if (entry.isFile()) {
|
|
61
|
-
const s = await stat(full);
|
|
62
|
-
out.push({ path: full, size: s.size });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return out;
|
|
66
|
-
}
|
|
67
|
-
async function collectBuildOutput(distDir) {
|
|
68
|
-
if (!existsSync(distDir)) return null;
|
|
69
|
-
const all = await walkFiles(distDir);
|
|
70
|
-
if (all.length === 0) return null;
|
|
71
|
-
const files = all.map((f) => ({ path: relative(distDir, f.path), size: f.size })).sort((a, b) => b.size !== a.size ? b.size - a.size : a.path.localeCompare(b.path));
|
|
72
|
-
const byExt = /* @__PURE__ */ new Map();
|
|
73
|
-
for (const f of files) {
|
|
74
|
-
const ext = extname(f.path) || "(none)";
|
|
75
|
-
const entry = byExt.get(ext) ?? { ext, count: 0, size: 0 };
|
|
76
|
-
entry.count++;
|
|
77
|
-
entry.size += f.size;
|
|
78
|
-
byExt.set(ext, entry);
|
|
79
|
-
}
|
|
80
|
-
const byType = [...byExt.values()].sort(
|
|
81
|
-
(a, b) => b.size !== a.size ? b.size - a.size : b.count - a.count
|
|
82
|
-
);
|
|
83
|
-
return {
|
|
84
|
-
files,
|
|
85
|
-
byType,
|
|
86
|
-
totalFiles: files.length,
|
|
87
|
-
totalSize: files.reduce((sum, f) => sum + f.size, 0)
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
function formatBuildSummary(output) {
|
|
91
|
-
const lines = [];
|
|
92
|
-
lines.push("\u{1F4E6} Build Output Summary:");
|
|
93
|
-
lines.push(SEP);
|
|
94
|
-
const shown = output.files.slice(0, MAX_FILES_SHOWN);
|
|
95
|
-
const nameWidth = Math.min(Math.max(...shown.map((f) => f.path.length), 1), 50);
|
|
96
|
-
for (const f of shown) {
|
|
97
|
-
lines.push(` ${f.path.padEnd(nameWidth)} ${formatSize(f.size).padStart(10)}`);
|
|
98
|
-
}
|
|
99
|
-
const remaining = output.totalFiles - shown.length;
|
|
100
|
-
if (remaining > 0) {
|
|
101
|
-
lines.push(` ... and ${remaining} more file${remaining === 1 ? "" : "s"}`);
|
|
102
|
-
}
|
|
103
|
-
lines.push(SEP);
|
|
104
|
-
lines.push("\u{1F4CA} Summary by Type:");
|
|
105
|
-
const extWidth = Math.max(...output.byType.map((t) => t.ext.length), 1);
|
|
106
|
-
for (const t of output.byType) {
|
|
107
|
-
const plural = t.count === 1 ? "file" : "files";
|
|
108
|
-
lines.push(
|
|
109
|
-
` ${t.ext.padEnd(extWidth)} ${String(t.count).padStart(3)} ${plural} ${formatSize(
|
|
110
|
-
t.size
|
|
111
|
-
).padStart(10)}`
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
lines.push(SEP);
|
|
115
|
-
const totalPlural = output.totalFiles === 1 ? "file" : "files";
|
|
116
|
-
lines.push(
|
|
117
|
-
`\u{1F4C8} Total: ${output.totalFiles} ${totalPlural} ${formatSize(output.totalSize).padStart(10)}`
|
|
118
|
-
);
|
|
119
|
-
lines.push(SEP);
|
|
120
|
-
return lines.join("\n");
|
|
121
|
-
}
|
|
122
|
-
async function generateBuildSummary(distDir) {
|
|
123
|
-
const output = await collectBuildOutput(distDir);
|
|
124
|
-
return output ? formatBuildSummary(output) : null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// .docu/node/build.impl.ts
|
|
128
41
|
function parseArgs() {
|
|
129
42
|
const args = process.argv.slice(2);
|
|
130
43
|
return {
|
|
@@ -137,7 +50,7 @@ function hashContent(content) {
|
|
|
137
50
|
}
|
|
138
51
|
async function readCache() {
|
|
139
52
|
try {
|
|
140
|
-
if (
|
|
53
|
+
if (existsSync(CACHE_FILE)) {
|
|
141
54
|
const data = await readFile(CACHE_FILE, "utf-8");
|
|
142
55
|
return JSON.parse(data);
|
|
143
56
|
}
|
|
@@ -229,12 +142,12 @@ ${msg}`, { cause: err });
|
|
|
229
142
|
return html;
|
|
230
143
|
}
|
|
231
144
|
async function copyDirectoryRecursive(src, dest) {
|
|
232
|
-
if (!
|
|
145
|
+
if (!existsSync(src)) return;
|
|
233
146
|
await mkdir(dest, { recursive: true });
|
|
234
|
-
const entries = await
|
|
147
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
235
148
|
for (const entry of entries) {
|
|
236
|
-
const srcPath =
|
|
237
|
-
const destPath =
|
|
149
|
+
const srcPath = join(src, entry.name);
|
|
150
|
+
const destPath = join(dest, entry.name);
|
|
238
151
|
if (entry.isDirectory()) {
|
|
239
152
|
await copyDirectoryRecursive(srcPath, destPath);
|
|
240
153
|
} else {
|
|
@@ -256,7 +169,7 @@ async function runBuild() {
|
|
|
256
169
|
}
|
|
257
170
|
await mkdir(DIST_DIR, { recursive: true });
|
|
258
171
|
await mkdir(ASSETS_DIR, { recursive: true });
|
|
259
|
-
await copyDirectoryRecursive(DOCS_ASSETS_DIR,
|
|
172
|
+
await copyDirectoryRecursive(DOCS_ASSETS_DIR, join(DIST_DIR, "docs", "assets"));
|
|
260
173
|
const mdxFiles = await scanMdxFiles(DOCS_DIR);
|
|
261
174
|
const cache = args.force ? {} : await readCache();
|
|
262
175
|
let built = 0;
|
|
@@ -287,8 +200,8 @@ async function runBuild() {
|
|
|
287
200
|
logger.spinner.start("Building pages...");
|
|
288
201
|
t = performance.now();
|
|
289
202
|
const allRelPaths = mdxFiles.map((f) => f.absPath.replace(PROJECT_ROOT + "/", ""));
|
|
290
|
-
const indexMdxFull =
|
|
291
|
-
if (
|
|
203
|
+
const indexMdxFull = join(DOCS_DIR, "index.mdx");
|
|
204
|
+
if (existsSync(indexMdxFull)) {
|
|
292
205
|
allRelPaths.push(indexMdxFull.replace(PROJECT_ROOT + "/", ""));
|
|
293
206
|
}
|
|
294
207
|
const gitDates = await getGitLastModifiedBatch(allRelPaths);
|
|
@@ -298,8 +211,8 @@ async function runBuild() {
|
|
|
298
211
|
for (const file of mdxFiles) {
|
|
299
212
|
const rebuildDecision = shouldRebuild(file.path, file.mtime, cache);
|
|
300
213
|
if (rebuildDecision === "no") {
|
|
301
|
-
const outputPath =
|
|
302
|
-
if (
|
|
214
|
+
const outputPath = join(DIST_DIR, "docs", `${file.path}.html`);
|
|
215
|
+
if (existsSync(outputPath) && !assetsChanged) {
|
|
303
216
|
skipped++;
|
|
304
217
|
continue;
|
|
305
218
|
}
|
|
@@ -316,8 +229,8 @@ async function runBuild() {
|
|
|
316
229
|
const cached = cache[file.path];
|
|
317
230
|
if (cached && cached.hash === contentHash) {
|
|
318
231
|
if (!assetsChanged) {
|
|
319
|
-
const outputPath =
|
|
320
|
-
if (
|
|
232
|
+
const outputPath = join(DIST_DIR, "docs", `${file.path}.html`);
|
|
233
|
+
if (existsSync(outputPath)) {
|
|
321
234
|
cache[file.path] = { ...cached, mtime: file.mtime, builtAt: Date.now() };
|
|
322
235
|
skipped++;
|
|
323
236
|
continue;
|
|
@@ -340,7 +253,7 @@ async function runBuild() {
|
|
|
340
253
|
builder,
|
|
341
254
|
pageNonce
|
|
342
255
|
);
|
|
343
|
-
const outputPath =
|
|
256
|
+
const outputPath = join(DIST_DIR, "docs", `${capturedFile.path}.html`);
|
|
344
257
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
345
258
|
await writeFile(outputPath, html);
|
|
346
259
|
cache[capturedFile.path] = {
|
|
@@ -362,7 +275,7 @@ async function runBuild() {
|
|
|
362
275
|
await Promise.all(buildTasks.slice(i, i + CONCURRENCY).map((fn) => fn()));
|
|
363
276
|
}
|
|
364
277
|
try {
|
|
365
|
-
const indexMdxPath =
|
|
278
|
+
const indexMdxPath = join(DOCS_DIR, "index.mdx");
|
|
366
279
|
const indexRaw = await readFile(indexMdxPath, "utf-8");
|
|
367
280
|
const indexRelPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
|
|
368
281
|
const indexHtml = await renderDocsPage(
|
|
@@ -374,8 +287,8 @@ async function runBuild() {
|
|
|
374
287
|
builder,
|
|
375
288
|
generateNonce()
|
|
376
289
|
);
|
|
377
|
-
await mkdir(
|
|
378
|
-
await writeFile(
|
|
290
|
+
await mkdir(join(DIST_DIR, "docs"), { recursive: true });
|
|
291
|
+
await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
|
|
379
292
|
} catch (err) {
|
|
380
293
|
const msg = err instanceof Error ? err.message : String(err);
|
|
381
294
|
errors.push(`index.mdx: ${msg}`);
|
|
@@ -395,7 +308,7 @@ async function runBuild() {
|
|
|
395
308
|
nonce: generateNonce(),
|
|
396
309
|
themeCss: inlineThemeCss
|
|
397
310
|
});
|
|
398
|
-
await writeFile(
|
|
311
|
+
await writeFile(join(DIST_DIR, "index.html"), landingHtml);
|
|
399
312
|
const notFoundPage = React.createElement(
|
|
400
313
|
DocsLayout,
|
|
401
314
|
{ repoUrl: docuConfig.repo?.url },
|
|
@@ -412,7 +325,7 @@ async function runBuild() {
|
|
|
412
325
|
nonce: generateNonce(),
|
|
413
326
|
themeCss: inlineThemeCss
|
|
414
327
|
});
|
|
415
|
-
await writeFile(
|
|
328
|
+
await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
|
|
416
329
|
logger.spinner.stop(
|
|
417
330
|
`Built ${built} pages (${skipped} cached) \x1B[90m(${Math.round(performance.now() - t)}ms)\x1B[0m`
|
|
418
331
|
);
|
|
@@ -420,8 +333,8 @@ async function runBuild() {
|
|
|
420
333
|
const pages = mdxFiles.map((f) => ({
|
|
421
334
|
slug: f.path,
|
|
422
335
|
title: f.path.split("/").pop() || f.path,
|
|
423
|
-
filePath:
|
|
424
|
-
outputPath:
|
|
336
|
+
filePath: join(DOCS_DIR, f.path),
|
|
337
|
+
outputPath: join(DIST_DIR, "docs", `${f.path}.html`)
|
|
425
338
|
}));
|
|
426
339
|
await builder.runOnEnd(pages);
|
|
427
340
|
}
|
|
@@ -430,12 +343,6 @@ async function runBuild() {
|
|
|
430
343
|
const indexCount = await generateSearchIndex();
|
|
431
344
|
logger.indexDone(indexCount, Math.round(performance.now() - t));
|
|
432
345
|
logger.routes();
|
|
433
|
-
console.log("");
|
|
434
|
-
const buildSummary = await generateBuildSummary(DIST_DIR);
|
|
435
|
-
if (buildSummary) {
|
|
436
|
-
console.log(buildSummary);
|
|
437
|
-
console.log("");
|
|
438
|
-
}
|
|
439
346
|
await writeCache(cache);
|
|
440
347
|
if (errors.length > 0) {
|
|
441
348
|
console.error(`
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// .docu/node/security.ts
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { realpathSync } from "node:fs";
|
|
4
5
|
var SECURITY_HEADERS = {
|
|
@@ -9,7 +10,7 @@ var SECURITY_HEADERS = {
|
|
|
9
10
|
"Permissions-Policy": "camera=(), microphone=(), geolocation=()"
|
|
10
11
|
};
|
|
11
12
|
function generateNonce() {
|
|
12
|
-
return
|
|
13
|
+
return randomBytes(16).toString("base64");
|
|
13
14
|
}
|
|
14
15
|
function cspHeader(nonce, allowEval = false) {
|
|
15
16
|
const scriptSrc = allowEval ? `script-src 'self' 'nonce-${nonce}' 'unsafe-eval'` : `script-src 'self' 'nonce-${nonce}'`;
|
|
@@ -89,11 +90,16 @@ function wrapPluginResponse(pluginResponse, allowEval = false) {
|
|
|
89
90
|
securedHeaders.set(key, value);
|
|
90
91
|
}
|
|
91
92
|
}
|
|
93
|
+
let body = pluginResponse.body;
|
|
92
94
|
const contentType = securedHeaders.get("Content-Type") || "";
|
|
93
95
|
if (contentType.includes("text/html") && !securedHeaders.has("Content-Security-Policy")) {
|
|
94
|
-
|
|
96
|
+
const nonce = generateNonce();
|
|
97
|
+
securedHeaders.set("Content-Security-Policy", cspHeader(nonce, allowEval));
|
|
98
|
+
if (typeof body === "string") {
|
|
99
|
+
body = injectNonce(body, nonce);
|
|
100
|
+
}
|
|
95
101
|
}
|
|
96
|
-
return new Response(
|
|
102
|
+
return new Response(body, {
|
|
97
103
|
status: pluginResponse.status,
|
|
98
104
|
statusText: pluginResponse.statusText,
|
|
99
105
|
headers: securedHeaders
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
7
|
name: "@docubook/flame",
|
|
8
|
-
version: "1.5.
|
|
8
|
+
version: "1.5.2",
|
|
9
9
|
description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
10
10
|
type: "module",
|
|
11
11
|
bin: {
|
|
@@ -89,13 +89,11 @@ var package_default = {
|
|
|
89
89
|
"@types/node": "^22.0.0",
|
|
90
90
|
"@types/react": "^19.0.0",
|
|
91
91
|
"@types/react-dom": "^19.0.0",
|
|
92
|
-
autoprefixer: "^10.4.0",
|
|
93
92
|
"bun-types": "^1.3.0",
|
|
94
93
|
eslint: "^10.3.0",
|
|
95
94
|
"eslint-plugin-perfectionist": "^5.9.0",
|
|
96
95
|
"eslint-plugin-react": "^7.37.5",
|
|
97
96
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
98
|
-
postcss: "^8.4.0",
|
|
99
97
|
tailwindcss: "^4.3.0",
|
|
100
98
|
typescript: "^5.9.3",
|
|
101
99
|
"typescript-eslint": "^8.59.2"
|
|
@@ -12,7 +12,7 @@ var WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
|
|
|
12
12
|
async function runDeploy() {
|
|
13
13
|
console.log("\u{1F4E6} Building for production...\n");
|
|
14
14
|
process.env.NODE_ENV = "production";
|
|
15
|
-
const { runBuildCli } = await import("./build.impl-
|
|
15
|
+
const { runBuildCli } = await import("./build.impl-FA57P3VC.js");
|
|
16
16
|
await runBuildCli();
|
|
17
17
|
await writeFile(join(DIST_DIR, ".nojekyll"), "");
|
|
18
18
|
if (!existsSync(WORKFLOW_FILE)) {
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
generateNonce,
|
|
5
5
|
getContentType,
|
|
6
6
|
injectNonce
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EOK6KATZ.js";
|
|
8
8
|
import {
|
|
9
9
|
logger
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GLOJMRT4.js";
|
|
11
11
|
import {
|
|
12
12
|
DIST_DIR
|
|
13
13
|
} from "./chunk-J5NMYSBJ.js";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isExternalUrl,
|
|
6
6
|
normalizeImporterPath,
|
|
7
7
|
scanMdxFiles
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EOK6KATZ.js";
|
|
9
9
|
import {
|
|
10
10
|
ASSETS_DIR,
|
|
11
11
|
DOCS_DIR,
|
|
@@ -847,15 +847,6 @@ async function buildClientBundle() {
|
|
|
847
847
|
return { contents: JSON.stringify(resolved), loader: "json" };
|
|
848
848
|
});
|
|
849
849
|
}
|
|
850
|
-
},
|
|
851
|
-
{
|
|
852
|
-
name: "mdx-jsx-runtime",
|
|
853
|
-
setup(build2) {
|
|
854
|
-
build2.onLoad({ filter: /next-mdx-remote[/\\].*jsx-runtime/ }, () => {
|
|
855
|
-
const source = nodeEnv === "production" ? `module.exports.jsxRuntime = require("react/jsx-runtime");` : `module.exports.jsxRuntime = require("react/jsx-dev-runtime");`;
|
|
856
|
-
return { contents: source, loader: "js" };
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
850
|
}
|
|
860
851
|
]
|
|
861
852
|
});
|
|
@@ -1040,6 +1031,7 @@ async function initSentry() {
|
|
|
1040
1031
|
initialized = true;
|
|
1041
1032
|
} catch {
|
|
1042
1033
|
sentry = null;
|
|
1034
|
+
initialized = false;
|
|
1043
1035
|
}
|
|
1044
1036
|
}
|
|
1045
1037
|
function captureException(err, context) {
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
htmlShell,
|
|
15
15
|
initSentry,
|
|
16
16
|
loadPlugins
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-QGEFJH47.js";
|
|
18
18
|
import {
|
|
19
19
|
SECURITY_HEADERS,
|
|
20
20
|
generateNonce,
|
|
@@ -24,10 +24,10 @@ import {
|
|
|
24
24
|
isSlugSafe,
|
|
25
25
|
stripDocsHtmlSuffix,
|
|
26
26
|
wrapPluginResponse
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-EOK6KATZ.js";
|
|
28
28
|
import {
|
|
29
29
|
logger
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GLOJMRT4.js";
|
|
31
31
|
import {
|
|
32
32
|
DIST_DIR,
|
|
33
33
|
DOCS_DIR,
|
package/.docu/lib/clean.js
CHANGED
package/.docu/lib/deploy.deno.js
CHANGED
package/.docu/lib/deploy.node.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runPreview
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-Q3AXTF2V.js";
|
|
4
|
+
import "./chunk-EOK6KATZ.js";
|
|
5
|
+
import "./chunk-GLOJMRT4.js";
|
|
6
6
|
import "./chunk-J5NMYSBJ.js";
|
|
7
7
|
|
|
8
8
|
// .docu/node/preview.deno.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runPreview
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-Q3AXTF2V.js";
|
|
4
|
+
import "./chunk-EOK6KATZ.js";
|
|
5
|
+
import "./chunk-GLOJMRT4.js";
|
|
6
6
|
import "./chunk-J5NMYSBJ.js";
|
|
7
7
|
|
|
8
8
|
// .docu/node/preview.node.ts
|
package/.docu/lib/server.deno.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runServer
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-TEXSBNMR.js";
|
|
4
|
+
import "./chunk-QGEFJH47.js";
|
|
5
|
+
import "./chunk-EOK6KATZ.js";
|
|
6
|
+
import "./chunk-GLOJMRT4.js";
|
|
7
7
|
import "./chunk-J5NMYSBJ.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/server.deno.ts
|
package/.docu/lib/server.node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runServer
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-TEXSBNMR.js";
|
|
4
|
+
import "./chunk-QGEFJH47.js";
|
|
5
|
+
import "./chunk-EOK6KATZ.js";
|
|
6
|
+
import "./chunk-GLOJMRT4.js";
|
|
7
7
|
import "./chunk-J5NMYSBJ.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/server.node.ts
|
package/.docu/node/build.impl.ts
CHANGED
|
@@ -26,7 +26,6 @@ import { htmlShell } from "./html.shared";
|
|
|
26
26
|
import { generateSearchIndex } from "./search-indexer";
|
|
27
27
|
import { buildClientBundle, computeInlineThemeCss } from "./hydrate.node";
|
|
28
28
|
import { logger } from "./logger";
|
|
29
|
-
import { generateBuildSummary } from "./build-summary";
|
|
30
29
|
import { initSentry, captureException } from "./sentry";
|
|
31
30
|
import { loadPlugins } from "./plugin-loader";
|
|
32
31
|
import { BuildPluginBuilder } from "./plugin-builder";
|
|
@@ -396,13 +395,6 @@ export async function runBuild(): Promise<void> {
|
|
|
396
395
|
logger.indexDone(indexCount, Math.round(performance.now() - t));
|
|
397
396
|
|
|
398
397
|
logger.routes();
|
|
399
|
-
console.log("");
|
|
400
|
-
|
|
401
|
-
const buildSummary = await generateBuildSummary(DIST_DIR);
|
|
402
|
-
if (buildSummary) {
|
|
403
|
-
console.log(buildSummary);
|
|
404
|
-
console.log("");
|
|
405
|
-
}
|
|
406
398
|
|
|
407
399
|
await writeCache(cache);
|
|
408
400
|
|
|
@@ -226,18 +226,7 @@ export async function buildClientBundle(): Promise<{ js: string; css: string }>
|
|
|
226
226
|
});
|
|
227
227
|
},
|
|
228
228
|
},
|
|
229
|
-
|
|
230
|
-
name: "mdx-jsx-runtime",
|
|
231
|
-
setup(build) {
|
|
232
|
-
build.onLoad({ filter: /next-mdx-remote[/\\].*jsx-runtime/ }, () => {
|
|
233
|
-
const source =
|
|
234
|
-
nodeEnv === "production"
|
|
235
|
-
? `module.exports.jsxRuntime = require("react/jsx-runtime");`
|
|
236
|
-
: `module.exports.jsxRuntime = require("react/jsx-dev-runtime");`;
|
|
237
|
-
return { contents: source, loader: "js" };
|
|
238
|
-
});
|
|
239
|
-
},
|
|
240
|
-
},
|
|
229
|
+
|
|
241
230
|
],
|
|
242
231
|
});
|
|
243
232
|
} finally {
|
package/.docu/node/hydrate.ts
CHANGED
|
@@ -92,18 +92,7 @@ export async function buildClientBundle(): Promise<{ js: string; css: string }>
|
|
|
92
92
|
});
|
|
93
93
|
},
|
|
94
94
|
},
|
|
95
|
-
|
|
96
|
-
name: "mdx-jsx-runtime",
|
|
97
|
-
setup(build) {
|
|
98
|
-
build.onLoad({ filter: /next-mdx-remote[/\\].*jsx-runtime/ }, () => {
|
|
99
|
-
const source =
|
|
100
|
-
nodeEnv === "production"
|
|
101
|
-
? `module.exports.jsxRuntime = require("react/jsx-runtime");`
|
|
102
|
-
: `module.exports.jsxRuntime = require("react/jsx-dev-runtime");`;
|
|
103
|
-
return { contents: source, loader: "js" };
|
|
104
|
-
});
|
|
105
|
-
},
|
|
106
|
-
},
|
|
95
|
+
|
|
107
96
|
],
|
|
108
97
|
});
|
|
109
98
|
|
package/.docu/node/security.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
1
2
|
import { resolve } from "node:path";
|
|
2
3
|
import { realpathSync } from "node:fs";
|
|
3
4
|
|
|
@@ -10,7 +11,7 @@ export const SECURITY_HEADERS: Record<string, string> = {
|
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export function generateNonce(): string {
|
|
13
|
-
return
|
|
14
|
+
return randomBytes(16).toString("base64");
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function cspHeader(nonce: string, allowEval = false): string {
|
|
@@ -117,11 +118,16 @@ export function wrapPluginResponse(
|
|
|
117
118
|
securedHeaders.set(key, value);
|
|
118
119
|
}
|
|
119
120
|
}
|
|
121
|
+
let body = pluginResponse.body;
|
|
120
122
|
const contentType = securedHeaders.get("Content-Type") || "";
|
|
121
123
|
if (contentType.includes("text/html") && !securedHeaders.has("Content-Security-Policy")) {
|
|
122
|
-
|
|
124
|
+
const nonce = generateNonce();
|
|
125
|
+
securedHeaders.set("Content-Security-Policy", cspHeader(nonce, allowEval));
|
|
126
|
+
if (typeof body === "string") {
|
|
127
|
+
body = injectNonce(body, nonce);
|
|
128
|
+
}
|
|
123
129
|
}
|
|
124
|
-
return new Response(
|
|
130
|
+
return new Response(body, {
|
|
125
131
|
status: pluginResponse.status,
|
|
126
132
|
statusText: pluginResponse.statusText,
|
|
127
133
|
headers: securedHeaders,
|
package/.docu/node/sentry.ts
CHANGED
package/bin/cli.js
CHANGED
|
@@ -9,9 +9,13 @@ import { spawnSync } from "node:child_process";
|
|
|
9
9
|
const __dirname = import.meta.dirname;
|
|
10
10
|
|
|
11
11
|
// Runtime detection — override with FLAME_RUNTIME=bun|node|deno for testing.
|
|
12
|
+
// Deno's npm compat layer may expose `Bun` via globals, check execPath first.
|
|
12
13
|
const runtime =
|
|
13
14
|
process.env.FLAME_RUNTIME ||
|
|
14
|
-
(
|
|
15
|
+
(process.execPath.includes("deno") ? "deno" :
|
|
16
|
+
typeof Bun !== "undefined" ? "bun" :
|
|
17
|
+
typeof Deno !== "undefined" ? "deno" :
|
|
18
|
+
"node");
|
|
15
19
|
|
|
16
20
|
const COMMAND_MAP = {
|
|
17
21
|
bun: {
|
|
@@ -107,13 +111,19 @@ if (command === "init") {
|
|
|
107
111
|
preview: `${flameCmd} preview`,
|
|
108
112
|
deploy: `${flameCmd} deploy`,
|
|
109
113
|
};
|
|
114
|
+
|
|
115
|
+
// Generate deno.json with nodeModulesDir for npm compat.
|
|
116
|
+
const denoConfig = {
|
|
117
|
+
nodeModulesDir: "auto",
|
|
118
|
+
};
|
|
119
|
+
writeFileSync(join(targetDir, "deno.json"), JSON.stringify(denoConfig, null, 2) + "\n");
|
|
110
120
|
}
|
|
111
121
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
112
122
|
|
|
113
123
|
const nextSteps = {
|
|
114
124
|
bun: " bun install\n bun run dev",
|
|
115
125
|
node: " npm install\n npm run dev",
|
|
116
|
-
deno: " deno task dev",
|
|
126
|
+
deno: " deno task dev\n\n ⚠️ If you see a freshness error, run:\n DENO_ALLOW_NEWER=true deno task dev",
|
|
117
127
|
}[runtime];
|
|
118
128
|
console.log(`\n ✓ Project scaffolded!\n\n Next steps:\n${nextSteps}\n`);
|
|
119
129
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docubook/flame",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
"react": "^19.2.7",
|
|
55
55
|
"react-dom": "^19.2.7",
|
|
56
56
|
"unified": "^11.0.0",
|
|
57
|
-
"@docubook/core": "^1.8.
|
|
58
|
-
"@docubook/mdx-content": "^3.4.
|
|
57
|
+
"@docubook/core": "^1.8.2",
|
|
58
|
+
"@docubook/mdx-content": "^3.4.3",
|
|
59
|
+
"@docubook/themes-colors": "^1.0.1",
|
|
59
60
|
"@docubook/runt": "^1.0.0",
|
|
60
|
-
"@docubook/themes-colors": "^1.0.0",
|
|
61
61
|
"@docubook/ui-react": "^1.0.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
@@ -73,13 +73,11 @@
|
|
|
73
73
|
"@types/node": "^22.0.0",
|
|
74
74
|
"@types/react": "^19.0.0",
|
|
75
75
|
"@types/react-dom": "^19.0.0",
|
|
76
|
-
"autoprefixer": "^10.4.0",
|
|
77
76
|
"bun-types": "^1.3.0",
|
|
78
77
|
"eslint": "^10.3.0",
|
|
79
78
|
"eslint-plugin-perfectionist": "^5.9.0",
|
|
80
79
|
"eslint-plugin-react": "^7.37.5",
|
|
81
80
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
82
|
-
"postcss": "^8.4.0",
|
|
83
81
|
"tailwindcss": "^4.3.0",
|
|
84
82
|
"typescript": "^5.9.3",
|
|
85
83
|
"typescript-eslint": "^8.59.2"
|
|
@@ -8,7 +8,7 @@ Flame produces **flat static HTML** — no server, no database, no runtime requi
|
|
|
8
8
|
## GitHub Pages
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
npm run deploy
|
|
11
|
+
npm run deploy # or: bun run deploy / deno task deploy
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
This builds the site, adds `.nojekyll`, and generates a GitHub Actions workflow on first run.
|
|
@@ -16,7 +16,7 @@ This builds the site, adds `.nojekyll`, and generates a GitHub Actions workflow
|
|
|
16
16
|
## Manual
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm run build
|
|
19
|
+
npm run build # or: bun run build / deno task build
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Upload `.docu/dist/` to any of:
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build output summary: walks `dist` after a build, collects generated files
|
|
3
|
-
* and their sizes, groups them by extension, and formats a scannable report
|
|
4
|
-
* for CI logs. Runtime-neutral — uses only `node:fs/promises` and `node:path`
|
|
5
|
-
* so it runs identically under Bun, Node, and Deno.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readdir, stat } from "node:fs/promises";
|
|
9
|
-
import { join, relative, extname } from "node:path";
|
|
10
|
-
import { existsSync } from "node:fs";
|
|
11
|
-
|
|
12
|
-
export interface BuildOutputFile {
|
|
13
|
-
path: string;
|
|
14
|
-
size: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface BuildOutputType {
|
|
18
|
-
ext: string;
|
|
19
|
-
count: number;
|
|
20
|
-
size: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface BuildOutput {
|
|
24
|
-
files: BuildOutputFile[];
|
|
25
|
-
byType: BuildOutputType[];
|
|
26
|
-
totalFiles: number;
|
|
27
|
-
totalSize: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const MAX_FILES_SHOWN = 10;
|
|
31
|
-
const SEP = "─".repeat(60);
|
|
32
|
-
|
|
33
|
-
export function formatSize(bytes: number): string {
|
|
34
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
35
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
36
|
-
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function walkFiles(dir: string): Promise<BuildOutputFile[]> {
|
|
40
|
-
const out: BuildOutputFile[] = [];
|
|
41
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
const full = join(dir, entry.name);
|
|
44
|
-
if (entry.isDirectory()) {
|
|
45
|
-
out.push(...(await walkFiles(full)));
|
|
46
|
-
} else if (entry.isFile()) {
|
|
47
|
-
const s = await stat(full);
|
|
48
|
-
out.push({ path: full, size: s.size });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return out;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Collect build output metadata for `distDir`. Returns `null` if the dir is missing or empty. */
|
|
55
|
-
export async function collectBuildOutput(distDir: string): Promise<BuildOutput | null> {
|
|
56
|
-
if (!existsSync(distDir)) return null;
|
|
57
|
-
const all = await walkFiles(distDir);
|
|
58
|
-
if (all.length === 0) return null;
|
|
59
|
-
|
|
60
|
-
const files = all
|
|
61
|
-
.map((f) => ({ path: relative(distDir, f.path), size: f.size }))
|
|
62
|
-
.sort((a, b) => (b.size !== a.size ? b.size - a.size : a.path.localeCompare(b.path)));
|
|
63
|
-
|
|
64
|
-
const byExt = new Map<string, BuildOutputType>();
|
|
65
|
-
for (const f of files) {
|
|
66
|
-
const ext = extname(f.path) || "(none)";
|
|
67
|
-
const entry = byExt.get(ext) ?? { ext, count: 0, size: 0 };
|
|
68
|
-
entry.count++;
|
|
69
|
-
entry.size += f.size;
|
|
70
|
-
byExt.set(ext, entry);
|
|
71
|
-
}
|
|
72
|
-
const byType = [...byExt.values()].sort((a, b) =>
|
|
73
|
-
b.size !== a.size ? b.size - a.size : b.count - a.count
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
files,
|
|
78
|
-
byType,
|
|
79
|
-
totalFiles: files.length,
|
|
80
|
-
totalSize: files.reduce((sum, f) => sum + f.size, 0),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Format `output` as a human-readable, color-free summary suitable for CI logs. */
|
|
85
|
-
export function formatBuildSummary(output: BuildOutput): string {
|
|
86
|
-
const lines: string[] = [];
|
|
87
|
-
lines.push("📦 Build Output Summary:");
|
|
88
|
-
lines.push(SEP);
|
|
89
|
-
|
|
90
|
-
const shown = output.files.slice(0, MAX_FILES_SHOWN);
|
|
91
|
-
const nameWidth = Math.min(Math.max(...shown.map((f) => f.path.length), 1), 50);
|
|
92
|
-
for (const f of shown) {
|
|
93
|
-
lines.push(` ${f.path.padEnd(nameWidth)} ${formatSize(f.size).padStart(10)}`);
|
|
94
|
-
}
|
|
95
|
-
const remaining = output.totalFiles - shown.length;
|
|
96
|
-
if (remaining > 0) {
|
|
97
|
-
lines.push(` ... and ${remaining} more file${remaining === 1 ? "" : "s"}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
lines.push(SEP);
|
|
101
|
-
lines.push("📊 Summary by Type:");
|
|
102
|
-
const extWidth = Math.max(...output.byType.map((t) => t.ext.length), 1);
|
|
103
|
-
for (const t of output.byType) {
|
|
104
|
-
const plural = t.count === 1 ? "file" : "files";
|
|
105
|
-
lines.push(
|
|
106
|
-
` ${t.ext.padEnd(extWidth)} ${String(t.count).padStart(3)} ${plural} ${formatSize(
|
|
107
|
-
t.size
|
|
108
|
-
).padStart(10)}`
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
lines.push(SEP);
|
|
113
|
-
const totalPlural = output.totalFiles === 1 ? "file" : "files";
|
|
114
|
-
lines.push(
|
|
115
|
-
`📈 Total: ${output.totalFiles} ${totalPlural} ${formatSize(output.totalSize).padStart(10)}`
|
|
116
|
-
);
|
|
117
|
-
lines.push(SEP);
|
|
118
|
-
|
|
119
|
-
return lines.join("\n");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Collect and format the build summary for `distDir`. Returns `null` when there is nothing to report. */
|
|
123
|
-
export async function generateBuildSummary(distDir: string): Promise<string | null> {
|
|
124
|
-
const output = await collectBuildOutput(distDir);
|
|
125
|
-
return output ? formatBuildSummary(output) : null;
|
|
126
|
-
}
|