@docubook/flame 1.6.0 → 1.6.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.
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-JTH4JBBS.js";
4
- import "./chunk-IJFMQFWW.js";
3
+ } from "./chunk-OREEWUOV.js";
4
+ import "./chunk-WSJC3PRH.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-Y2TAEAUQ.js";
6
+ import "./chunk-TU7G4RJV.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.deno.ts
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runBuild,
3
3
  runBuildCli
4
- } from "./chunk-JTH4JBBS.js";
5
- import "./chunk-IJFMQFWW.js";
4
+ } from "./chunk-OREEWUOV.js";
5
+ import "./chunk-WSJC3PRH.js";
6
6
  import "./chunk-EOK6KATZ.js";
7
- import "./chunk-Y2TAEAUQ.js";
7
+ import "./chunk-TU7G4RJV.js";
8
8
  import "./chunk-4IQXHHPF.js";
9
9
  export {
10
10
  runBuild,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-JTH4JBBS.js";
4
- import "./chunk-IJFMQFWW.js";
3
+ } from "./chunk-OREEWUOV.js";
4
+ import "./chunk-WSJC3PRH.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-Y2TAEAUQ.js";
6
+ import "./chunk-TU7G4RJV.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.node.ts
@@ -14,7 +14,7 @@ import {
14
14
  htmlShell,
15
15
  initSentry,
16
16
  loadPlugins
17
- } from "./chunk-IJFMQFWW.js";
17
+ } from "./chunk-WSJC3PRH.js";
18
18
  import {
19
19
  SECURITY_HEADERS,
20
20
  generateNonce,
@@ -27,7 +27,7 @@ import {
27
27
  } from "./chunk-EOK6KATZ.js";
28
28
  import {
29
29
  logger
30
- } from "./chunk-Y2TAEAUQ.js";
30
+ } from "./chunk-TU7G4RJV.js";
31
31
  import {
32
32
  DIST_DIR,
33
33
  DOCS_DIR,
@@ -9,6 +9,98 @@ import { existsSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  var WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
11
11
  var WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
12
+ var NGINX_CONF = `server {
13
+ listen 80;
14
+ server_name _;
15
+ root /usr/share/nginx/html;
16
+ index index.html;
17
+
18
+ gzip on;
19
+ gzip_types text/html text/css application/javascript image/svg+xml;
20
+
21
+ # Security headers (HSTS effective when HTTPS is terminated upstream)
22
+ add_header X-Frame-Options "DENY" always;
23
+ add_header X-Content-Type-Options "nosniff" always;
24
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
25
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
26
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
27
+
28
+ location /assets/ {
29
+ expires 1y;
30
+ add_header Cache-Control "public, immutable";
31
+ add_header X-Frame-Options "DENY" always;
32
+ add_header X-Content-Type-Options "nosniff" always;
33
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
34
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
35
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
36
+ }
37
+
38
+ location /docs/assets/ {
39
+ expires 7d;
40
+ add_header Cache-Control "public";
41
+ add_header X-Frame-Options "DENY" always;
42
+ add_header X-Content-Type-Options "nosniff" always;
43
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
44
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
45
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
46
+ }
47
+
48
+ location / {
49
+ try_files $uri $uri.html $uri/ =404;
50
+ }
51
+ }
52
+ `;
53
+ var DOCKERIGNORE = `node_modules
54
+ .git
55
+ *.DS_Store
56
+ .docu/dist
57
+ .docu/lib
58
+ .env
59
+ .env.*
60
+ .npmrc
61
+ *.log
62
+ `;
63
+ function detectPkgManager(dir) {
64
+ const bunLockFile = existsSync(join(dir, "bun.lock")) ? "bun.lock" : existsSync(join(dir, "bun.lockb")) ? "bun.lockb" : null;
65
+ if (bunLockFile) {
66
+ return {
67
+ baseImage: "oven/bun:1",
68
+ lockFile: bunLockFile,
69
+ installCmd: "bun install --frozen-lockfile",
70
+ runCmd: "bun",
71
+ cache: "",
72
+ setupAction: "bun"
73
+ };
74
+ }
75
+ if (existsSync(join(dir, "pnpm-lock.yaml"))) {
76
+ return {
77
+ baseImage: "node:22-alpine",
78
+ lockFile: "pnpm-lock.yaml",
79
+ installCmd: "corepack enable && pnpm install --frozen-lockfile",
80
+ runCmd: "pnpm",
81
+ cache: "pnpm",
82
+ setupAction: "pnpm"
83
+ };
84
+ }
85
+ if (existsSync(join(dir, "yarn.lock"))) {
86
+ return {
87
+ baseImage: "node:22-alpine",
88
+ lockFile: "yarn.lock",
89
+ installCmd: "yarn install --frozen-lockfile",
90
+ runCmd: "yarn",
91
+ cache: "yarn",
92
+ setupAction: "node"
93
+ };
94
+ }
95
+ return {
96
+ baseImage: "node:22-alpine",
97
+ lockFile: "package-lock.json",
98
+ installCmd: "npm ci",
99
+ runCmd: "npm",
100
+ cache: "npm",
101
+ setupAction: "node"
102
+ };
103
+ }
12
104
  var HEADERS_FILE = `/*
13
105
  X-Frame-Options: DENY
14
106
  X-Content-Type-Options: nosniff
@@ -35,20 +127,21 @@ async function runBuild() {
35
127
  process.env.FLAME_BUILD_SILENT = "1";
36
128
  process.env.LOG_LEVEL = "error";
37
129
  }
38
- const { runBuildCli } = await import("./build.impl-N2MBT3CM.js");
130
+ const { runBuildCli } = await import("./build.impl-CFTUVIX7.js");
39
131
  await runBuildCli();
40
132
  }
41
133
  async function writeDockerFiles() {
42
134
  const dockerDir = PROJECT_ROOT;
135
+ const pm = detectPkgManager(dockerDir);
43
136
  if (!existsSync(join(dockerDir, "Dockerfile"))) {
44
137
  await writeFile(
45
138
  join(dockerDir, "Dockerfile"),
46
- `FROM node:22-alpine AS builder
139
+ `FROM ${pm.baseImage} AS builder
47
140
  WORKDIR /app
48
- COPY package.json package-lock.json ./
49
- RUN npm ci
141
+ COPY package.json ${pm.lockFile} ./
142
+ RUN ${pm.installCmd}
50
143
  COPY . .
51
- RUN npm run build
144
+ RUN ${pm.runCmd} run build
52
145
 
53
146
  FROM nginx:alpine
54
147
  COPY --from=builder /app/.docu/dist /usr/share/nginx/html
@@ -61,73 +154,96 @@ CMD ["nginx", "-g", "daemon off;"]
61
154
  log.created("\u{1F4C4} Created Dockerfile");
62
155
  }
63
156
  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;
157
+ await writeFile(join(dockerDir, "nginx.conf"), NGINX_CONF);
158
+ log.created("\u{1F4C4} Created nginx.conf");
100
159
  }
101
-
102
- location / {
103
- try_files $uri $uri.html $uri/ =404;
160
+ if (!existsSync(join(dockerDir, ".dockerignore"))) {
161
+ await writeFile(join(dockerDir, ".dockerignore"), DOCKERIGNORE);
162
+ log.created("\u{1F4C4} Created .dockerignore");
104
163
  }
105
164
  }
106
- `
165
+ function generateWorkflowYml() {
166
+ const pm = detectPkgManager(PROJECT_ROOT);
167
+ const setupSteps = [
168
+ " - uses: actions/checkout@v7",
169
+ " with:",
170
+ " fetch-depth: 0",
171
+ ""
172
+ ];
173
+ if (pm.setupAction === "bun") {
174
+ setupSteps.push(
175
+ " - uses: oven-sh/setup-bun@v2",
176
+ " with:",
177
+ " bun-version: latest",
178
+ ""
107
179
  );
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
- `
180
+ } else if (pm.setupAction === "pnpm") {
181
+ setupSteps.push(
182
+ " - uses: pnpm/action-setup@v6",
183
+ "",
184
+ " - uses: actions/setup-node@v6",
185
+ " with:",
186
+ " node-version: 22",
187
+ ` cache: ${pm.cache}`,
188
+ ""
189
+ );
190
+ } else {
191
+ setupSteps.push(
192
+ " - uses: actions/setup-node@v6",
193
+ " with:",
194
+ " node-version: 22",
195
+ ` cache: ${pm.cache}`,
196
+ ""
123
197
  );
124
- log.created("\u{1F4C4} Created .dockerignore");
125
198
  }
199
+ return [
200
+ `name: Deploy to GitHub Pages`,
201
+ "",
202
+ "on:",
203
+ " push:",
204
+ " branches: [main]",
205
+ " workflow_dispatch:",
206
+ "",
207
+ "permissions:",
208
+ " contents: read",
209
+ " pages: write",
210
+ " id-token: write",
211
+ "",
212
+ "concurrency:",
213
+ ' group: "pages"',
214
+ " cancel-in-progress: false",
215
+ "",
216
+ "jobs:",
217
+ " build:",
218
+ " runs-on: ubuntu-latest",
219
+ " steps:",
220
+ ...setupSteps,
221
+ ` - run: ${pm.installCmd}`,
222
+ "",
223
+ ` - run: ${pm.runCmd} run build`,
224
+ "",
225
+ " - name: Add .nojekyll",
226
+ " run: touch .docu/dist/.nojekyll",
227
+ "",
228
+ " - uses: actions/upload-pages-artifact@v3",
229
+ " with:",
230
+ " path: .docu/dist",
231
+ "",
232
+ " deploy:",
233
+ " environment:",
234
+ " name: github-pages",
235
+ " url: ${{ steps.deployment.outputs.page_url }}",
236
+ " runs-on: ubuntu-latest",
237
+ " needs: build",
238
+ " steps:",
239
+ " - id: deployment",
240
+ " uses: actions/deploy-pages@v4"
241
+ ].join("\n");
126
242
  }
127
243
  async function writeGhaWorkflow() {
128
244
  if (!existsSync(WORKFLOW_FILE)) {
129
245
  await mkdir(WORKFLOW_DIR, { recursive: true });
130
- await writeFile(WORKFLOW_FILE, GITHUB_ACTIONS_WORKFLOW);
246
+ await writeFile(WORKFLOW_FILE, generateWorkflowYml());
131
247
  log.created("\u{1F4C4} Created .github/workflows/deploy.yml");
132
248
  }
133
249
  }
@@ -149,55 +265,6 @@ async function runDeploy() {
149
265
  log.out(" Push to GitHub and enable Pages (Settings \u2192 Pages \u2192 Source: GitHub Actions)");
150
266
  }
151
267
  }
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
268
 
202
269
  export {
203
270
  runDeploy
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-EOK6KATZ.js";
8
8
  import {
9
9
  logger
10
- } from "./chunk-Y2TAEAUQ.js";
10
+ } from "./chunk-TU7G4RJV.js";
11
11
  import {
12
12
  DIST_DIR
13
13
  } from "./chunk-4IQXHHPF.js";
@@ -13,7 +13,7 @@ import {
13
13
  htmlShell,
14
14
  initSentry,
15
15
  loadPlugins
16
- } from "./chunk-IJFMQFWW.js";
16
+ } from "./chunk-WSJC3PRH.js";
17
17
  import {
18
18
  cspHeader,
19
19
  generateNonce,
@@ -21,7 +21,7 @@ import {
21
21
  } from "./chunk-EOK6KATZ.js";
22
22
  import {
23
23
  logger
24
- } from "./chunk-Y2TAEAUQ.js";
24
+ } from "./chunk-TU7G4RJV.js";
25
25
  import {
26
26
  ASSETS_DIR,
27
27
  CACHE_FILE,
@@ -5,7 +5,7 @@ import {
5
5
  // package.json
6
6
  var package_default = {
7
7
  name: "@docubook/flame",
8
- version: "1.6.0",
8
+ version: "1.6.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: {
@@ -103,8 +103,8 @@ var BuildPluginBuilder = class {
103
103
  _remarkPlugins = [];
104
104
  _transformFrontmatter = [];
105
105
  _transformHtml = [];
106
- constructor(config3) {
107
- this.config = config3;
106
+ constructor(config2) {
107
+ this.config = config2;
108
108
  }
109
109
  /**
110
110
  * Collect and deduplicate all `<body>` injection snippets from registered plugins.
@@ -646,8 +646,8 @@ function getThemeConfig() {
646
646
  if (process.env.FLAME_THEME) {
647
647
  return process.env.FLAME_THEME;
648
648
  }
649
- const config3 = loadDocuConfig();
650
- return config3.themes?.colors;
649
+ const config2 = loadDocuConfig();
650
+ return config2.themes?.colors;
651
651
  }
652
652
  function buildThemeCss(baseCss, themeConfig) {
653
653
  try {
@@ -683,19 +683,19 @@ import { existsSync, readFileSync, readdirSync as readdirSync2 } from "node:fs";
683
683
  import { promisify } from "node:util";
684
684
  import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
685
685
  import { createHash } from "node:crypto";
686
- function extractConfigIcons(config3) {
686
+ function extractConfigIcons(config2) {
687
687
  const icons = [];
688
688
  const pushIf = (s) => {
689
689
  if (s) icons.push(s);
690
690
  };
691
- config3.home?.hero?.actions?.forEach((a) => pushIf(a.icon));
692
- config3.home?.features?.forEach((f) => pushIf(f.icon));
691
+ config2.home?.hero?.actions?.forEach((a) => pushIf(a.icon));
692
+ config2.home?.features?.forEach((f) => pushIf(f.icon));
693
693
  (function walk(routes3) {
694
694
  for (const r of routes3) {
695
695
  pushIf(r.context?.icon);
696
696
  if (r.items) walk(r.items);
697
697
  }
698
- })(config3.routes ?? []);
698
+ })(config2.routes ?? []);
699
699
  return [...new Set(icons.filter((n) => /^[A-Z]/.test(n)))];
700
700
  }
701
701
  var execFileAsync = promisify(execFile);
@@ -874,17 +874,25 @@ async function buildClientBundle() {
874
874
  {
875
875
  name: "docu-config",
876
876
  setup(build2) {
877
- build2.onResolve({ filter: /docu\.json$/ }, (args) => ({
877
+ build2.onResolve({ filter: /client-routes$/ }, (args) => ({
878
878
  path: args.path,
879
- namespace: "docu-config"
879
+ namespace: "client-routes"
880
880
  }));
881
- build2.onLoad({ filter: /.*/, namespace: "docu-config" }, () => {
882
- const config3 = loadDocuConfig();
881
+ build2.onLoad({ filter: /.*/, namespace: "client-routes" }, () => {
882
+ const config2 = loadDocuConfig();
883
883
  const resolved = {
884
- ...config3,
885
- routes: resolveRoutes(config3.routes)
884
+ ...config2,
885
+ routes: resolveRoutes(config2.routes)
886
+ };
887
+ return {
888
+ contents: [
889
+ `import type { DocuRoute, DocuConfig } from "./types";`,
890
+ `const docuConfig = ${JSON.stringify(resolved)};`,
891
+ `export const routes = docuConfig.routes || [];`,
892
+ `export const config = docuConfig;`
893
+ ].join("\n"),
894
+ loader: "ts"
886
895
  };
887
- return { contents: JSON.stringify(resolved), loader: "json" };
888
896
  });
889
897
  }
890
898
  }
@@ -2028,15 +2036,10 @@ function Anchor({
2028
2036
  return /* @__PURE__ */ jsx15("a", { href, className: baseClasses, ...props, children });
2029
2037
  }
2030
2038
 
2031
- // docu-config-runtime:../../docu.json
2032
- import { readFileSync as readFileSync2 } from "node:fs";
2033
- import { join as join4 } from "node:path";
2034
- var config = JSON.parse(readFileSync2(join4(process.cwd(), "docu.json"), "utf-8"));
2035
- var docu_default = config;
2036
-
2037
2039
  // .docu/node/client-routes.ts
2038
- var routes2 = docu_default.routes || [];
2039
- var config2 = docu_default;
2040
+ var docuConfig5 = loadDocuConfig();
2041
+ var routes2 = resolveRoutes(docuConfig5.routes || []);
2042
+ var config = docuConfig5;
2040
2043
 
2041
2044
  // .docu/components/Sublink.tsx
2042
2045
  import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
@@ -2078,7 +2081,7 @@ function Sublink({
2078
2081
  }
2079
2082
  );
2080
2083
  if (level >= 1) {
2081
- if (config2.sidebar?.context === "separator") {
2084
+ if (config.sidebar?.context === "separator") {
2082
2085
  const sectionOffsets = { 1: 16, 2: 32 };
2083
2086
  let overlap = 14;
2084
2087
  for (let l = 1; l < level; l++) {
@@ -2187,7 +2190,7 @@ function Menu({ onNavigate, className = "", pathname, routes: routes3 = [] }) {
2187
2190
  () => pathname || (typeof window !== "undefined" ? window.location.pathname : "/docs")
2188
2191
  );
2189
2192
  if (!currentPath.startsWith("/docs")) return null;
2190
- const mode = config2.sidebar?.context || "dropdown";
2193
+ const mode = config.sidebar?.context || "dropdown";
2191
2194
  const navProps = {
2192
2195
  "aria-label": "Documentation navigation",
2193
2196
  className: cn("transition-all duration-200", className)
@@ -2249,7 +2252,7 @@ function Menu({ onNavigate, className = "", pathname, routes: routes3 = [] }) {
2249
2252
  }
2250
2253
 
2251
2254
  // .docu/components/DocsLayout.tsx
2252
- var docuConfig5 = loadDocuConfig();
2255
+ var docuConfig6 = loadDocuConfig();
2253
2256
  function DocsLayout({ children, repoUrl, pathname = "/docs" }) {
2254
2257
  return React2.createElement(
2255
2258
  "div",
@@ -2270,7 +2273,7 @@ function DocsLayout({ children, repoUrl, pathname = "/docs" }) {
2270
2273
  React2.createElement(
2271
2274
  "div",
2272
2275
  { className: "flex h-full flex-col overflow-y-auto px-4" },
2273
- React2.createElement(Menu, { pathname, routes: docuConfig5.routes || [] })
2276
+ React2.createElement(Menu, { pathname, routes: docuConfig6.routes || [] })
2274
2277
  )
2275
2278
  ),
2276
2279
  React2.createElement(
@@ -2282,7 +2285,7 @@ function DocsLayout({ children, repoUrl, pathname = "/docs" }) {
2282
2285
  React2.createElement(
2283
2286
  "nav",
2284
2287
  { className: "flex items-center gap-6 text-sm font-medium text-base-content/80" },
2285
- ...(docuConfig5.navbar?.menu || []).map((item) => {
2288
+ ...(docuConfig6.navbar?.menu || []).map((item) => {
2286
2289
  const isExternal = /^https?:\/\//.test(item.href);
2287
2290
  const isDocsActive = item.href === "/docs";
2288
2291
  return React2.createElement(
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-Y2TAEAUQ.js";
3
+ } from "./chunk-TU7G4RJV.js";
4
4
  import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/clean.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runDeploy
3
- } from "./chunk-TI6M4643.js";
3
+ } from "./chunk-KHRQUSNL.js";
4
4
  import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/deploy.deno.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runDeploy
3
- } from "./chunk-TI6M4643.js";
3
+ } from "./chunk-KHRQUSNL.js";
4
4
  import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/deploy.node.ts
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runPreview
3
- } from "./chunk-UUS45SUC.js";
3
+ } from "./chunk-N25ZVCKO.js";
4
4
  import "./chunk-EOK6KATZ.js";
5
- import "./chunk-Y2TAEAUQ.js";
5
+ import "./chunk-TU7G4RJV.js";
6
6
  import "./chunk-4IQXHHPF.js";
7
7
 
8
8
  // .docu/node/preview.deno.ts
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  runPreview
3
- } from "./chunk-UUS45SUC.js";
3
+ } from "./chunk-N25ZVCKO.js";
4
4
  import "./chunk-EOK6KATZ.js";
5
- import "./chunk-Y2TAEAUQ.js";
5
+ import "./chunk-TU7G4RJV.js";
6
6
  import "./chunk-4IQXHHPF.js";
7
7
 
8
8
  // .docu/node/preview.node.ts
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runServer
3
- } from "./chunk-ZA5QB5UK.js";
4
- import "./chunk-IJFMQFWW.js";
3
+ } from "./chunk-77NNQDFX.js";
4
+ import "./chunk-WSJC3PRH.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-Y2TAEAUQ.js";
6
+ import "./chunk-TU7G4RJV.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/server.deno.ts
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runServer
3
- } from "./chunk-ZA5QB5UK.js";
4
- import "./chunk-IJFMQFWW.js";
3
+ } from "./chunk-77NNQDFX.js";
4
+ import "./chunk-WSJC3PRH.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-Y2TAEAUQ.js";
6
+ import "./chunk-TU7G4RJV.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/server.node.ts
@@ -1,5 +1,7 @@
1
1
  import type { DocuRoute, DocuConfig } from "./types";
2
- import docuConfig from "../../docu.json" with { type: "json" };
2
+ import { loadDocuConfig } from "./paths";
3
+ import { resolveRoutes } from "./fs-scanner";
3
4
 
4
- export const routes: DocuRoute[] = docuConfig.routes || [];
5
+ const docuConfig = loadDocuConfig();
6
+ export const routes: DocuRoute[] = resolveRoutes(docuConfig.routes || []);
5
7
  export const config = docuConfig as unknown as DocuConfig;
@@ -16,6 +16,113 @@ import { DIST_DIR, PROJECT_ROOT } from "./paths";
16
16
  const WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
17
17
  const WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
18
18
 
19
+ export const NGINX_CONF = `server {
20
+ listen 80;
21
+ server_name _;
22
+ root /usr/share/nginx/html;
23
+ index index.html;
24
+
25
+ gzip on;
26
+ gzip_types text/html text/css application/javascript image/svg+xml;
27
+
28
+ # Security headers (HSTS effective when HTTPS is terminated upstream)
29
+ add_header X-Frame-Options "DENY" always;
30
+ add_header X-Content-Type-Options "nosniff" always;
31
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
32
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
33
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
34
+
35
+ location /assets/ {
36
+ expires 1y;
37
+ add_header Cache-Control "public, immutable";
38
+ add_header X-Frame-Options "DENY" always;
39
+ add_header X-Content-Type-Options "nosniff" always;
40
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
41
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
42
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
43
+ }
44
+
45
+ location /docs/assets/ {
46
+ expires 7d;
47
+ add_header Cache-Control "public";
48
+ add_header X-Frame-Options "DENY" always;
49
+ add_header X-Content-Type-Options "nosniff" always;
50
+ add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
51
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
52
+ add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
53
+ }
54
+
55
+ location / {
56
+ try_files $uri $uri.html $uri/ =404;
57
+ }
58
+ }
59
+ `;
60
+
61
+ export const DOCKERIGNORE = `node_modules
62
+ .git
63
+ *.DS_Store
64
+ .docu/dist
65
+ .docu/lib
66
+ .env
67
+ .env.*
68
+ .npmrc
69
+ *.log
70
+ `;
71
+
72
+ export function detectPkgManager(dir: string): {
73
+ baseImage: string;
74
+ lockFile: string;
75
+ installCmd: string;
76
+ runCmd: string;
77
+ cache: string;
78
+ setupAction: string;
79
+ } {
80
+ const bunLockFile = existsSync(join(dir, "bun.lock"))
81
+ ? "bun.lock"
82
+ : existsSync(join(dir, "bun.lockb"))
83
+ ? "bun.lockb"
84
+ : null;
85
+ if (bunLockFile) {
86
+ return {
87
+ baseImage: "oven/bun:1",
88
+ lockFile: bunLockFile,
89
+ installCmd: "bun install --frozen-lockfile",
90
+ runCmd: "bun",
91
+ cache: "",
92
+ setupAction: "bun",
93
+ };
94
+ }
95
+ if (existsSync(join(dir, "pnpm-lock.yaml"))) {
96
+ return {
97
+ baseImage: "node:22-alpine",
98
+ lockFile: "pnpm-lock.yaml",
99
+ installCmd: "corepack enable && pnpm install --frozen-lockfile",
100
+ runCmd: "pnpm",
101
+ cache: "pnpm",
102
+ setupAction: "pnpm",
103
+ };
104
+ }
105
+ if (existsSync(join(dir, "yarn.lock"))) {
106
+ return {
107
+ baseImage: "node:22-alpine",
108
+ lockFile: "yarn.lock",
109
+ installCmd: "yarn install --frozen-lockfile",
110
+ runCmd: "yarn",
111
+ cache: "yarn",
112
+ setupAction: "node",
113
+ };
114
+ }
115
+ // default: npm
116
+ return {
117
+ baseImage: "node:22-alpine",
118
+ lockFile: "package-lock.json",
119
+ installCmd: "npm ci",
120
+ runCmd: "npm",
121
+ cache: "npm",
122
+ setupAction: "node",
123
+ };
124
+ }
125
+
19
126
  export const HEADERS_FILE = `/*
20
127
  X-Frame-Options: DENY
21
128
  X-Content-Type-Options: nosniff
@@ -50,16 +157,17 @@ async function runBuild() {
50
157
 
51
158
  async function writeDockerFiles() {
52
159
  const dockerDir = PROJECT_ROOT;
160
+ const pm = detectPkgManager(dockerDir);
53
161
 
54
162
  if (!existsSync(join(dockerDir, "Dockerfile"))) {
55
163
  await writeFile(
56
164
  join(dockerDir, "Dockerfile"),
57
- `FROM node:22-alpine AS builder
165
+ `FROM ${pm.baseImage} AS builder
58
166
  WORKDIR /app
59
- COPY package.json package-lock.json ./
60
- RUN npm ci
167
+ COPY package.json ${pm.lockFile} ./
168
+ RUN ${pm.installCmd}
61
169
  COPY . .
62
- RUN npm run build
170
+ RUN ${pm.runCmd} run build
63
171
 
64
172
  FROM nginx:alpine
65
173
  COPY --from=builder /app/.docu/dist /usr/share/nginx/html
@@ -73,75 +181,103 @@ CMD ["nginx", "-g", "daemon off;"]
73
181
  }
74
182
 
75
183
  if (!existsSync(join(dockerDir, "nginx.conf"))) {
76
- await writeFile(
77
- join(dockerDir, "nginx.conf"),
78
- `server {
79
- listen 80;
80
- server_name _;
81
- root /usr/share/nginx/html;
82
- index index.html;
83
-
84
- gzip on;
85
- gzip_types text/html text/css application/javascript image/svg+xml;
86
-
87
- # Security headers (HSTS effective when HTTPS is terminated upstream)
88
- add_header X-Frame-Options "DENY" always;
89
- add_header X-Content-Type-Options "nosniff" always;
90
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
91
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
92
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
93
-
94
- location /assets/ {
95
- expires 1y;
96
- add_header Cache-Control "public, immutable";
97
- add_header X-Frame-Options "DENY" always;
98
- add_header X-Content-Type-Options "nosniff" always;
99
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
100
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
101
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
102
- }
103
-
104
- location /docs/assets/ {
105
- expires 7d;
106
- add_header Cache-Control "public";
107
- add_header X-Frame-Options "DENY" always;
108
- add_header X-Content-Type-Options "nosniff" always;
109
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
110
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
111
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
184
+ await writeFile(join(dockerDir, "nginx.conf"), NGINX_CONF);
185
+ log.created("📄 Created nginx.conf");
112
186
  }
113
187
 
114
- location / {
115
- try_files $uri $uri.html $uri/ =404;
188
+ if (!existsSync(join(dockerDir, ".dockerignore"))) {
189
+ await writeFile(join(dockerDir, ".dockerignore"), DOCKERIGNORE);
190
+ log.created("📄 Created .dockerignore");
116
191
  }
117
192
  }
118
- `
119
- );
120
- log.created("📄 Created nginx.conf");
121
- }
122
193
 
123
- if (!existsSync(join(dockerDir, ".dockerignore"))) {
124
- await writeFile(
125
- join(dockerDir, ".dockerignore"),
126
- `node_modules
127
- .git
128
- *.DS_Store
129
- .docu/dist
130
- .docu/lib
131
- .env
132
- .env.*
133
- .npmrc
134
- *.log
135
- `
194
+ function generateWorkflowYml(): string {
195
+ const pm = detectPkgManager(PROJECT_ROOT);
196
+
197
+ const setupSteps: string[] = [
198
+ " - uses: actions/checkout@v7",
199
+ " with:",
200
+ " fetch-depth: 0",
201
+ "",
202
+ ];
203
+
204
+ if (pm.setupAction === "bun") {
205
+ setupSteps.push(
206
+ " - uses: oven-sh/setup-bun@v2",
207
+ " with:",
208
+ " bun-version: latest",
209
+ ""
210
+ );
211
+ } else if (pm.setupAction === "pnpm") {
212
+ setupSteps.push(
213
+ " - uses: pnpm/action-setup@v6",
214
+ "",
215
+ " - uses: actions/setup-node@v6",
216
+ " with:",
217
+ " node-version: 22",
218
+ ` cache: ${pm.cache}`,
219
+ ""
220
+ );
221
+ } else {
222
+ // npm / yarn
223
+ setupSteps.push(
224
+ " - uses: actions/setup-node@v6",
225
+ " with:",
226
+ " node-version: 22",
227
+ ` cache: ${pm.cache}`,
228
+ ""
136
229
  );
137
- log.created("📄 Created .dockerignore");
138
230
  }
231
+
232
+ return [
233
+ `name: Deploy to GitHub Pages`,
234
+ "",
235
+ "on:",
236
+ " push:",
237
+ " branches: [main]",
238
+ " workflow_dispatch:",
239
+ "",
240
+ "permissions:",
241
+ " contents: read",
242
+ " pages: write",
243
+ " id-token: write",
244
+ "",
245
+ "concurrency:",
246
+ ' group: "pages"',
247
+ " cancel-in-progress: false",
248
+ "",
249
+ "jobs:",
250
+ " build:",
251
+ " runs-on: ubuntu-latest",
252
+ " steps:",
253
+ ...setupSteps,
254
+ ` - run: ${pm.installCmd}`,
255
+ "",
256
+ ` - run: ${pm.runCmd} run build`,
257
+ "",
258
+ " - name: Add .nojekyll",
259
+ " run: touch .docu/dist/.nojekyll",
260
+ "",
261
+ " - uses: actions/upload-pages-artifact@v3",
262
+ " with:",
263
+ " path: .docu/dist",
264
+ "",
265
+ " deploy:",
266
+ " environment:",
267
+ " name: github-pages",
268
+ " url: ${{ steps.deployment.outputs.page_url }}",
269
+ " runs-on: ubuntu-latest",
270
+ " needs: build",
271
+ " steps:",
272
+ " - id: deployment",
273
+ " uses: actions/deploy-pages@v4",
274
+ ].join("\n");
139
275
  }
140
276
 
141
277
  async function writeGhaWorkflow() {
142
278
  if (!existsSync(WORKFLOW_FILE)) {
143
279
  await mkdir(WORKFLOW_DIR, { recursive: true });
144
- await writeFile(WORKFLOW_FILE, GITHUB_ACTIONS_WORKFLOW);
280
+ await writeFile(WORKFLOW_FILE, generateWorkflowYml());
145
281
  log.created("📄 Created .github/workflows/deploy.yml");
146
282
  }
147
283
  }
@@ -168,53 +304,3 @@ export async function runDeploy(): Promise<void> {
168
304
  log.out(" Push to GitHub and enable Pages (Settings → Pages → Source: GitHub Actions)");
169
305
  }
170
306
  }
171
-
172
- const GITHUB_ACTIONS_WORKFLOW = `name: Deploy to GitHub Pages
173
-
174
- on:
175
- push:
176
- branches: [main]
177
- workflow_dispatch:
178
-
179
- permissions:
180
- contents: read
181
- pages: write
182
- id-token: write
183
-
184
- concurrency:
185
- group: "pages"
186
- cancel-in-progress: false
187
-
188
- jobs:
189
- build:
190
- runs-on: ubuntu-latest
191
- steps:
192
- - uses: actions/checkout@v4
193
- with:
194
- fetch-depth: 0
195
-
196
- - uses: actions/setup-node@v4
197
- with:
198
- node-version: 22
199
-
200
- - run: npm install
201
-
202
- - run: npm run build
203
-
204
- - name: Add .nojekyll
205
- run: touch .docu/dist/.nojekyll
206
-
207
- - uses: actions/upload-pages-artifact@v3
208
- with:
209
- path: .docu/dist
210
-
211
- deploy:
212
- environment:
213
- name: github-pages
214
- url: \${{ steps.deployment.outputs.page_url }}
215
- runs-on: ubuntu-latest
216
- needs: build
217
- steps:
218
- - id: deployment
219
- uses: actions/deploy-pages@v4
220
- `;
@@ -11,9 +11,9 @@ import { mkdir } from "node:fs/promises";
11
11
  import { existsSync } from "node:fs";
12
12
  import { join } from "node:path";
13
13
  import { DIST_DIR, PROJECT_ROOT } from "./paths";
14
- import { HEADERS_FILE } from "./deploy.shared";
14
+ import { HEADERS_FILE, NGINX_CONF, DOCKERIGNORE } from "./deploy.shared";
15
15
 
16
- export { HEADERS_FILE };
16
+ export { HEADERS_FILE, NGINX_CONF, DOCKERIGNORE };
17
17
 
18
18
  const WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
19
19
  const WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
@@ -43,49 +43,6 @@ async function runBuild() {
43
43
  }
44
44
  }
45
45
 
46
- export const NGINX_CONF = `server {
47
- listen 80;
48
- server_name _;
49
- root /usr/share/nginx/html;
50
- index index.html;
51
-
52
- gzip on;
53
- gzip_types text/html text/css application/javascript image/svg+xml;
54
-
55
- # Security headers (server-level, inherited by all locations)
56
- # HSTS effective when HTTPS is terminated upstream (reverse proxy / LB)
57
- add_header X-Frame-Options "DENY" always;
58
- add_header X-Content-Type-Options "nosniff" always;
59
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
60
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
61
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
62
-
63
- location /assets/ {
64
- expires 1y;
65
- add_header Cache-Control "public, immutable";
66
- add_header X-Frame-Options "DENY" always;
67
- add_header X-Content-Type-Options "nosniff" always;
68
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
69
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
70
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
71
- }
72
-
73
- location /docs/assets/ {
74
- expires 7d;
75
- add_header Cache-Control "public";
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
-
83
- location / {
84
- try_files $uri $uri.html $uri/ =404;
85
- }
86
- }
87
- `;
88
-
89
46
  export const DOCKERFILE_BUN = `FROM oven/bun:1 AS builder
90
47
  WORKDIR /app
91
48
  COPY package.json bun.lock ./
@@ -101,17 +58,6 @@ EXPOSE 80
101
58
  CMD ["nginx", "-g", "daemon off;"]
102
59
  `;
103
60
 
104
- export const DOCKERIGNORE = `node_modules
105
- .git
106
- *.DS_Store
107
- .docu/dist
108
- .docu/lib
109
- .env
110
- .env.*
111
- .npmrc
112
- *.log
113
- `;
114
-
115
61
  async function writeDockerFiles() {
116
62
  const dockerDir = PROJECT_ROOT;
117
63
 
@@ -182,7 +128,7 @@ jobs:
182
128
  build:
183
129
  runs-on: ubuntu-latest
184
130
  steps:
185
- - uses: actions/checkout@v4
131
+ - uses: actions/checkout@v7
186
132
  with:
187
133
  fetch-depth: 0
188
134
 
@@ -270,17 +270,27 @@ export async function buildClientBundle(): Promise<{ js: string; css: string }>
270
270
  {
271
271
  name: "docu-config",
272
272
  setup(build) {
273
- build.onResolve({ filter: /docu\.json$/ }, (args) => ({
273
+ // Components import as "../node/client-routes" (no .ts extension),
274
+ // so filter matches the path tail without requiring the extension.
275
+ build.onResolve({ filter: /client-routes$/ }, (args) => ({
274
276
  path: args.path,
275
- namespace: "docu-config",
277
+ namespace: "client-routes",
276
278
  }));
277
- build.onLoad({ filter: /.*/, namespace: "docu-config" }, () => {
279
+ build.onLoad({ filter: /.*/, namespace: "client-routes" }, () => {
278
280
  const config = loadDocuConfig();
279
281
  const resolved = {
280
282
  ...config,
281
283
  routes: resolveRoutes(config.routes as DocuRoute[] | undefined),
282
284
  };
283
- return { contents: JSON.stringify(resolved), loader: "json" };
285
+ return {
286
+ contents: [
287
+ `import type { DocuRoute, DocuConfig } from "./types";`,
288
+ `const docuConfig = ${JSON.stringify(resolved)};`,
289
+ `export const routes = docuConfig.routes || [];`,
290
+ `export const config = docuConfig;`,
291
+ ].join("\n"),
292
+ loader: "ts",
293
+ };
284
294
  });
285
295
  },
286
296
  },
@@ -142,17 +142,27 @@ export async function buildClientBundle(): Promise<{ js: string; css: string }>
142
142
  {
143
143
  name: "docu-config",
144
144
  setup(build) {
145
- build.onResolve({ filter: /docu\.json$/ }, (args) => ({
145
+ // Components import as "../node/client-routes" (no .ts extension),
146
+ // so filter matches the path tail without requiring the extension.
147
+ build.onResolve({ filter: /client-routes$/ }, (args) => ({
146
148
  path: args.path,
147
- namespace: "docu-config",
149
+ namespace: "client-routes",
148
150
  }));
149
- build.onLoad({ filter: /.*/, namespace: "docu-config" }, () => {
151
+ build.onLoad({ filter: /.*/, namespace: "client-routes" }, () => {
150
152
  const config = loadDocuConfig();
151
153
  const resolved = {
152
154
  ...config,
153
155
  routes: resolveRoutes(config.routes as DocuRoute[] | undefined),
154
156
  };
155
- return { contents: JSON.stringify(resolved), loader: "json" };
157
+ return {
158
+ contents: [
159
+ `import type { DocuRoute, DocuConfig } from "./types";`,
160
+ `const docuConfig = ${JSON.stringify(resolved)};`,
161
+ `export const routes = docuConfig.routes || [];`,
162
+ `export const config = docuConfig;`,
163
+ ].join("\n"),
164
+ loader: "ts",
165
+ };
156
166
  });
157
167
  },
158
168
  },
@@ -35,30 +35,6 @@ await build({
35
35
  packages: "external",
36
36
  jsx: "automatic",
37
37
  logLevel: "info",
38
- plugins: [
39
- {
40
- // The SSR component graph imports `docu.json` statically
41
- // (client-routes.ts). At runtime that must be the USER's project
42
- // config, so replace the import with a cwd-relative read instead of
43
- // baking the monorepo's docu.json into the published bundle.
44
- name: "docu-config-runtime",
45
- setup(build) {
46
- build.onResolve({ filter: /docu\.json$/ }, (args) => ({
47
- path: args.path,
48
- namespace: "docu-config-runtime",
49
- }));
50
- build.onLoad({ filter: /.*/, namespace: "docu-config-runtime" }, () => ({
51
- contents: [
52
- `import { readFileSync } from "node:fs";`,
53
- `import { join } from "node:path";`,
54
- `const config = JSON.parse(readFileSync(join(process.cwd(), "docu.json"), "utf-8"));`,
55
- `export default config;`,
56
- ].join("\n"),
57
- loader: "js",
58
- }));
59
- },
60
- },
61
- ],
62
38
  });
63
39
 
64
40
  // esbuild's service process keeps Deno's event loop alive after one-shot
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docubook/flame",
3
- "version": "1.6.0",
3
+ "version": "1.6.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": {
@@ -57,8 +57,8 @@
57
57
  "@docubook/core": "^1.8.2",
58
58
  "@docubook/mdx-content": "^3.4.5",
59
59
  "@docubook/runt": "^1.0.0",
60
- "@docubook/themes-colors": "^1.0.2",
61
- "@docubook/ui-react": "^1.0.0"
60
+ "@docubook/ui-react": "^1.0.0",
61
+ "@docubook/themes-colors": "^1.0.2"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@sentry/bun": "^10.0.0"