@docubook/flame 1.5.3 → 1.6.0

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.
Files changed (37) hide show
  1. package/.docu/lib/build.deno.js +4 -4
  2. package/.docu/lib/build.impl-N2MBT3CM.js +12 -0
  3. package/.docu/lib/build.node.js +4 -4
  4. package/.docu/lib/{chunk-J5NMYSBJ.js → chunk-4IQXHHPF.js} +5 -2
  5. package/.docu/lib/{chunk-YI76L7LB.js → chunk-IJFMQFWW.js} +69 -25
  6. package/.docu/lib/{chunk-YENO4SVR.js → chunk-JTH4JBBS.js} +38 -5
  7. package/.docu/lib/chunk-TI6M4643.js +204 -0
  8. package/.docu/lib/{chunk-EIKBL7SD.js → chunk-UUS45SUC.js} +2 -2
  9. package/.docu/lib/{chunk-WCMRAAWH.js → chunk-Y2TAEAUQ.js} +2 -2
  10. package/.docu/lib/{chunk-VX4HLVHE.js → chunk-ZA5QB5UK.js} +4 -4
  11. package/.docu/lib/clean.js +2 -2
  12. package/.docu/lib/deploy.deno.js +2 -2
  13. package/.docu/lib/deploy.node.js +2 -2
  14. package/.docu/lib/preview.deno.js +3 -3
  15. package/.docu/lib/preview.node.js +3 -3
  16. package/.docu/lib/server.deno.js +4 -4
  17. package/.docu/lib/server.node.js +4 -4
  18. package/.docu/node/build.impl.ts +15 -3
  19. package/.docu/node/build.ts +6 -0
  20. package/.docu/node/client.ts +38 -13
  21. package/.docu/node/deploy.shared.ts +147 -12
  22. package/.docu/node/deploy.ts +141 -30
  23. package/.docu/node/html.shared.ts +19 -0
  24. package/.docu/node/html.ts +17 -23
  25. package/.docu/node/hydrate.node.ts +74 -39
  26. package/.docu/node/hydrate.ts +74 -48
  27. package/.docu/node/paths.ts +7 -5
  28. package/.docu/node/seo.ts +42 -0
  29. package/.docu/node/server-routes.ts +2 -1
  30. package/.docu/node/types.ts +2 -0
  31. package/bin/cli.js +19 -6
  32. package/docu.schema.json +5 -1
  33. package/package.json +4 -4
  34. package/template/docs/assets/images/og.png +0 -0
  35. package/template/docu.json +2 -1
  36. package/.docu/lib/build.impl-O3Z5XIGM.js +0 -12
  37. package/.docu/lib/chunk-7VXEOH7X.js +0 -79
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-YENO4SVR.js";
4
- import "./chunk-YI76L7LB.js";
3
+ } from "./chunk-JTH4JBBS.js";
4
+ import "./chunk-IJFMQFWW.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-WCMRAAWH.js";
7
- import "./chunk-J5NMYSBJ.js";
6
+ import "./chunk-Y2TAEAUQ.js";
7
+ import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.deno.ts
10
10
  await runBuildCli();
@@ -0,0 +1,12 @@
1
+ import {
2
+ runBuild,
3
+ runBuildCli
4
+ } from "./chunk-JTH4JBBS.js";
5
+ import "./chunk-IJFMQFWW.js";
6
+ import "./chunk-EOK6KATZ.js";
7
+ import "./chunk-Y2TAEAUQ.js";
8
+ import "./chunk-4IQXHHPF.js";
9
+ export {
10
+ runBuild,
11
+ runBuildCli
12
+ };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-YENO4SVR.js";
4
- import "./chunk-YI76L7LB.js";
3
+ } from "./chunk-JTH4JBBS.js";
4
+ import "./chunk-IJFMQFWW.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-WCMRAAWH.js";
7
- import "./chunk-J5NMYSBJ.js";
6
+ import "./chunk-Y2TAEAUQ.js";
7
+ import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.node.ts
10
10
  await runBuildCli();
