@docubook/flame 1.4.4 → 1.5.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 +11 -0
- package/.docu/lib/build.impl-ST63VRTV.js +12 -0
- package/.docu/lib/build.node.js +10 -0
- package/.docu/lib/chunk-2QHMGZIL.js +2419 -0
- package/.docu/lib/chunk-654THQOR.js +461 -0
- package/.docu/lib/chunk-C6RZ2KBH.js +79 -0
- package/.docu/lib/chunk-HH4YXWEF.js +300 -0
- package/.docu/lib/chunk-J5NMYSBJ.js +59 -0
- package/.docu/lib/chunk-RE4NGTMT.js +185 -0
- package/.docu/lib/chunk-X6GYOIYZ.js +383 -0
- package/.docu/lib/chunk-ZOWTASXL.js +92 -0
- package/.docu/lib/clean.js +32 -0
- package/.docu/lib/deploy.deno.js +13 -0
- package/.docu/lib/deploy.node.js +10 -0
- package/.docu/lib/preview.deno.js +10 -0
- package/.docu/lib/preview.node.js +10 -0
- package/.docu/lib/server.deno.js +11 -0
- package/.docu/lib/server.node.js +11 -0
- package/.docu/node/build-summary.ts +126 -0
- package/.docu/node/build.deno.ts +7 -0
- package/.docu/node/build.impl.ts +424 -0
- package/.docu/node/build.node.ts +3 -0
- package/.docu/node/deploy.deno.ts +11 -0
- package/.docu/node/deploy.node.ts +6 -0
- package/.docu/node/deploy.shared.ts +85 -0
- package/.docu/node/deploy.ts +11 -0
- package/.docu/node/escapeHtml.ts +18 -0
- package/.docu/node/git.ts +79 -0
- package/.docu/node/html.shared.ts +110 -0
- package/.docu/node/hydrate.node.ts +287 -0
- package/.docu/node/hydrate.ts +16 -19
- package/.docu/node/mdx.ts +1 -1
- package/.docu/node/paths.ts +24 -0
- package/.docu/node/plugin-builder.ts +6 -2
- package/.docu/node/plugin.ts +11 -2
- package/.docu/node/preview.deno.ts +4 -0
- package/.docu/node/preview.impl.ts +96 -0
- package/.docu/node/preview.node.ts +4 -0
- package/.docu/node/security.ts +5 -0
- package/.docu/node/server-routes.ts +4 -4
- package/.docu/node/server.deno.ts +4 -0
- package/.docu/node/server.impl.ts +184 -0
- package/.docu/node/server.node.ts +4 -0
- package/.docu/styles/globals.css +20 -5
- package/README.md +57 -506
- package/bin/cli.js +89 -14
- package/bin/compile-lib.mjs +67 -0
- package/package.json +9 -5
- package/template/docs/getting-started/configuration.mdx +18 -0
- package/template/docs/getting-started/overview.mdx +50 -0
- package/template/docs/guide/deployment.mdx +27 -0
- package/template/docs/guide/routing.mdx +25 -0
- package/template/docs/index.mdx +8 -205
- package/template/docu.json +32 -1
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BuildPluginBuilder,
|
|
3
|
+
DocsLayout,
|
|
4
|
+
DocsPage,
|
|
5
|
+
IndexPage,
|
|
6
|
+
NotFoundPage,
|
|
7
|
+
buildClientBundle,
|
|
8
|
+
captureException,
|
|
9
|
+
compileMdx,
|
|
10
|
+
computeInlineThemeCss,
|
|
11
|
+
errorHtml,
|
|
12
|
+
generateSearchIndex,
|
|
13
|
+
hmrScript,
|
|
14
|
+
htmlShell,
|
|
15
|
+
initSentry,
|
|
16
|
+
loadPlugins
|
|
17
|
+
} from "./chunk-2QHMGZIL.js";
|
|
18
|
+
import {
|
|
19
|
+
SECURITY_HEADERS,
|
|
20
|
+
generateNonce,
|
|
21
|
+
getContentType,
|
|
22
|
+
htmlResponse,
|
|
23
|
+
isPathSafe,
|
|
24
|
+
isSlugSafe,
|
|
25
|
+
stripDocsHtmlSuffix,
|
|
26
|
+
wrapPluginResponse
|
|
27
|
+
} from "./chunk-RE4NGTMT.js";
|
|
28
|
+
import {
|
|
29
|
+
logger
|
|
30
|
+
} from "./chunk-HH4YXWEF.js";
|
|
31
|
+
import {
|
|
32
|
+
DIST_DIR,
|
|
33
|
+
DOCS_DIR,
|
|
34
|
+
PROJECT_ROOT,
|
|
35
|
+
loadDocuConfig
|
|
36
|
+
} from "./chunk-J5NMYSBJ.js";
|
|
37
|
+
|
|
38
|
+
// .docu/node/server.impl.ts
|
|
39
|
+
import { watch } from "node:fs";
|
|
40
|
+
|
|
41
|
+
// .docu/node/server-routes.ts
|
|
42
|
+
import { readFile } from "node:fs/promises";
|
|
43
|
+
import { resolve } from "node:path";
|
|
44
|
+
import { readFileSync, statSync } from "node:fs";
|
|
45
|
+
import React from "react";
|
|
46
|
+
import { renderToString } from "react-dom/server";
|
|
47
|
+
function createHtmlResponse(title, description, body, status, state, depth = 0) {
|
|
48
|
+
const nonce = generateNonce();
|
|
49
|
+
const favicon = state.docuConfig.meta?.favicon || "/favicon.ico";
|
|
50
|
+
const html = htmlShell({
|
|
51
|
+
title,
|
|
52
|
+
description,
|
|
53
|
+
body,
|
|
54
|
+
favicon,
|
|
55
|
+
css: state.assetManifest.css,
|
|
56
|
+
js: state.assetManifest.js,
|
|
57
|
+
nonce,
|
|
58
|
+
extraScripts: hmrScript(nonce),
|
|
59
|
+
themeCss: state.inlineThemeCss,
|
|
60
|
+
depth
|
|
61
|
+
});
|
|
62
|
+
return htmlResponse(html, nonce, status, process.env.NODE_ENV !== "production");
|
|
63
|
+
}
|
|
64
|
+
async function getDocsForSlug(slug, state) {
|
|
65
|
+
if (!isSlugSafe(slug, DOCS_DIR)) return null;
|
|
66
|
+
const paths = [
|
|
67
|
+
resolve(DOCS_DIR, slug, "index.mdx"),
|
|
68
|
+
resolve(DOCS_DIR, `${slug}.mdx`),
|
|
69
|
+
resolve(DOCS_DIR, slug, "index.md"),
|
|
70
|
+
resolve(DOCS_DIR, `${slug}.md`)
|
|
71
|
+
];
|
|
72
|
+
const resolvedDocsDir = resolve(DOCS_DIR);
|
|
73
|
+
let filePath = null;
|
|
74
|
+
let raw = null;
|
|
75
|
+
for (const p of paths) {
|
|
76
|
+
const resolvedCandidate = resolve(p);
|
|
77
|
+
if (resolvedCandidate !== resolvedDocsDir && !resolvedCandidate.startsWith(resolvedDocsDir + "/")) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
raw = await readFile(resolvedCandidate, "utf-8");
|
|
82
|
+
filePath = resolvedCandidate;
|
|
83
|
+
break;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (err.code !== "ENOENT") throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!filePath || !raw) return null;
|
|
89
|
+
const relPath = filePath.replace(PROJECT_ROOT + "/", "");
|
|
90
|
+
let content = raw;
|
|
91
|
+
if (state.builder) {
|
|
92
|
+
const transformed = await state.builder.runOnLoad(relPath, content);
|
|
93
|
+
if (transformed?.contents) {
|
|
94
|
+
content = transformed.contents;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const remarkPlugins = state.builder?.collectRemarkPlugins();
|
|
98
|
+
const rehypePlugins = state.builder?.collectRehypePlugins();
|
|
99
|
+
const result = await compileMdx(content, relPath, void 0, remarkPlugins, rehypePlugins);
|
|
100
|
+
let frontmatter = result.frontmatter;
|
|
101
|
+
if (state.builder) {
|
|
102
|
+
frontmatter = await state.builder.runTransformFrontmatterChain(frontmatter, {
|
|
103
|
+
slug: slug || "/",
|
|
104
|
+
filePath: relPath,
|
|
105
|
+
content
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
content: result.content,
|
|
110
|
+
compiledSource: result.compiledSource,
|
|
111
|
+
frontmatter,
|
|
112
|
+
tocs: result.tocs,
|
|
113
|
+
filePath: relPath,
|
|
114
|
+
resolvedContent: content
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async function renderDocsServerPage(doc, slug, pathname, state) {
|
|
118
|
+
const title = (typeof doc.frontmatter.title === "string" ? doc.frontmatter.title : "") || slug.join("/") || "Docs";
|
|
119
|
+
const description = typeof doc.frontmatter.description === "string" ? doc.frontmatter.description : "";
|
|
120
|
+
const page = React.createElement(
|
|
121
|
+
DocsLayout,
|
|
122
|
+
{ repoUrl: state.docuConfig.repo?.url, pathname },
|
|
123
|
+
React.createElement(DocsPage, {
|
|
124
|
+
slug,
|
|
125
|
+
title,
|
|
126
|
+
description,
|
|
127
|
+
date: doc.frontmatter.date,
|
|
128
|
+
content: doc.content,
|
|
129
|
+
tocs: doc.tocs,
|
|
130
|
+
filePath: doc.filePath,
|
|
131
|
+
repoUrl: state.docuConfig.repo?.url,
|
|
132
|
+
compiledSource: doc.compiledSource
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
const body = renderToString(page);
|
|
136
|
+
const depth = slug.length || 1;
|
|
137
|
+
if (state.builder) {
|
|
138
|
+
const ctx = {
|
|
139
|
+
slug: slug.join("/") || "/",
|
|
140
|
+
filePath: doc.filePath,
|
|
141
|
+
frontmatter: doc.frontmatter,
|
|
142
|
+
content: doc.resolvedContent,
|
|
143
|
+
config: state.docuConfig
|
|
144
|
+
};
|
|
145
|
+
const headExtra = state.builder.collectHead(ctx);
|
|
146
|
+
const bodyExtra = state.builder.collectBody(ctx);
|
|
147
|
+
const nonce = generateNonce();
|
|
148
|
+
const favicon = state.docuConfig.meta?.favicon || "/favicon.ico";
|
|
149
|
+
let html = htmlShell({
|
|
150
|
+
title,
|
|
151
|
+
description,
|
|
152
|
+
body,
|
|
153
|
+
favicon,
|
|
154
|
+
css: state.assetManifest.css,
|
|
155
|
+
js: state.assetManifest.js,
|
|
156
|
+
nonce,
|
|
157
|
+
extraScripts: hmrScript(nonce),
|
|
158
|
+
themeCss: state.inlineThemeCss,
|
|
159
|
+
headExtra,
|
|
160
|
+
bodyExtra,
|
|
161
|
+
depth
|
|
162
|
+
});
|
|
163
|
+
html = await state.builder.runTransformHtmlChain(html, ctx);
|
|
164
|
+
return htmlResponse(html, nonce, 200, process.env.NODE_ENV !== "production");
|
|
165
|
+
}
|
|
166
|
+
return createHtmlResponse(title, description, body, 200, state, depth);
|
|
167
|
+
}
|
|
168
|
+
function renderPage(Component, title, description, status, state, props = {}, depth = 0) {
|
|
169
|
+
const page = React.createElement(
|
|
170
|
+
DocsLayout,
|
|
171
|
+
{ repoUrl: state.docuConfig.repo?.url, pathname: "/docs" },
|
|
172
|
+
React.createElement(Component, props)
|
|
173
|
+
);
|
|
174
|
+
const body = renderToString(page);
|
|
175
|
+
return createHtmlResponse(title, description, body, status, state, depth);
|
|
176
|
+
}
|
|
177
|
+
async function handleDocsIndex(state) {
|
|
178
|
+
const doc = await getDocsForSlug("", state);
|
|
179
|
+
if (!doc) return renderPage(NotFoundPage, "404 - Not Found", "", 404, state, {}, 1);
|
|
180
|
+
return renderDocsServerPage(doc, [], "/docs", state);
|
|
181
|
+
}
|
|
182
|
+
async function handleDocsRoute(slug, state) {
|
|
183
|
+
const path = slug.join("/");
|
|
184
|
+
const doc = await getDocsForSlug(path, state);
|
|
185
|
+
if (!doc)
|
|
186
|
+
return renderPage(NotFoundPage, "404 - Not Found", "", 404, state, {}, slug.length || 1);
|
|
187
|
+
return renderDocsServerPage(doc, slug, `/docs/${path}`, state);
|
|
188
|
+
}
|
|
189
|
+
function handleIndex(state) {
|
|
190
|
+
const page = React.createElement(IndexPage);
|
|
191
|
+
const body = renderToString(page);
|
|
192
|
+
return createHtmlResponse(
|
|
193
|
+
state.docuConfig.meta?.title || "DocuBook",
|
|
194
|
+
state.docuConfig.meta?.description || "",
|
|
195
|
+
body,
|
|
196
|
+
200,
|
|
197
|
+
state
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
function handleNotFound(state, depth = 0) {
|
|
201
|
+
return renderPage(NotFoundPage, "404 - Not Found", "", 404, state, {}, depth);
|
|
202
|
+
}
|
|
203
|
+
function serveStatic(pathname) {
|
|
204
|
+
let decoded;
|
|
205
|
+
try {
|
|
206
|
+
decoded = decodeURIComponent(pathname);
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
if (!isPathSafe(pathname, DIST_DIR)) return null;
|
|
211
|
+
const assetPath = resolve(DIST_DIR, decoded.slice(1));
|
|
212
|
+
try {
|
|
213
|
+
const s = statSync(assetPath);
|
|
214
|
+
if (s.isFile()) {
|
|
215
|
+
return new Response(readFileSync(assetPath), {
|
|
216
|
+
headers: { "Content-Type": getContentType(pathname) }
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
if (err.code !== "ENOENT") throw err;
|
|
221
|
+
}
|
|
222
|
+
if (decoded.startsWith("/docs/assets/")) {
|
|
223
|
+
const docsAssetsDir = resolve(DOCS_DIR, "assets");
|
|
224
|
+
const requestedRelative = decoded.slice("/docs/assets/".length);
|
|
225
|
+
const docsAsset = resolve(docsAssetsDir, requestedRelative);
|
|
226
|
+
const docsAssetsDirWithSep = docsAssetsDir.endsWith("/") ? docsAssetsDir : docsAssetsDir + "/";
|
|
227
|
+
if (docsAsset !== docsAssetsDir && !docsAsset.startsWith(docsAssetsDirWithSep)) return null;
|
|
228
|
+
try {
|
|
229
|
+
const s = statSync(docsAsset);
|
|
230
|
+
if (s.isFile()) {
|
|
231
|
+
return new Response(readFileSync(docsAsset), {
|
|
232
|
+
headers: { "Content-Type": getContentType(pathname) }
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
if (err.code !== "ENOENT") throw err;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
function serverErrorResponse(error) {
|
|
242
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
243
|
+
const st = error instanceof Error ? error.stack : void 0;
|
|
244
|
+
return new Response(errorHtml(msg, st), {
|
|
245
|
+
status: 500,
|
|
246
|
+
headers: {
|
|
247
|
+
"Content-Type": "text/html",
|
|
248
|
+
...SECURITY_HEADERS,
|
|
249
|
+
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'"
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// .docu/node/server.impl.ts
|
|
255
|
+
async function runServer(adapter) {
|
|
256
|
+
const docuConfig = loadDocuConfig();
|
|
257
|
+
const parsedPort = parseInt(process.env.PORT ?? "3000", 10);
|
|
258
|
+
const PORT = Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535 ? parsedPort : 3e3;
|
|
259
|
+
logger.buildStart();
|
|
260
|
+
await initSentry();
|
|
261
|
+
logger.bundleStart();
|
|
262
|
+
let t = performance.now();
|
|
263
|
+
const assetManifest = await buildClientBundle();
|
|
264
|
+
logger.bundleDone(Math.round(performance.now() - t));
|
|
265
|
+
const inlineThemeCss = computeInlineThemeCss();
|
|
266
|
+
logger.indexStart();
|
|
267
|
+
t = performance.now();
|
|
268
|
+
const records = await generateSearchIndex();
|
|
269
|
+
logger.indexDone(records, Math.round(performance.now() - t));
|
|
270
|
+
logger.routes();
|
|
271
|
+
const pluginsConfig = docuConfig.plugins ?? [];
|
|
272
|
+
const builder = pluginsConfig.length > 0 ? new BuildPluginBuilder(docuConfig) : null;
|
|
273
|
+
if (builder) {
|
|
274
|
+
const plugins = await loadPlugins(pluginsConfig);
|
|
275
|
+
for (const plugin of plugins) {
|
|
276
|
+
await plugin.setup(builder);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const state = {
|
|
280
|
+
docuConfig,
|
|
281
|
+
assetManifest,
|
|
282
|
+
inlineThemeCss,
|
|
283
|
+
builder
|
|
284
|
+
};
|
|
285
|
+
const hmrClients = /* @__PURE__ */ new Set();
|
|
286
|
+
let hmrTimeout = null;
|
|
287
|
+
const watcher = watch(DOCS_DIR, { recursive: true }, (_event, filename) => {
|
|
288
|
+
if (!filename || !filename.endsWith(".mdx") && !filename.endsWith(".md")) return;
|
|
289
|
+
if (hmrTimeout) clearTimeout(hmrTimeout);
|
|
290
|
+
hmrTimeout = setTimeout(() => {
|
|
291
|
+
for (const client of [...hmrClients]) {
|
|
292
|
+
try {
|
|
293
|
+
client.enqueue(new TextEncoder().encode("data: reload\n\n"));
|
|
294
|
+
} catch {
|
|
295
|
+
hmrClients.delete(client);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}, 300);
|
|
299
|
+
});
|
|
300
|
+
process.on("SIGINT", () => {
|
|
301
|
+
watcher.close();
|
|
302
|
+
process.exit(0);
|
|
303
|
+
});
|
|
304
|
+
process.on("SIGTERM", () => {
|
|
305
|
+
watcher.close();
|
|
306
|
+
process.exit(0);
|
|
307
|
+
});
|
|
308
|
+
let handle = null;
|
|
309
|
+
const fetchHandler = async (req) => {
|
|
310
|
+
const url = new URL(req.url);
|
|
311
|
+
const pathname = stripDocsHtmlSuffix(url.pathname);
|
|
312
|
+
const startTime = performance.now();
|
|
313
|
+
if (builder) {
|
|
314
|
+
const pluginResponse = await builder.runHandleRequest(req, {
|
|
315
|
+
port: handle?.port ?? PORT,
|
|
316
|
+
hostname: handle?.hostname ?? "localhost"
|
|
317
|
+
});
|
|
318
|
+
if (pluginResponse) {
|
|
319
|
+
const securedResponse = wrapPluginResponse(pluginResponse, true);
|
|
320
|
+
logger.request(
|
|
321
|
+
req.method,
|
|
322
|
+
pathname,
|
|
323
|
+
securedResponse.status,
|
|
324
|
+
Math.round(performance.now() - startTime)
|
|
325
|
+
);
|
|
326
|
+
return securedResponse;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
if (pathname === "/__hmr") {
|
|
331
|
+
const stream = new ReadableStream({
|
|
332
|
+
start(controller) {
|
|
333
|
+
hmrClients.add(controller);
|
|
334
|
+
controller.enqueue(new TextEncoder().encode("data: connected\n\n"));
|
|
335
|
+
},
|
|
336
|
+
cancel(controller) {
|
|
337
|
+
hmrClients.delete(controller);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
return new Response(stream, {
|
|
341
|
+
headers: {
|
|
342
|
+
"Content-Type": "text/event-stream",
|
|
343
|
+
"Cache-Control": "no-cache",
|
|
344
|
+
Connection: "keep-alive"
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
if (pathname.startsWith("/assets/") || /\.\w+$/.test(pathname)) {
|
|
349
|
+
const staticRes = serveStatic(pathname);
|
|
350
|
+
if (staticRes) return staticRes;
|
|
351
|
+
}
|
|
352
|
+
let response;
|
|
353
|
+
if (pathname === "/") {
|
|
354
|
+
response = handleIndex(state);
|
|
355
|
+
} else if (pathname === "/docs" || pathname === "/docs/") {
|
|
356
|
+
response = await handleDocsIndex(state);
|
|
357
|
+
} else if (pathname.startsWith("/docs/")) {
|
|
358
|
+
const slug = pathname.slice("/docs/".length).split("/").filter(Boolean);
|
|
359
|
+
response = slug.length === 0 ? await handleDocsIndex(state) : await handleDocsRoute(slug, state);
|
|
360
|
+
} else {
|
|
361
|
+
response = handleNotFound(state);
|
|
362
|
+
}
|
|
363
|
+
logger.request(
|
|
364
|
+
req.method,
|
|
365
|
+
pathname,
|
|
366
|
+
response.status,
|
|
367
|
+
Math.round(performance.now() - startTime)
|
|
368
|
+
);
|
|
369
|
+
return response;
|
|
370
|
+
} catch (err) {
|
|
371
|
+
captureException(err, { method: req.method, pathname });
|
|
372
|
+
logger.request(req.method, pathname, 500, Math.round(performance.now() - startTime));
|
|
373
|
+
return serverErrorResponse(err);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
handle = await adapter.serve(fetchHandler, { port: PORT, idleTimeout: 255 });
|
|
377
|
+
logger.ready(handle.port, true);
|
|
378
|
+
return handle;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export {
|
|
382
|
+
runServer
|
|
383
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SECURITY_HEADERS,
|
|
3
|
+
cspHeader,
|
|
4
|
+
generateNonce,
|
|
5
|
+
getContentType,
|
|
6
|
+
injectNonce
|
|
7
|
+
} from "./chunk-RE4NGTMT.js";
|
|
8
|
+
import {
|
|
9
|
+
logger
|
|
10
|
+
} from "./chunk-HH4YXWEF.js";
|
|
11
|
+
import {
|
|
12
|
+
DIST_DIR
|
|
13
|
+
} from "./chunk-J5NMYSBJ.js";
|
|
14
|
+
|
|
15
|
+
// .docu/node/preview.impl.ts
|
|
16
|
+
import { existsSync, statSync, readFileSync } from "node:fs";
|
|
17
|
+
import { readFile } from "node:fs/promises";
|
|
18
|
+
import { resolve } from "node:path";
|
|
19
|
+
function resolveFile(pathname) {
|
|
20
|
+
const path = pathname.slice(1);
|
|
21
|
+
const isWithinDist = (p) => p === DIST_DIR || p.startsWith(DIST_DIR + "/");
|
|
22
|
+
if (!path.includes(".")) {
|
|
23
|
+
const withIndex = resolve(DIST_DIR, path, "index.html");
|
|
24
|
+
if (isWithinDist(withIndex) && existsSync(withIndex)) return withIndex;
|
|
25
|
+
const withHtml = resolve(DIST_DIR, path + ".html");
|
|
26
|
+
if (isWithinDist(withHtml) && existsSync(withHtml)) return withHtml;
|
|
27
|
+
}
|
|
28
|
+
const exact = resolve(DIST_DIR, path);
|
|
29
|
+
if (!isWithinDist(exact)) return null;
|
|
30
|
+
try {
|
|
31
|
+
if (statSync(exact).isFile()) return exact;
|
|
32
|
+
} catch (err) {
|
|
33
|
+
if (err.code !== "ENOENT") throw err;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
async function runPreview(adapter) {
|
|
38
|
+
const PORT = parseInt(process.env.PORT || "4173", 10);
|
|
39
|
+
logger.buildStart();
|
|
40
|
+
if (!existsSync(DIST_DIR)) {
|
|
41
|
+
logger.spinner.start("Checking build output...");
|
|
42
|
+
logger.spinner.info("dist not found. Run \x1B[1mflame build\x1B[0m first.");
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
const notFoundPath = resolve(DIST_DIR, "404.html");
|
|
46
|
+
const handle = await adapter.serve(
|
|
47
|
+
async (req) => {
|
|
48
|
+
const url = new URL(req.url);
|
|
49
|
+
const pathname = decodeURIComponent(url.pathname);
|
|
50
|
+
const filePath = resolveFile(pathname);
|
|
51
|
+
if (filePath) {
|
|
52
|
+
const contentType = getContentType(filePath);
|
|
53
|
+
if (contentType === "text/html") {
|
|
54
|
+
const nonce = generateNonce();
|
|
55
|
+
const html = await readFile(filePath, "utf-8");
|
|
56
|
+
const modified = injectNonce(html, nonce);
|
|
57
|
+
return new Response(modified, {
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "text/html",
|
|
60
|
+
...SECURITY_HEADERS,
|
|
61
|
+
"Content-Security-Policy": cspHeader(nonce, true)
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return new Response(readFileSync(filePath), {
|
|
66
|
+
headers: { "Content-Type": contentType }
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (existsSync(notFoundPath)) {
|
|
70
|
+
const nonce = generateNonce();
|
|
71
|
+
const html = await readFile(notFoundPath, "utf-8");
|
|
72
|
+
const modified = injectNonce(html, nonce);
|
|
73
|
+
return new Response(modified, {
|
|
74
|
+
status: 404,
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "text/html",
|
|
77
|
+
...SECURITY_HEADERS,
|
|
78
|
+
"Content-Security-Policy": cspHeader(nonce, true)
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return new Response("404 - Not Found", { status: 404 });
|
|
83
|
+
},
|
|
84
|
+
{ port: PORT }
|
|
85
|
+
);
|
|
86
|
+
logger.ready(handle.port);
|
|
87
|
+
return handle;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
runPreview
|
|
92
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-HH4YXWEF.js";
|
|
4
|
+
import "./chunk-J5NMYSBJ.js";
|
|
5
|
+
|
|
6
|
+
// .docu/node/clean.ts
|
|
7
|
+
import { rm } from "node:fs/promises";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
var DIST = ".docu/dist";
|
|
10
|
+
var CACHE = ".docu/build-cache.json";
|
|
11
|
+
async function clean() {
|
|
12
|
+
logger.buildStart();
|
|
13
|
+
logger.spinner.start("Cleaning build artifacts...");
|
|
14
|
+
let removed = 0;
|
|
15
|
+
if (existsSync(DIST)) {
|
|
16
|
+
await rm(DIST, { recursive: true, force: true });
|
|
17
|
+
removed++;
|
|
18
|
+
}
|
|
19
|
+
if (existsSync(CACHE)) {
|
|
20
|
+
await rm(CACHE, { force: true });
|
|
21
|
+
removed++;
|
|
22
|
+
}
|
|
23
|
+
if (removed === 0) {
|
|
24
|
+
logger.spinner.info("Nothing to clean");
|
|
25
|
+
} else {
|
|
26
|
+
logger.spinner.stop(`Cleaned ${removed} item(s)`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
clean().catch((err) => {
|
|
30
|
+
console.error("Clean failed:", err);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runPreview
|
|
3
|
+
} from "./chunk-ZOWTASXL.js";
|
|
4
|
+
import "./chunk-RE4NGTMT.js";
|
|
5
|
+
import "./chunk-HH4YXWEF.js";
|
|
6
|
+
import "./chunk-J5NMYSBJ.js";
|
|
7
|
+
|
|
8
|
+
// .docu/node/preview.deno.ts
|
|
9
|
+
import { denoAdapter } from "@docubook/runt";
|
|
10
|
+
await runPreview(denoAdapter);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runPreview
|
|
3
|
+
} from "./chunk-ZOWTASXL.js";
|
|
4
|
+
import "./chunk-RE4NGTMT.js";
|
|
5
|
+
import "./chunk-HH4YXWEF.js";
|
|
6
|
+
import "./chunk-J5NMYSBJ.js";
|
|
7
|
+
|
|
8
|
+
// .docu/node/preview.node.ts
|
|
9
|
+
import { nodeAdapter } from "@docubook/runt";
|
|
10
|
+
await runPreview(nodeAdapter);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runServer
|
|
3
|
+
} from "./chunk-X6GYOIYZ.js";
|
|
4
|
+
import "./chunk-2QHMGZIL.js";
|
|
5
|
+
import "./chunk-RE4NGTMT.js";
|
|
6
|
+
import "./chunk-HH4YXWEF.js";
|
|
7
|
+
import "./chunk-J5NMYSBJ.js";
|
|
8
|
+
|
|
9
|
+
// .docu/node/server.deno.ts
|
|
10
|
+
import { denoAdapter } from "@docubook/runt";
|
|
11
|
+
await runServer(denoAdapter);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runServer
|
|
3
|
+
} from "./chunk-X6GYOIYZ.js";
|
|
4
|
+
import "./chunk-2QHMGZIL.js";
|
|
5
|
+
import "./chunk-RE4NGTMT.js";
|
|
6
|
+
import "./chunk-HH4YXWEF.js";
|
|
7
|
+
import "./chunk-J5NMYSBJ.js";
|
|
8
|
+
|
|
9
|
+
// .docu/node/server.node.ts
|
|
10
|
+
import { nodeAdapter } from "@docubook/runt";
|
|
11
|
+
await runServer(nodeAdapter);
|