@docubook/flame 1.6.5 → 1.7.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.
- package/.docu/lib/build.deno.js +3 -3
- package/.docu/lib/{build.impl-VCWNBEBU.js → build.impl-F4M6HJXA.js} +3 -3
- package/.docu/lib/build.node.js +3 -3
- package/.docu/lib/{chunk-P2P2NB3K.js → chunk-4NI6P6NM.js} +63 -10
- package/.docu/lib/{chunk-OUILOW2S.js → chunk-7GWATWNE.js} +2 -2
- package/.docu/lib/{chunk-I7OBFORY.js → chunk-PL3JZCCY.js} +1 -1
- package/.docu/lib/{chunk-Y4PEPREZ.js → chunk-UDDWU5NP.js} +2 -2
- package/.docu/lib/{chunk-74AOEDLW.js → chunk-VJKIFRLV.js} +1 -1
- package/.docu/lib/{chunk-WSJC3PRH.js → chunk-VW7B64WE.js} +13 -4
- 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 +2 -2
- package/.docu/lib/preview.node.js +2 -2
- package/.docu/lib/server.deno.js +3 -3
- package/.docu/lib/server.node.js +3 -3
- package/.docu/node/deploy.shared.ts +66 -10
- package/.docu/node/deploy.ts +65 -8
- package/.docu/node/git.ts +14 -0
- package/.docu/node/mdx.ts +2 -1
- package/bin/cli.js +6 -3
- package/package.json +4 -4
- package/template/docs/guide/deployment.mdx +13 -0
- package/template/package.json +2 -2
package/.docu/lib/build.deno.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-7GWATWNE.js";
|
|
4
|
+
import "./chunk-VW7B64WE.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-PL3JZCCY.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-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-7GWATWNE.js";
|
|
5
|
+
import "./chunk-VW7B64WE.js";
|
|
6
6
|
import "./chunk-EOK6KATZ.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-PL3JZCCY.js";
|
|
8
8
|
import "./chunk-4IQXHHPF.js";
|
|
9
9
|
export {
|
|
10
10
|
runBuild,
|
package/.docu/lib/build.node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuildCli
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-7GWATWNE.js";
|
|
4
|
+
import "./chunk-VW7B64WE.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-PL3JZCCY.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/build.node.ts
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DIST_DIR,
|
|
3
|
+
FRAMEWORK_ROOT,
|
|
3
4
|
PROJECT_ROOT
|
|
4
5
|
} from "./chunk-4IQXHHPF.js";
|
|
5
6
|
|
|
6
7
|
// .docu/node/deploy.shared.ts
|
|
7
8
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
9
|
-
import { join } from "node:path";
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
import { join, resolve } from "node:path";
|
|
11
|
+
var FLAME_MAJOR = JSON.parse(
|
|
12
|
+
readFileSync(resolve(FRAMEWORK_ROOT, "package.json"), "utf-8")
|
|
13
|
+
).version.split(".")[0];
|
|
10
14
|
var WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
|
|
11
15
|
var WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
|
|
12
16
|
var NGINX_CONF = `server {
|
|
@@ -54,7 +58,6 @@ var NGINX_CONF = `server {
|
|
|
54
58
|
}
|
|
55
59
|
`;
|
|
56
60
|
var DOCKERIGNORE = `node_modules
|
|
57
|
-
.git
|
|
58
61
|
*.DS_Store
|
|
59
62
|
.docu/dist
|
|
60
63
|
.docu/lib
|
|
@@ -117,6 +120,7 @@ var HEADERS_FILE = `/*
|
|
|
117
120
|
`;
|
|
118
121
|
var isDocker = !!process.env.FLAME_DEPLOY_DOCKER;
|
|
119
122
|
var isSilent = !!process.env.FLAME_DEPLOY_SILENT;
|
|
123
|
+
var isCi = !!process.env.FLAME_DEPLOY_CI;
|
|
120
124
|
var log = isSilent ? { info: () => {
|
|
121
125
|
}, ok: () => {
|
|
122
126
|
}, created: () => {
|
|
@@ -133,22 +137,19 @@ async function runBuild() {
|
|
|
133
137
|
process.env.FLAME_BUILD_SILENT = "1";
|
|
134
138
|
process.env.LOG_LEVEL = "error";
|
|
135
139
|
}
|
|
136
|
-
const { runBuildCli } = await import("./build.impl-
|
|
140
|
+
const { runBuildCli } = await import("./build.impl-F4M6HJXA.js");
|
|
137
141
|
await runBuildCli();
|
|
138
142
|
}
|
|
139
143
|
async function writeDockerFiles() {
|
|
140
144
|
const dockerDir = PROJECT_ROOT;
|
|
141
|
-
const pm = detectPkgManager(dockerDir);
|
|
142
145
|
if (!existsSync(join(dockerDir, "Dockerfile"))) {
|
|
143
146
|
await writeFile(
|
|
144
147
|
join(dockerDir, "Dockerfile"),
|
|
145
|
-
`FROM
|
|
148
|
+
`FROM ghcr.io/docubook/flame-builder:${FLAME_MAJOR} AS builder
|
|
146
149
|
ENV NODE_ENV=production
|
|
147
150
|
WORKDIR /app
|
|
148
|
-
COPY package.json ${pm.lockFile} ./
|
|
149
|
-
RUN ${pm.installCmd}
|
|
150
151
|
COPY . .
|
|
151
|
-
RUN
|
|
152
|
+
RUN flame build
|
|
152
153
|
|
|
153
154
|
FROM nginx:alpine
|
|
154
155
|
COPY --from=builder /app/.docu/dist /usr/share/nginx/html
|
|
@@ -246,6 +247,43 @@ function generateWorkflowYml() {
|
|
|
246
247
|
" uses: actions/deploy-pages@v4"
|
|
247
248
|
].join("\n");
|
|
248
249
|
}
|
|
250
|
+
function generateDockerWorkflowYml() {
|
|
251
|
+
const imageName = "ghcr.io/${{ github.repository }}";
|
|
252
|
+
return [
|
|
253
|
+
`name: Build & Push Docker Image`,
|
|
254
|
+
"",
|
|
255
|
+
"on:",
|
|
256
|
+
" push:",
|
|
257
|
+
" branches: [main]",
|
|
258
|
+
" workflow_dispatch:",
|
|
259
|
+
"",
|
|
260
|
+
"permissions:",
|
|
261
|
+
" contents: read",
|
|
262
|
+
" packages: write",
|
|
263
|
+
"",
|
|
264
|
+
"jobs:",
|
|
265
|
+
" build:",
|
|
266
|
+
" runs-on: ubuntu-latest",
|
|
267
|
+
" steps:",
|
|
268
|
+
" - uses: actions/checkout@v4",
|
|
269
|
+
" with:",
|
|
270
|
+
" fetch-depth: 0",
|
|
271
|
+
"",
|
|
272
|
+
" - name: Log in to GHCR",
|
|
273
|
+
" uses: docker/login-action@v3",
|
|
274
|
+
" with:",
|
|
275
|
+
" registry: ghcr.io",
|
|
276
|
+
" username: ${{ github.actor }}",
|
|
277
|
+
" password: ${{ secrets.GITHUB_TOKEN }}",
|
|
278
|
+
"",
|
|
279
|
+
" - name: Build & push",
|
|
280
|
+
" uses: docker/build-push-action@v5",
|
|
281
|
+
" with:",
|
|
282
|
+
" context: .",
|
|
283
|
+
` tags: ${imageName}:latest`,
|
|
284
|
+
" push: true"
|
|
285
|
+
].join("\n");
|
|
286
|
+
}
|
|
249
287
|
async function writeGhaWorkflow() {
|
|
250
288
|
if (!existsSync(WORKFLOW_FILE)) {
|
|
251
289
|
await mkdir(WORKFLOW_DIR, { recursive: true });
|
|
@@ -253,6 +291,14 @@ async function writeGhaWorkflow() {
|
|
|
253
291
|
log.created("\u{1F4C4} Created .github/workflows/deploy.yml");
|
|
254
292
|
}
|
|
255
293
|
}
|
|
294
|
+
var DOCKER_WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy-docker.yml");
|
|
295
|
+
async function writeDockerWorkflow() {
|
|
296
|
+
if (!existsSync(DOCKER_WORKFLOW_FILE)) {
|
|
297
|
+
await mkdir(WORKFLOW_DIR, { recursive: true });
|
|
298
|
+
await writeFile(DOCKER_WORKFLOW_FILE, generateDockerWorkflowYml());
|
|
299
|
+
log.created("\u{1F4C4} Created .github/workflows/deploy-docker.yml");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
256
302
|
async function runDeploy() {
|
|
257
303
|
log.info("\u{1F4E6} Building for production...\n");
|
|
258
304
|
await runBuild();
|
|
@@ -260,13 +306,20 @@ async function runDeploy() {
|
|
|
260
306
|
await writeFile(join(DIST_DIR, "_headers"), HEADERS_FILE);
|
|
261
307
|
if (isDocker) {
|
|
262
308
|
await writeDockerFiles();
|
|
309
|
+
if (isCi) await writeDockerWorkflow();
|
|
263
310
|
} else {
|
|
264
311
|
await writeGhaWorkflow();
|
|
265
312
|
}
|
|
266
313
|
log.ok();
|
|
267
314
|
log.out(" Output: .docu/dist/");
|
|
268
315
|
if (isDocker) {
|
|
269
|
-
log.out("
|
|
316
|
+
log.out(" Build locally: docker build -t my-docs . && docker run -p 80:80 my-docs");
|
|
317
|
+
if (isCi) {
|
|
318
|
+
log.out(" Push to GitHub \u2014 CI will build & push Docker image to GHCR");
|
|
319
|
+
log.out(" Then pull latest image on your hosting platform (Coolify, etc.)");
|
|
320
|
+
} else {
|
|
321
|
+
log.out(" For Coolify: connect repo, set build pack to Dockerfile");
|
|
322
|
+
}
|
|
270
323
|
} else {
|
|
271
324
|
log.out(" Push to GitHub and enable Pages (Settings \u2192 Pages \u2192 Source: GitHub Actions)");
|
|
272
325
|
}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
htmlShell,
|
|
14
14
|
initSentry,
|
|
15
15
|
loadPlugins
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VW7B64WE.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-
|
|
24
|
+
} from "./chunk-PL3JZCCY.js";
|
|
25
25
|
import {
|
|
26
26
|
ASSETS_DIR,
|
|
27
27
|
CACHE_FILE,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
htmlShell,
|
|
15
15
|
initSentry,
|
|
16
16
|
loadPlugins
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-VW7B64WE.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-
|
|
30
|
+
} from "./chunk-PL3JZCCY.js";
|
|
31
31
|
import {
|
|
32
32
|
DIST_DIR,
|
|
33
33
|
DOCS_DIR,
|
|
@@ -555,8 +555,8 @@ function scanDir(dirPath, docsRoot) {
|
|
|
555
555
|
if (entry.startsWith(".") || entry === "assets") continue;
|
|
556
556
|
const absPath = join(dirPath, entry);
|
|
557
557
|
try {
|
|
558
|
-
const
|
|
559
|
-
if (
|
|
558
|
+
const stat2 = statSync(absPath);
|
|
559
|
+
if (stat2.isDirectory()) {
|
|
560
560
|
const children = scanDir(absPath, docsRoot);
|
|
561
561
|
if (children.length > 0) {
|
|
562
562
|
nodes.push({
|
|
@@ -567,7 +567,7 @@ function scanDir(dirPath, docsRoot) {
|
|
|
567
567
|
children
|
|
568
568
|
});
|
|
569
569
|
}
|
|
570
|
-
} else if (
|
|
570
|
+
} else if (stat2.isFile() && isDocFile(entry)) {
|
|
571
571
|
nodes.push({
|
|
572
572
|
name: entry,
|
|
573
573
|
relPath: normalizePath(relative(docsRoot, absPath).replace(/\.(mdx|md)$/, "")),
|
|
@@ -1073,6 +1073,7 @@ function captureException(err, context) {
|
|
|
1073
1073
|
|
|
1074
1074
|
// .docu/node/git.ts
|
|
1075
1075
|
import { execFile as execFile2 } from "node:child_process";
|
|
1076
|
+
import { stat } from "node:fs/promises";
|
|
1076
1077
|
function runGit(args) {
|
|
1077
1078
|
return new Promise((resolve4, reject) => {
|
|
1078
1079
|
execFile2("git", args, { maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
|
|
@@ -1098,6 +1099,14 @@ async function getGitLastModified(filePath) {
|
|
|
1098
1099
|
return null;
|
|
1099
1100
|
}
|
|
1100
1101
|
}
|
|
1102
|
+
async function getFilesystemMtime(filePath) {
|
|
1103
|
+
try {
|
|
1104
|
+
const stats = await stat(filePath);
|
|
1105
|
+
return stats.mtime.toISOString();
|
|
1106
|
+
} catch {
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1101
1110
|
async function getGitLastModifiedBatch(filePaths) {
|
|
1102
1111
|
const result = /* @__PURE__ */ new Map();
|
|
1103
1112
|
if (filePaths.length === 0) return result;
|
|
@@ -1208,7 +1217,7 @@ async function compileMdx(rawMdx, filePath, gitDates, remarkPlugins, rehypePlugi
|
|
|
1208
1217
|
frontmatter: {},
|
|
1209
1218
|
components
|
|
1210
1219
|
});
|
|
1211
|
-
const date = frontmatter.date || gitDates?.get(filePath) || await getGitLastModified(filePath) || void 0;
|
|
1220
|
+
const date = frontmatter.date || gitDates?.get(filePath) || await getGitLastModified(filePath) || await getFilesystemMtime(filePath) || void 0;
|
|
1212
1221
|
return {
|
|
1213
1222
|
content,
|
|
1214
1223
|
compiledSource: serialized.compiledSource,
|
package/.docu/lib/clean.js
CHANGED
package/.docu/lib/deploy.deno.js
CHANGED
package/.docu/lib/deploy.node.js
CHANGED
package/.docu/lib/server.deno.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runServer
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-UDDWU5NP.js";
|
|
4
|
+
import "./chunk-VW7B64WE.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-PL3JZCCY.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.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-
|
|
3
|
+
} from "./chunk-UDDWU5NP.js";
|
|
4
|
+
import "./chunk-VW7B64WE.js";
|
|
5
5
|
import "./chunk-EOK6KATZ.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-PL3JZCCY.js";
|
|
7
7
|
import "./chunk-4IQXHHPF.js";
|
|
8
8
|
|
|
9
9
|
// .docu/node/server.node.ts
|
|
@@ -9,9 +9,13 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
12
|
-
import { existsSync } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
import { DIST_DIR, PROJECT_ROOT } from "./paths";
|
|
12
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
import { DIST_DIR, PROJECT_ROOT, FRAMEWORK_ROOT } from "./paths";
|
|
15
|
+
|
|
16
|
+
const FLAME_MAJOR = JSON.parse(
|
|
17
|
+
readFileSync(resolve(FRAMEWORK_ROOT, "package.json"), "utf-8")
|
|
18
|
+
).version.split(".")[0];
|
|
15
19
|
|
|
16
20
|
const WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
|
|
17
21
|
const WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
|
|
@@ -62,7 +66,6 @@ export const NGINX_CONF = `server {
|
|
|
62
66
|
`;
|
|
63
67
|
|
|
64
68
|
export const DOCKERIGNORE = `node_modules
|
|
65
|
-
.git
|
|
66
69
|
*.DS_Store
|
|
67
70
|
.docu/dist
|
|
68
71
|
.docu/lib
|
|
@@ -140,6 +143,7 @@ export const HEADERS_FILE = `/*
|
|
|
140
143
|
|
|
141
144
|
const isDocker = !!process.env.FLAME_DEPLOY_DOCKER;
|
|
142
145
|
const isSilent = !!process.env.FLAME_DEPLOY_SILENT;
|
|
146
|
+
const isCi = !!process.env.FLAME_DEPLOY_CI;
|
|
143
147
|
|
|
144
148
|
/** Logger that no-ops all non-error output in silent mode. */
|
|
145
149
|
const log = isSilent
|
|
@@ -163,18 +167,15 @@ async function runBuild() {
|
|
|
163
167
|
|
|
164
168
|
async function writeDockerFiles() {
|
|
165
169
|
const dockerDir = PROJECT_ROOT;
|
|
166
|
-
const pm = detectPkgManager(dockerDir);
|
|
167
170
|
|
|
168
171
|
if (!existsSync(join(dockerDir, "Dockerfile"))) {
|
|
169
172
|
await writeFile(
|
|
170
173
|
join(dockerDir, "Dockerfile"),
|
|
171
|
-
`FROM
|
|
174
|
+
`FROM ghcr.io/docubook/flame-builder:${FLAME_MAJOR} AS builder
|
|
172
175
|
ENV NODE_ENV=production
|
|
173
176
|
WORKDIR /app
|
|
174
|
-
COPY package.json ${pm.lockFile} ./
|
|
175
|
-
RUN ${pm.installCmd}
|
|
176
177
|
COPY . .
|
|
177
|
-
RUN
|
|
178
|
+
RUN flame build
|
|
178
179
|
|
|
179
180
|
FROM nginx:alpine
|
|
180
181
|
COPY --from=builder /app/.docu/dist /usr/share/nginx/html
|
|
@@ -280,6 +281,44 @@ function generateWorkflowYml(): string {
|
|
|
280
281
|
].join("\n");
|
|
281
282
|
}
|
|
282
283
|
|
|
284
|
+
function generateDockerWorkflowYml(): string {
|
|
285
|
+
const imageName = "ghcr.io/${{ github.repository }}";
|
|
286
|
+
return [
|
|
287
|
+
`name: Build & Push Docker Image`,
|
|
288
|
+
"",
|
|
289
|
+
"on:",
|
|
290
|
+
" push:",
|
|
291
|
+
" branches: [main]",
|
|
292
|
+
" workflow_dispatch:",
|
|
293
|
+
"",
|
|
294
|
+
"permissions:",
|
|
295
|
+
" contents: read",
|
|
296
|
+
" packages: write",
|
|
297
|
+
"",
|
|
298
|
+
"jobs:",
|
|
299
|
+
" build:",
|
|
300
|
+
" runs-on: ubuntu-latest",
|
|
301
|
+
" steps:",
|
|
302
|
+
" - uses: actions/checkout@v4",
|
|
303
|
+
" with:",
|
|
304
|
+
" fetch-depth: 0",
|
|
305
|
+
"",
|
|
306
|
+
" - name: Log in to GHCR",
|
|
307
|
+
" uses: docker/login-action@v3",
|
|
308
|
+
" with:",
|
|
309
|
+
" registry: ghcr.io",
|
|
310
|
+
" username: ${{ github.actor }}",
|
|
311
|
+
" password: ${{ secrets.GITHUB_TOKEN }}",
|
|
312
|
+
"",
|
|
313
|
+
" - name: Build & push",
|
|
314
|
+
" uses: docker/build-push-action@v5",
|
|
315
|
+
" with:",
|
|
316
|
+
" context: .",
|
|
317
|
+
` tags: ${imageName}:latest`,
|
|
318
|
+
" push: true",
|
|
319
|
+
].join("\n");
|
|
320
|
+
}
|
|
321
|
+
|
|
283
322
|
async function writeGhaWorkflow() {
|
|
284
323
|
if (!existsSync(WORKFLOW_FILE)) {
|
|
285
324
|
await mkdir(WORKFLOW_DIR, { recursive: true });
|
|
@@ -288,6 +327,16 @@ async function writeGhaWorkflow() {
|
|
|
288
327
|
}
|
|
289
328
|
}
|
|
290
329
|
|
|
330
|
+
const DOCKER_WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy-docker.yml");
|
|
331
|
+
|
|
332
|
+
async function writeDockerWorkflow() {
|
|
333
|
+
if (!existsSync(DOCKER_WORKFLOW_FILE)) {
|
|
334
|
+
await mkdir(WORKFLOW_DIR, { recursive: true });
|
|
335
|
+
await writeFile(DOCKER_WORKFLOW_FILE, generateDockerWorkflowYml());
|
|
336
|
+
log.created("📄 Created .github/workflows/deploy-docker.yml");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
291
340
|
export async function runDeploy(): Promise<void> {
|
|
292
341
|
log.info("📦 Building for production...\n");
|
|
293
342
|
await runBuild();
|
|
@@ -298,6 +347,7 @@ export async function runDeploy(): Promise<void> {
|
|
|
298
347
|
|
|
299
348
|
if (isDocker) {
|
|
300
349
|
await writeDockerFiles();
|
|
350
|
+
if (isCi) await writeDockerWorkflow();
|
|
301
351
|
} else {
|
|
302
352
|
await writeGhaWorkflow();
|
|
303
353
|
}
|
|
@@ -305,7 +355,13 @@ export async function runDeploy(): Promise<void> {
|
|
|
305
355
|
log.ok();
|
|
306
356
|
log.out(" Output: .docu/dist/");
|
|
307
357
|
if (isDocker) {
|
|
308
|
-
log.out("
|
|
358
|
+
log.out(" Build locally: docker build -t my-docs . && docker run -p 80:80 my-docs");
|
|
359
|
+
if (isCi) {
|
|
360
|
+
log.out(" Push to GitHub — CI will build & push Docker image to GHCR");
|
|
361
|
+
log.out(" Then pull latest image on your hosting platform (Coolify, etc.)");
|
|
362
|
+
} else {
|
|
363
|
+
log.out(" For Coolify: connect repo, set build pack to Dockerfile");
|
|
364
|
+
}
|
|
309
365
|
} else {
|
|
310
366
|
log.out(" Push to GitHub and enable Pages (Settings → Pages → Source: GitHub Actions)");
|
|
311
367
|
}
|
package/.docu/node/deploy.ts
CHANGED
|
@@ -8,18 +8,24 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { mkdir } from "node:fs/promises";
|
|
11
|
-
import { existsSync } from "node:fs";
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
-
import { DIST_DIR, PROJECT_ROOT } from "./paths";
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
import { DIST_DIR, PROJECT_ROOT, FRAMEWORK_ROOT } from "./paths";
|
|
14
14
|
import { HEADERS_FILE, NGINX_CONF, DOCKERIGNORE } from "./deploy.shared";
|
|
15
15
|
|
|
16
|
+
const FLAME_PKG = JSON.parse(readFileSync(resolve(FRAMEWORK_ROOT, "package.json"), "utf-8"));
|
|
17
|
+
const FLAME_VERSION = FLAME_PKG.version;
|
|
18
|
+
const FLAME_MAJOR = FLAME_VERSION.split(".")[0];
|
|
19
|
+
|
|
16
20
|
export { HEADERS_FILE, NGINX_CONF, DOCKERIGNORE };
|
|
17
21
|
|
|
18
22
|
const WORKFLOW_DIR = join(PROJECT_ROOT, ".github/workflows");
|
|
19
23
|
const WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy.yml");
|
|
24
|
+
const DOCKER_WORKFLOW_FILE = join(WORKFLOW_DIR, "deploy-docker.yml");
|
|
20
25
|
|
|
21
26
|
const isDocker = !!process.env.FLAME_DEPLOY_DOCKER;
|
|
22
27
|
const isSilent = !!process.env.FLAME_DEPLOY_SILENT;
|
|
28
|
+
const isCi = !!process.env.FLAME_DEPLOY_CI;
|
|
23
29
|
|
|
24
30
|
/** Logger that no-ops all non-error output in silent mode. */
|
|
25
31
|
const log = isSilent
|
|
@@ -43,13 +49,11 @@ async function runBuild() {
|
|
|
43
49
|
}
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
export const DOCKERFILE_BUN = `FROM
|
|
52
|
+
export const DOCKERFILE_BUN = `FROM ghcr.io/docubook/flame-builder:${FLAME_MAJOR} AS builder
|
|
47
53
|
ENV NODE_ENV=production
|
|
48
54
|
WORKDIR /app
|
|
49
|
-
COPY package.json bun.lock ./
|
|
50
|
-
RUN bun install --frozen-lockfile
|
|
51
55
|
COPY . .
|
|
52
|
-
RUN
|
|
56
|
+
RUN flame build
|
|
53
57
|
|
|
54
58
|
FROM nginx:alpine
|
|
55
59
|
COPY --from=builder /app/.docu/dist /usr/share/nginx/html
|
|
@@ -85,6 +89,52 @@ async function writeGhaWorkflow() {
|
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
function generateDockerWorkflowYml(): string {
|
|
93
|
+
const imageName = "ghcr.io/${{ github.repository }}";
|
|
94
|
+
return [
|
|
95
|
+
`name: Build & Push Docker Image`,
|
|
96
|
+
"",
|
|
97
|
+
"on:",
|
|
98
|
+
" push:",
|
|
99
|
+
" branches: [main]",
|
|
100
|
+
" workflow_dispatch:",
|
|
101
|
+
"",
|
|
102
|
+
"permissions:",
|
|
103
|
+
" contents: read",
|
|
104
|
+
" packages: write",
|
|
105
|
+
"",
|
|
106
|
+
"jobs:",
|
|
107
|
+
" build:",
|
|
108
|
+
" runs-on: ubuntu-latest",
|
|
109
|
+
" steps:",
|
|
110
|
+
" - uses: actions/checkout@v4",
|
|
111
|
+
" with:",
|
|
112
|
+
" fetch-depth: 0",
|
|
113
|
+
"",
|
|
114
|
+
" - name: Log in to GHCR",
|
|
115
|
+
" uses: docker/login-action@v3",
|
|
116
|
+
" with:",
|
|
117
|
+
" registry: ghcr.io",
|
|
118
|
+
" username: ${{ github.actor }}",
|
|
119
|
+
" password: ${{ secrets.GITHUB_TOKEN }}",
|
|
120
|
+
"",
|
|
121
|
+
" - name: Build & push",
|
|
122
|
+
" uses: docker/build-push-action@v5",
|
|
123
|
+
" with:",
|
|
124
|
+
" context: .",
|
|
125
|
+
` tags: ${imageName}:latest`,
|
|
126
|
+
" push: true",
|
|
127
|
+
].join("\n");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function writeDockerWorkflow() {
|
|
131
|
+
if (!existsSync(DOCKER_WORKFLOW_FILE)) {
|
|
132
|
+
await mkdir(WORKFLOW_DIR, { recursive: true });
|
|
133
|
+
await Bun.write(DOCKER_WORKFLOW_FILE, generateDockerWorkflowYml());
|
|
134
|
+
log.created("📄 Created .github/workflows/deploy-docker.yml");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
88
138
|
async function deploy() {
|
|
89
139
|
log.info("📦 Building for production...\n");
|
|
90
140
|
await runBuild();
|
|
@@ -95,6 +145,7 @@ async function deploy() {
|
|
|
95
145
|
|
|
96
146
|
if (isDocker) {
|
|
97
147
|
await writeDockerFiles();
|
|
148
|
+
if (isCi) await writeDockerWorkflow();
|
|
98
149
|
} else {
|
|
99
150
|
await writeGhaWorkflow();
|
|
100
151
|
}
|
|
@@ -102,7 +153,13 @@ async function deploy() {
|
|
|
102
153
|
log.ok();
|
|
103
154
|
log.out(" Output: .docu/dist/");
|
|
104
155
|
if (isDocker) {
|
|
105
|
-
log.out("
|
|
156
|
+
log.out(" Build locally: docker build -t my-docs . && docker run -p 80:80 my-docs");
|
|
157
|
+
if (isCi) {
|
|
158
|
+
log.out(" Push to GitHub — CI will build & push Docker image to GHCR");
|
|
159
|
+
log.out(" Then pull latest image on your hosting platform (Coolify, etc.)");
|
|
160
|
+
} else {
|
|
161
|
+
log.out(" For Coolify: connect repo, set build pack to Dockerfile");
|
|
162
|
+
}
|
|
106
163
|
} else {
|
|
107
164
|
log.out(" Push to GitHub and enable Pages (Settings → Pages → Source: GitHub Actions)");
|
|
108
165
|
}
|
package/.docu/node/git.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { execFile } from "node:child_process";
|
|
8
|
+
import { stat } from "node:fs/promises";
|
|
8
9
|
|
|
9
10
|
function runGit(args: string[]): Promise<string> {
|
|
10
11
|
return new Promise((resolve, reject) => {
|
|
@@ -35,6 +36,19 @@ export async function getGitLastModified(filePath: string): Promise<string | nul
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Fallback to filesystem mtime when git is unavailable (shallow clone, no .git, etc.).
|
|
41
|
+
* Returns ISO 8601 date string or null.
|
|
42
|
+
*/
|
|
43
|
+
export async function getFilesystemMtime(filePath: string): Promise<string | null> {
|
|
44
|
+
try {
|
|
45
|
+
const stats = await stat(filePath);
|
|
46
|
+
return stats.mtime.toISOString();
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
/** Batch git last modified dates for multiple files in a single spawn */
|
|
39
53
|
export async function getGitLastModifiedBatch(filePaths: string[]): Promise<Map<string, string>> {
|
|
40
54
|
const result = new Map<string, string>();
|
package/.docu/node/mdx.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
MDXRemote,
|
|
10
10
|
} from "@docubook/core";
|
|
11
11
|
import { createMdxComponents } from "@docubook/mdx-content";
|
|
12
|
-
import { getGitLastModified, getGitLastModifiedBatch } from "./git";
|
|
12
|
+
import { getGitLastModified, getGitLastModifiedBatch, getFilesystemMtime } from "./git";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Return the value with `.html` appended, or null if the value should be left
|
|
@@ -161,6 +161,7 @@ export async function compileMdx(
|
|
|
161
161
|
frontmatter.date ||
|
|
162
162
|
gitDates?.get(filePath) ||
|
|
163
163
|
(await getGitLastModified(filePath)) ||
|
|
164
|
+
(await getFilesystemMtime(filePath)) ||
|
|
164
165
|
undefined;
|
|
165
166
|
|
|
166
167
|
return {
|
package/bin/cli.js
CHANGED
|
@@ -58,8 +58,10 @@ if (themeIndex !== -1 && themeIndex + 1 < process.argv.length) {
|
|
|
58
58
|
}
|
|
59
59
|
const hasDocker = process.argv.includes("--docker");
|
|
60
60
|
const hasSilent = process.argv.includes("--silent");
|
|
61
|
+
const hasCi = process.argv.includes("--ci");
|
|
61
62
|
if (hasDocker) process.env.FLAME_DEPLOY_DOCKER = "1";
|
|
62
63
|
if (hasSilent) process.env.FLAME_DEPLOY_SILENT = "1";
|
|
64
|
+
if (hasCi) process.env.FLAME_DEPLOY_CI = "1";
|
|
63
65
|
|
|
64
66
|
// Production mode for build/preview/deploy — ensures minified client bundle
|
|
65
67
|
// on all platforms (Coolify, Vercel, etc.) without requiring the user to
|
|
@@ -88,7 +90,8 @@ if (!command || command === "--help" || command === "-h") {
|
|
|
88
90
|
Options:
|
|
89
91
|
--help Show this help message
|
|
90
92
|
--theme <name> Override theme preset (e.g. freshlime, coffee). Works with dev, build, preview.
|
|
91
|
-
--docker Generate Docker deployment files (Dockerfile + nginx.conf). Works with deploy.
|
|
93
|
+
--docker Generate Docker deployment files (Dockerfile + nginx.conf + .dockerignore). Works with deploy.
|
|
94
|
+
--ci Generate CI workflow (.github/workflows/deploy-docker.yml) for auto build & push to GHCR. Use with --docker.
|
|
92
95
|
--silent Suppress non-essential output. Works with deploy.
|
|
93
96
|
`);
|
|
94
97
|
process.exit(0);
|
|
@@ -127,8 +130,8 @@ if (command === "init") {
|
|
|
127
130
|
pkg.scripts = {
|
|
128
131
|
dev: `${flameCmd} dev`,
|
|
129
132
|
build: `NODE_ENV=production ${flameCmd} build`,
|
|
130
|
-
preview:
|
|
131
|
-
deploy:
|
|
133
|
+
preview: `${flameCmd} preview`,
|
|
134
|
+
deploy: `${flameCmd} deploy`,
|
|
132
135
|
};
|
|
133
136
|
|
|
134
137
|
// Generate deno.json with nodeModulesDir for npm compat.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docubook/flame",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
"react-dom": "^19.2.7",
|
|
56
56
|
"unified": "^11.0.0",
|
|
57
57
|
"@docubook/core": "^1.8.2",
|
|
58
|
-
"@docubook/
|
|
59
|
-
"@docubook/mdx-content": "^3.4.5",
|
|
58
|
+
"@docubook/ui-react": "^1.0.0",
|
|
60
59
|
"@docubook/themes-colors": "^1.0.2",
|
|
61
|
-
"@docubook/
|
|
60
|
+
"@docubook/runt": "^1.0.0",
|
|
61
|
+
"@docubook/mdx-content": "^3.4.5"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"@sentry/bun": "^10.0.0"
|
|
@@ -13,6 +13,19 @@ npm run deploy # or: bun run deploy / deno task deploy
|
|
|
13
13
|
|
|
14
14
|
This builds the site, adds `.nojekyll`, and generates a GitHub Actions workflow on first run.
|
|
15
15
|
|
|
16
|
+
## Docker (self-hosted / VPS)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
flame deploy --docker
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Generates `Dockerfile` + `nginx.conf` + `.dockerignore`. Build and run:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
docker build -t my-docs .
|
|
26
|
+
docker run -p 80:80 my-docs
|
|
27
|
+
```
|
|
28
|
+
|
|
16
29
|
## Manual
|
|
17
30
|
|
|
18
31
|
```bash
|
package/template/package.json
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "flame dev",
|
|
7
7
|
"build": "NODE_ENV=production flame build",
|
|
8
|
-
"preview": "
|
|
9
|
-
"deploy": "
|
|
8
|
+
"preview": "flame preview",
|
|
9
|
+
"deploy": "flame deploy"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@docubook/flame": "latest"
|