@@ -16,10 +16,11 @@ var DOCS_DIR = join(PROJECT_ROOT, "docs");
16
16
  var DOCS_ASSETS_DIR = join(PROJECT_ROOT, "docs/assets");
17
17
  var DOCU_CONFIG_PATH = join(PROJECT_ROOT, "docu.json");
18
18
  var _config = null;
19
- async function cleanOldBundles() {
19
+ async function cleanOldBundles(preserve) {
20
20
  try {
21
21
  const files = await readdir(ASSETS_DIR);
22
22
  for (const file of files) {
23
+ if (preserve?.has(file)) continue;
23
24
  if (file.startsWith("client.") || file.startsWith("client-")) {
24
25
  await unlink(join(ASSETS_DIR, file));
25
26
  }
@@ -32,7 +33,9 @@ async function cleanOldBundles() {
32
33
  try {
33
34
  await rm(join(ASSETS_DIR, "chunks"), { recursive: true, force: true });
34
35
  } catch (err) {
35
- console.error("Failed to clean old chunks:", err.message);
36
+ if (err.code !== "ENOENT") {
37
+ console.warn("[flame] Failed to clean chunks dir:", err.message);
38
+ }
36
39
  }
37
40
  }
38
41
  function loadDocuConfig() {
@@ -15,7 +15,7 @@ import {
15
15
  STYLES_DIR,
16
16
  cleanOldBundles,
17
17
  loadDocuConfig
18
- } from "./chunk-J5NMYSBJ.js";
18
+ } from "./chunk-4IQXHHPF.js";
19
19
 
20
20
  // .docu/node/plugin-loader.ts
21
21
  import { resolve } from "node:path";
@@ -706,9 +706,29 @@ function resolveTailwindBin() {
706
706
  const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin.tailwindcss;
707
707
  return join2(dirname(pkgPath), binRel);
708
708
  }
709
- async function runTailwind(outputCss) {
709
+ function tailwindCacheKey() {
710
+ const globalsPath = join2(STYLES_DIR, "globals.css");
711
+ const globalsContent = existsSync(globalsPath) ? readFileSync(globalsPath, "utf-8") : "";
712
+ let themeSuffix = "";
713
+ try {
714
+ const themeColors = getThemeConfig();
715
+ if (themeColors) {
716
+ themeSuffix = JSON.stringify(themeColors);
717
+ }
718
+ } catch {
719
+ }
720
+ return createHash("md5").update(globalsContent + themeSuffix).digest("hex").slice(0, 16);
721
+ }
722
+ async function buildTailwindCss(key) {
723
+ const cachedFile = `client-${key}.css`;
724
+ const cachedPath = join2(ASSETS_DIR, cachedFile);
725
+ if (existsSync(cachedPath)) {
726
+ const content = readFileSync(cachedPath, "utf-8");
727
+ return { file: cachedFile, content };
728
+ }
729
+ const tmpCss = join2(ASSETS_DIR, `_tmp-${key}.css`);
710
730
  const bin = resolveTailwindBin();
711
- const twArgs = ["-i", join2(STYLES_DIR, "globals.css"), "-o", outputCss, "--minify"];
731
+ const twArgs = ["-i", join2(STYLES_DIR, "globals.css"), "-o", tmpCss, "--minify"];
712
732
  const isDeno = "Deno" in globalThis;
713
733
  const args = isDeno ? ["run", "-A", bin, ...twArgs] : [bin, ...twArgs];
714
734
  try {
@@ -718,6 +738,24 @@ async function runTailwind(outputCss) {
718
738
  throw new Error(`Tailwind CSS build failed:
719
739
  ${stderr}`, { cause: err });
720
740
  }
741
+ let cssContent = await readFile(tmpCss, "utf-8");
742
+ await unlink(tmpCss);
743
+ try {
744
+ const themeColors = getThemeConfig();
745
+ if (themeColors) {
746
+ cssContent = buildThemeCss(cssContent, themeColors);
747
+ }
748
+ } catch (err) {
749
+ console.warn(
750
+ `[flame] Failed to resolve theme config, falling back to globals.css only: ${err instanceof Error ? err.message : String(err)}`
751
+ );
752
+ }
753
+ const cssFile = `client-${key}.css`;
754
+ const outPath = join2(ASSETS_DIR, cssFile);
755
+ if (!existsSync(outPath)) {
756
+ await writeFile(outPath, cssContent);
757
+ }
758
+ return { file: cssFile, content: cssContent };
721
759
  }
722
760
  var NODE_BUILTINS_RE = new RegExp(
723
761
  `^(node:.*|${builtinModules.map((m) => m.replace(/\//g, "\\/")).join("|")})$`
@@ -749,7 +787,10 @@ function scanDirLucideIcons(dir, set) {
749
787
  }
750
788
  }
751
789
  }
752
- } catch {
790
+ } catch (err) {
791
+ console.warn(
792
+ `[flame] Failed to scan lucide icons: ${err instanceof Error ? err.message : String(err)}`
793
+ );
753
794
  }
754
795
  }
755
796
  function collectAllLucideIcons() {
@@ -768,7 +809,8 @@ function collectAllLucideIcons() {
768
809
  }
769
810
  async function buildClientBundle() {
770
811
  await mkdir(ASSETS_DIR, { recursive: true });
771
- await cleanOldBundles();
812
+ const twKey = tailwindCacheKey();
813
+ await cleanOldBundles(/* @__PURE__ */ new Set([`client-${twKey}.css`]));
772
814
  const nodeEnv = process.env.NODE_ENV || "development";
773
815
  const esbuild = await import("esbuild");
774
816
  const { build } = esbuild;
@@ -781,10 +823,8 @@ async function buildClientBundle() {
781
823
  bundle: true,
782
824
  outdir: ASSETS_DIR,
783
825
  entryNames: "client-[hash]",
784
- chunkNames: "chunks/[name]-[hash]",
785
826
  platform: "browser",
786
827
  format: "esm",
787
- splitting: true,
788
828
  minify: nodeEnv === "production",
789
829
  define: { "process.env.NODE_ENV": JSON.stringify(nodeEnv) },
790
830
  jsx: "automatic",
@@ -814,7 +854,7 @@ async function buildClientBundle() {
814
854
  }
815
855
  if (args.importer) {
816
856
  const normalized = normalizeImporterPath(args.importer);
817
- if (normalized.endsWith("/.docu/components/Lucide.tsx") || normalized.includes("/mdx-content/dist/")) {
857
+ if (normalized.includes("/mdx-content/dist/")) {
818
858
  return { path: getLucideRealEntry(), namespace: "file" };
819
859
  }
820
860
  }
@@ -862,23 +902,7 @@ async function buildClientBundle() {
862
902
  throw new Error("Client bundle produced no output files");
863
903
  }
864
904
  const jsFile = basename(jsOutput);
865
- const tmpCss = join2(ASSETS_DIR, "_tmp.css");
866
- await runTailwind(tmpCss);
867
- let cssContent = await readFile(tmpCss, "utf-8");
868
- try {
869
- const themeColors = getThemeConfig();
870
- if (themeColors) {
871
- cssContent = buildThemeCss(cssContent, themeColors);
872
- }
873
- } catch (err) {
874
- console.warn(
875
- `[flame] Failed to resolve theme config, falling back to globals.css only: ${err instanceof Error ? err.message : String(err)}`
876
- );
877
- }
878
- const cssHash = createHash("md5").update(cssContent).digest("hex").slice(0, 8);
879
- const cssFile = `client-${cssHash}.css`;
880
- await writeFile(join2(ASSETS_DIR, cssFile), cssContent);
881
- await unlink(tmpCss);
905
+ const { file: cssFile } = await buildTailwindCss(twKey);
882
906
  await writeFile(join2(ASSETS_DIR, "manifest.json"), JSON.stringify({ js: jsFile, css: cssFile }));
883
907
  return { js: jsFile, css: cssFile };
884
908
  }
@@ -2336,6 +2360,23 @@ function htmlShell(opts) {
2336
2360
  const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
2337
2361
  const assetPrefix = depthPrefix + "assets/";
2338
2362
  const resolvePath = (path) => path.startsWith("/") ? depthPrefix + path.slice(1) : path;
2363
+ let seoTags = "";
2364
+ if (opts.seo) {
2365
+ const s = opts.seo;
2366
+ const e = escapeHtml;
2367
+ seoTags = `
2368
+ <meta property="og:title" content="${e(title)}" />
2369
+ <meta property="og:description" content="${e(description)}" />
2370
+ <meta property="og:url" content="${e(s.url)}" />
2371
+ <meta property="og:type" content="website" />
2372
+ <meta property="og:site_name" content="${e(s.siteName)}" />
2373
+ <meta name="twitter:card" content="summary_large_image" />
2374
+ <link rel="canonical" href="${e(s.url)}" />`;
2375
+ if (s.image) {
2376
+ seoTags += `
2377
+ <meta property="og:image" content="${e(s.image)}" />`;
2378
+ }
2379
+ }
2339
2380
  return `<!DOCTYPE html>
2340
2381
  <html lang="en">
2341
2382
  <head>
@@ -2344,12 +2385,15 @@ function htmlShell(opts) {
2344
2385
  <title>${escapeHtml(title)}</title>
2345
2386
  <meta name="description" content="${escapeHtml(description)}">
2346
2387
  ${favicon ? `<link rel="icon" type="image/x-icon" href="${escapeHtml(resolvePath(favicon))}">` : ""}${themeStyle}
2388
+ <link rel="preload" href="${escapeHtml(assetPrefix + css)}" as="style">
2347
2389
  <link rel="stylesheet" href="${escapeHtml(assetPrefix + css)}">
2348
2390
  ${csp ? `<meta http-equiv="Content-Security-Policy" content="${escapeHtml(csp)}">` : ""}
2391
+ ${seoTags}
2349
2392
  <script${nonceAttr}>try{if(localStorage.getItem("theme")==="dark")document.documentElement.classList.add("dark")}catch(e){}</script>${headInjection}
2350
2393
  </head>
2351
2394
  <body>
2352
2395
  <div id="root">${body}</div>
2396
+ <link rel="modulepreload" href="${escapeHtml(assetPrefix + js)}">
2353
2397
  <script type="module"${nonceAttr} src="${escapeHtml(assetPrefix + js)}"></script>${extraScripts ? `
2354
2398
  ${extraScripts}` : ""}${bodyInjection}
2355
2399
  </body>
@@ -13,14 +13,15 @@ import {
13
13
  htmlShell,
14
14
  initSentry,
15
15
  loadPlugins
16
- } from "./chunk-YI76L7LB.js";
16
+ } from "./chunk-IJFMQFWW.js";
17
17
  import {
18
+ cspHeader,
18
19
  generateNonce,
19
20
  scanMdxFiles
20
21
  } from "./chunk-EOK6KATZ.js";
21
22
  import {
22
23
  logger
23
- } from "./chunk-WCMRAAWH.js";
24
+ } from "./chunk-Y2TAEAUQ.js";
24
25
  import {
25
26
  ASSETS_DIR,
26
27
  CACHE_FILE,
@@ -29,7 +30,7 @@ import {
29
30
  DOCS_DIR,
30
31
  PROJECT_ROOT,
31
32
  loadDocuConfig
32
- } from "./chunk-J5NMYSBJ.js";
33
+ } from "./chunk-4IQXHHPF.js";
33
34
 
34
35
  // .docu/node/build.impl.ts
35
36
  import { readFile, writeFile, mkdir, readdir, copyFile } from "node:fs/promises";
@@ -38,6 +39,27 @@ import { createHash } from "node:crypto";
38
39
  import { join, dirname } from "node:path";
39
40
  import React from "react";
40
41
  import { renderToString } from "react-dom/server";
42
+
43
+ // .docu/node/seo.ts
44
+ function buildSeoMeta(config, frontmatter, slug) {
45
+ const baseURL = config.meta?.baseURL?.replace(/\/+$/, "") || "";
46
+ const url = slug ? `${baseURL}/docs/${slug}` : `${baseURL}/`;
47
+ const result = {
48
+ url,
49
+ siteName: config.meta?.title || ""
50
+ };
51
+ const image = typeof frontmatter.image === "string" && frontmatter.image || config.meta?.ogImage;
52
+ if (image) {
53
+ try {
54
+ result.image = new URL(image, image.startsWith("/") ? baseURL : `${baseURL}/docs/`).href;
55
+ } catch {
56
+ result.image = image;
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+
62
+ // .docu/node/build.impl.ts
41
63
  function parseArgs() {
42
64
  const args = process.argv.slice(2);
43
65
  return {
@@ -123,11 +145,15 @@ ${msg}`, { cause: err });
123
145
  const bodyExtra = builder?.collectBody(ctx);
124
146
  const depth = slug ? slug.split("/").length : 1;
125
147
  const favicon = docuConfig.meta?.favicon || "/docs/assets/images/favicon.ico";
148
+ const seo = buildSeoMeta(docuConfig, frontmatter, slug || "");
149
+ const csp = cspHeader(nonce, process.env.NODE_ENV !== "production");
126
150
  let html = htmlShell({
127
151
  title,
128
152
  description,
129
153
  body,
130
154
  favicon,
155
+ seo,
156
+ csp,
131
157
  css: assetManifest.css,
132
158
  js: assetManifest.js,
133
159
  nonce,
@@ -298,14 +324,18 @@ async function runBuild() {
298
324
  }
299
325
  const landingPage = React.createElement(IndexPage);
300
326
  const landingFavicon = docuConfig.meta?.favicon || "/docs/assets/images/favicon.ico";
327
+ const landingSeo = buildSeoMeta(docuConfig, docuConfig.meta, "");
328
+ const landingNonce = generateNonce();
301
329
  const landingHtml = htmlShell({
302
330
  title: docuConfig.meta?.title || "DocuBook",
303
331
  description: docuConfig.meta?.description || "",
304
332
  body: renderToString(landingPage),
305
333
  favicon: landingFavicon,
334
+ seo: landingSeo,
335
+ csp: cspHeader(landingNonce, process.env.NODE_ENV !== "production"),
306
336
  css: assetManifest.css,
307
337
  js: assetManifest.js,
308
- nonce: generateNonce(),
338
+ nonce: landingNonce,
309
339
  themeCss: inlineThemeCss
310
340
  });
311
341
  await writeFile(join(DIST_DIR, "index.html"), landingHtml);
@@ -315,14 +345,17 @@ async function runBuild() {
315
345
  React.createElement(NotFoundPage)
316
346
  );
317
347
  const notFoundFavicon = docuConfig.meta?.favicon || "/docs/assets/images/favicon.ico";
348
+ const notFoundNonce = generateNonce();
318
349
  const notFoundHtml = htmlShell({
319
350
  title: "404 - Not Found",
320
351
  description: "",
321
352
  body: renderToString(notFoundPage),
322
353
  favicon: notFoundFavicon,
354
+ headExtra: ['<meta name="robots" content="noindex,follow">'],
355
+ csp: cspHeader(notFoundNonce, process.env.NODE_ENV !== "production"),
323
356
  css: assetManifest.css,
324
357
  js: assetManifest.js,
325
- nonce: generateNonce(),
358
+ nonce: notFoundNonce,
326
359
  themeCss: inlineThemeCss
327
360
  });
328
361
  await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
@@ -0,0 +1,204 @@
1
+ import {
2
+ DIST_DIR,
3
+ PROJECT_ROOT
4
+ } from "./chunk-4IQXHHPF.js";
5
+
6
+ // .docu/node/deploy.shared.ts
7
+ import { writeFile, mkdir } from "node:fs/promises";
8
+ import { existsSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ var WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
11
+ var WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
12
+ var HEADERS_FILE = `/*
13
+ X-Frame-Options: DENY
14
+ X-Content-Type-Options: nosniff
15
+ Referrer-Policy: strict-origin-when-cross-origin
16
+
17
+ /assets/*
18
+ Cache-Control: public, max-age=31536000, immutable
19
+ `;
20
+ var isDocker = !!process.env.FLAME_DEPLOY_DOCKER;
21
+ var isSilent = !!process.env.FLAME_DEPLOY_SILENT;
22
+ var log = isSilent ? { info: () => {
23
+ }, ok: () => {
24
+ }, created: () => {
25
+ }, out: () => {
26
+ } } : {
27
+ info: (m) => console.log(m),
28
+ ok: () => console.log("\n\u2705 Ready to deploy!"),
29
+ created: (m) => console.log(m),
30
+ out: (m) => console.log(m)
31
+ };
32
+ async function runBuild() {
33
+ process.env.NODE_ENV = "production";
34
+ if (isSilent) {
35
+ process.env.FLAME_BUILD_SILENT = "1";
36
+ process.env.LOG_LEVEL = "error";
37
+ }
38
+ const { runBuildCli } = await import("./build.impl-N2MBT3CM.js");
39
+ await runBuildCli();
40
+ }
41
+ async function writeDockerFiles() {
42
+ const dockerDir = PROJECT_ROOT;
43
+ if (!existsSync(join(dockerDir, "Dockerfile"))) {
44
+ await writeFile(
45
+ join(dockerDir, "Dockerfile"),
46
+ `FROM node:22-alpine AS builder
47
+ WORKDIR /app
48
+ COPY package.json package-lock.json ./
49
+ RUN npm ci
50
+ COPY . .
51
+ RUN npm run build
52
+
53
+ FROM nginx:alpine
54
+ COPY --from=builder /app/.docu/dist /usr/share/nginx/html
55
+ COPY nginx.conf /etc/nginx/conf.d/default.conf
56
+ USER nginx
57
+ EXPOSE 80
58
+ CMD ["nginx", "-g", "daemon off;"]
59
+ `
60
+ );
61
+ log.created("\u{1F4C4} Created Dockerfile");
62
+ }
63
+ if (!existsSync(join(dockerDir, "nginx.conf"))) {
64
+ await writeFile(
65
+ join(dockerDir, "nginx.conf"),
66
+ `server {
67
+ listen 80;
68
+ server_name _;
69
+ root /usr/share/nginx/html;
70
+ index index.html;
71
+
72
+ gzip on;
73
+ gzip_types text/html text/css application/javascript image/svg+xml;
74
+
75
+ # Security headers (HSTS effective when HTTPS is terminated upstream)
76
+ add_header X-Frame-Options "DENY" always;
77
+ add_header X-Content-Type-Options "nosniff" always;
78
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
79
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
80
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
81
+
82
+ location /assets/ {
83
+ expires 1y;
84
+ add_header Cache-Control "public, immutable";
85
+ add_header X-Frame-Options "DENY" always;
86
+ add_header X-Content-Type-Options "nosniff" always;
87
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
88
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
89
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
90
+ }
91
+
92
+ location /docs/assets/ {
93
+ expires 7d;
94
+ add_header Cache-Control "public";
95
+ add_header X-Frame-Options "DENY" always;
96
+ add_header X-Content-Type-Options "nosniff" always;
97
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
98
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
99
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
100
+ }
101
+
102
+ location / {
103
+ try_files $uri $uri.html $uri/ =404;
104
+ }
105
+ }
106
+ `
107
+ );
108
+ log.created("\u{1F4C4} Created nginx.conf");
109
+ }
110
+ if (!existsSync(join(dockerDir, ".dockerignore"))) {
111
+ await writeFile(
112
+ join(dockerDir, ".dockerignore"),
113
+ `node_modules
114
+ .git
115
+ *.DS_Store
116
+ .docu/dist
117
+ .docu/lib
118
+ .env
119
+ .env.*
120
+ .npmrc
121
+ *.log
122
+ `
123
+ );
124
+ log.created("\u{1F4C4} Created .dockerignore");
125
+ }
126
+ }
127
+ async function writeGhaWorkflow() {
128
+ if (!existsSync(WORKFLOW_FILE)) {
129
+ await mkdir(WORKFLOW_DIR, { recursive: true });
130
+ await writeFile(WORKFLOW_FILE, GITHUB_ACTIONS_WORKFLOW);
131
+ log.created("\u{1F4C4} Created .github/workflows/deploy.yml");
132
+ }
133
+ }
134
+ async function runDeploy() {
135
+ log.info("\u{1F4E6} Building for production...\n");
136
+ await runBuild();
137
+ await writeFile(join(DIST_DIR, ".nojekyll"), "");
138
+ await writeFile(join(DIST_DIR, "_headers"), HEADERS_FILE);
139
+ if (isDocker) {
140
+ await writeDockerFiles();
141
+ } else {
142
+ await writeGhaWorkflow();
143
+ }
144
+ log.ok();
145
+ log.out(" Output: .docu/dist/");
146
+ if (isDocker) {
147
+ log.out(" Run: docker build -t my-docs . && docker run -p 80:80 my-docs");
148
+ } else {
149
+ log.out(" Push to GitHub and enable Pages (Settings \u2192 Pages \u2192 Source: GitHub Actions)");
150
+ }
151
+ }
152
+ var GITHUB_ACTIONS_WORKFLOW = `name: Deploy to GitHub Pages
153
+
154
+ on:
155
+ push:
156
+ branches: [main]
157
+ workflow_dispatch:
158
+
159
+ permissions:
160
+ contents: read
161
+ pages: write
162
+ id-token: write
163
+
164
+ concurrency:
165
+ group: "pages"
166
+ cancel-in-progress: false
167
+
168
+ jobs:
169
+ build:
170
+ runs-on: ubuntu-latest
171
+ steps:
172
+ - uses: actions/checkout@v4
173
+ with:
174
+ fetch-depth: 0
175
+
176
+ - uses: actions/setup-node@v4
177
+ with:
178
+ node-version: 22
179
+
180
+ - run: npm install
181
+
182
+ - run: npm run build
183
+
184
+ - name: Add .nojekyll
185
+ run: touch .docu/dist/.nojekyll
186
+
187
+ - uses: actions/upload-pages-artifact@v3
188
+ with:
189
+ path: .docu/dist
190
+
191
+ deploy:
192
+ environment:
193
+ name: github-pages
194
+ url: \${{ steps.deployment.outputs.page_url }}
195
+ runs-on: ubuntu-latest
196
+ needs: build
197
+ steps:
198
+ - id: deployment
199
+ uses: actions/deploy-pages@v4
200
+ `;
201
+
202
+ export {
203
+ runDeploy
204
+ };
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-EOK6KATZ.js";
8
8
  import {
9
9
  logger
10
- } from "./chunk-WCMRAAWH.js";
10
+ } from "./chunk-Y2TAEAUQ.js";
11
11
  import {
12
12
  DIST_DIR
13
- } from "./chunk-J5NMYSBJ.js";
13
+ } from "./chunk-4IQXHHPF.js";
14
14
 
15
15
  // .docu/node/preview.impl.ts
16
16
  import { existsSync, statSync, readFileSync } from "node:fs";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  loadDocuConfig
3
- } from "./chunk-J5NMYSBJ.js";
3
+ } from "./chunk-4IQXHHPF.js";
4
4
 
5
5
  // package.json
6
6
  var package_default = {
7
7
  name: "@docubook/flame",
8
- version: "1.5.3",
8
+ version: "1.6.0",
9
9
  description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
10
10
  type: "module",
11
11
  bin: {
@@ -14,7 +14,7 @@ import {
14
14
  htmlShell,
15
15
  initSentry,
16
16
  loadPlugins
17
- } from "./chunk-YI76L7LB.js";
17
+ } from "./chunk-IJFMQFWW.js";
18
18
  import {
19
19
  SECURITY_HEADERS,
20
20
  generateNonce,
@@ -27,13 +27,13 @@ import {
27
27
  } from "./chunk-EOK6KATZ.js";
28
28
  import {
29
29
  logger
30
- } from "./chunk-WCMRAAWH.js";
30
+ } from "./chunk-Y2TAEAUQ.js";
31
31
  import {
32
32
  DIST_DIR,
33
33
  DOCS_DIR,
34
34
  PROJECT_ROOT,
35
35
  loadDocuConfig
36
- } from "./chunk-J5NMYSBJ.js";
36
+ } from "./chunk-4IQXHHPF.js";
37
37
 
38
38
  // .docu/node/server.impl.ts
39
39
  import { watch } from "node:fs";
@@ -240,7 +240,7 @@ function serveStatic(pathname) {
240
240
  }
241
241
  function serverErrorResponse(error) {
242
242
  const msg = error instanceof Error ? error.message : "Unknown error";
243
- const st = error instanceof Error ? error.stack : void 0;
243
+ const st = process.env.NODE_ENV !== "production" && error instanceof Error ? error.stack : void 0;
244
244
  return new Response(errorHtml(msg, st), {
245
245
  status: 500,
246
246
  headers: {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-WCMRAAWH.js";
4
- import "./chunk-J5NMYSBJ.js";
3
+ } from "./chunk-Y2TAEAUQ.js";
4
+ import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/clean.ts
7
7
  import { rm } from "node:fs/promises";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runDeploy
3
- } from "./chunk-7VXEOH7X.js";
4
- import "./chunk-J5NMYSBJ.js";
3
+ } from "./chunk-TI6M4643.js";
4
+ import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/deploy.deno.ts
7
7
  try {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  runDeploy
3
- } from "./chunk-7VXEOH7X.js";
4
- import "./chunk-J5NMYSBJ.js";
3
+ } from "./chunk-TI6M4643.js";
4
+ import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/deploy.node.ts
7
7
  runDeploy().catch((err) => {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runPreview
3
- } from "./chunk-EIKBL7SD.js";
3
+ } from "./chunk-UUS45SUC.js";
4
4
  import "./chunk-EOK6KATZ.js";
5
- import "./chunk-WCMRAAWH.js";
6
- import "./chunk-J5NMYSBJ.js";
5
+ import "./chunk-Y2TAEAUQ.js";
6
+ import "./chunk-4IQXHHPF.js";
7
7
 
8
8
  // .docu/node/preview.deno.ts
9
9
  import { denoAdapter } from "@docubook/runt";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runPreview
3
- } from "./chunk-EIKBL7SD.js";
3
+ } from "./chunk-UUS45SUC.js";
4
4
  import "./chunk-EOK6KATZ.js";
5
- import "./chunk-WCMRAAWH.js";
6
- import "./chunk-J5NMYSBJ.js";
5
+ import "./chunk-Y2TAEAUQ.js";
6
+ import "./chunk-4IQXHHPF.js";
7
7
 
8
8
  // .docu/node/preview.node.ts
9
9
  import { nodeAdapter } from "@docubook/runt";