@docubook/flame 1.0.0-beta.40 → 1.0.0-beta.50
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.ts +32 -64
- package/.docu/lib/client.ts +48 -34
- package/.docu/lib/fs-scanner.ts +4 -5
- package/.docu/lib/html.ts +31 -0
- package/.docu/lib/hydrate.ts +4 -1
- package/.docu/lib/paths.ts +3 -2
- package/.docu/lib/search-indexer.ts +12 -8
- package/.docu/lib/server.ts +15 -23
- package/bin/cli.js +35 -19
- package/package.json +1 -1
- package/template/gitignore +4 -0
- package/template/package.json +1 -1
package/.docu/lib/build.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
PROJECT_ROOT,
|
|
24
24
|
loadDocuConfig,
|
|
25
25
|
} from "./paths";
|
|
26
|
+
import { htmlShell as createHtmlShell } from "./html";
|
|
26
27
|
import { generateSearchIndex } from "./search-indexer";
|
|
27
28
|
import { buildClientBundle } from "./hydrate";
|
|
28
29
|
import { logger } from "./logger";
|
|
@@ -101,22 +102,14 @@ let assetManifest = { js: "client.js", css: "client.css" };
|
|
|
101
102
|
|
|
102
103
|
function htmlShell(title: string, description: string, body: string): string {
|
|
103
104
|
const favicon = docuConfig.meta?.favicon || "/favicon.ico";
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<link rel="stylesheet" href="/assets/${assetManifest.css}">
|
|
113
|
-
<script>try{if(localStorage.getItem("theme")==="dark")document.documentElement.classList.add("dark")}catch(e){}</script>
|
|
114
|
-
</head>
|
|
115
|
-
<body>
|
|
116
|
-
<div id="root">${body}</div>
|
|
117
|
-
<script src="/assets/${assetManifest.js}"></script>
|
|
118
|
-
</body>
|
|
119
|
-
</html>`;
|
|
105
|
+
return createHtmlShell({
|
|
106
|
+
title,
|
|
107
|
+
description,
|
|
108
|
+
body,
|
|
109
|
+
favicon,
|
|
110
|
+
css: assetManifest.css,
|
|
111
|
+
js: assetManifest.js,
|
|
112
|
+
});
|
|
120
113
|
}
|
|
121
114
|
|
|
122
115
|
async function renderDocsPage(slug: string, rawMdx: string, filePath: string): Promise<string> {
|
|
@@ -142,12 +135,17 @@ async function renderDocsPage(slug: string, rawMdx: string, filePath: string): P
|
|
|
142
135
|
throw new Error(`MDX Error in: docs/${slug}.mdx\n${msg}`, { cause: err });
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
const content = React.createElement(MDXRemote, {
|
|
138
|
+
const content = React.createElement(MDXRemote, {
|
|
139
|
+
compiledSource,
|
|
140
|
+
scope: {},
|
|
141
|
+
frontmatter: {},
|
|
142
|
+
components,
|
|
143
|
+
});
|
|
146
144
|
|
|
147
|
-
const title = frontmatter.title || slug;
|
|
145
|
+
const title = frontmatter.title || slug || "Docs";
|
|
148
146
|
const description = frontmatter.description || "";
|
|
149
147
|
const date = frontmatter.date || (await getGitLastModified(filePath));
|
|
150
|
-
const slugParts = slug.split("/");
|
|
148
|
+
const slugParts = slug ? slug.split("/") : [];
|
|
151
149
|
|
|
152
150
|
const page = React.createElement(
|
|
153
151
|
DocsLayout,
|
|
@@ -227,7 +225,7 @@ async function build() {
|
|
|
227
225
|
logger.spinner.start("Building pages...");
|
|
228
226
|
t = performance.now();
|
|
229
227
|
|
|
230
|
-
const CONCURRENCY = 10;
|
|
228
|
+
const CONCURRENCY = parseInt(process.env.BUILD_CONCURRENCY || "10", 10);
|
|
231
229
|
const buildTasks = [];
|
|
232
230
|
const errors: string[] = [];
|
|
233
231
|
|
|
@@ -287,48 +285,18 @@ async function build() {
|
|
|
287
285
|
await Promise.all(buildTasks.slice(i, i + CONCURRENCY).map((fn) => fn()));
|
|
288
286
|
}
|
|
289
287
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
},
|
|
303
|
-
});
|
|
304
|
-
const indexContent = React.createElement(MDXRemote, {
|
|
305
|
-
compiledSource: indexSerialized.compiledSource,
|
|
306
|
-
components: createMdxComponents(),
|
|
307
|
-
});
|
|
308
|
-
const indexRelPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
|
|
309
|
-
const indexDate = indexFm.date || (await getGitLastModified(indexRelPath)) || undefined;
|
|
310
|
-
const indexPageEl = React.createElement(
|
|
311
|
-
DocsLayout,
|
|
312
|
-
{ repoUrl: docuConfig.repo?.url },
|
|
313
|
-
React.createElement(DocsPage, {
|
|
314
|
-
slug: [],
|
|
315
|
-
title: indexFm.title || "Docs",
|
|
316
|
-
description: indexFm.description || "",
|
|
317
|
-
date: indexDate,
|
|
318
|
-
content: indexContent,
|
|
319
|
-
tocs: indexTocs,
|
|
320
|
-
filePath: indexRelPath,
|
|
321
|
-
repoUrl: docuConfig.repo?.url,
|
|
322
|
-
compiledSource: indexSerialized.compiledSource,
|
|
323
|
-
})
|
|
324
|
-
);
|
|
325
|
-
const indexHtml = htmlShell(
|
|
326
|
-
indexFm.title || docuConfig.meta?.title || "DocuBook",
|
|
327
|
-
indexFm.description || docuConfig.meta?.description || "",
|
|
328
|
-
renderToString(indexPageEl)
|
|
329
|
-
);
|
|
330
|
-
await mkdir(join(DIST_DIR, "docs"), { recursive: true });
|
|
331
|
-
await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
|
|
288
|
+
try {
|
|
289
|
+
const indexMdxPath = join(DOCS_DIR, "index.mdx");
|
|
290
|
+
const indexRaw = await readFile(indexMdxPath, "utf-8");
|
|
291
|
+
const indexRelPath = indexMdxPath.replace(PROJECT_ROOT + "/", "");
|
|
292
|
+
const indexHtml = await renderDocsPage("", indexRaw, indexRelPath);
|
|
293
|
+
await mkdir(join(DIST_DIR, "docs"), { recursive: true });
|
|
294
|
+
await writeFile(join(DIST_DIR, "docs", "index.html"), indexHtml);
|
|
295
|
+
} catch (err) {
|
|
296
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
297
|
+
errors.push(`index.mdx: ${msg}`);
|
|
298
|
+
console.error(`\n\u274C Failed to build index: ${msg}\n`);
|
|
299
|
+
}
|
|
332
300
|
|
|
333
301
|
const landingPage = React.createElement(IndexPage);
|
|
334
302
|
const landingHtml = htmlShell(
|
|
@@ -358,12 +326,12 @@ async function build() {
|
|
|
358
326
|
logger.routes();
|
|
359
327
|
console.log("");
|
|
360
328
|
|
|
329
|
+
await writeCache(cache);
|
|
330
|
+
|
|
361
331
|
if (errors.length > 0) {
|
|
362
332
|
console.error(`\n\u274C Build completed with ${errors.length} error(s)\n`);
|
|
363
333
|
process.exit(1);
|
|
364
334
|
}
|
|
365
|
-
|
|
366
|
-
await writeCache(cache);
|
|
367
335
|
}
|
|
368
336
|
|
|
369
337
|
initSentry()
|
package/.docu/lib/client.ts
CHANGED
|
@@ -15,43 +15,55 @@ function safeParseTocs(raw: string | undefined): TocItem[] {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
function mountIsland(
|
|
19
|
+
id: string,
|
|
20
|
+
render: (el: HTMLElement) => React.ReactElement,
|
|
21
|
+
forceCreate = false
|
|
22
|
+
) {
|
|
23
|
+
const el = document.getElementById(id);
|
|
24
|
+
if (!el) return;
|
|
25
|
+
const node = render(el);
|
|
26
|
+
if (!forceCreate && el.childElementCount > 0) {
|
|
27
|
+
hydrateRoot(el, node);
|
|
28
|
+
} else {
|
|
29
|
+
el.innerHTML = "";
|
|
30
|
+
createRoot(el).render(node);
|
|
30
31
|
}
|
|
32
|
+
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
function mountIslands() {
|
|
35
|
+
mountIsland(
|
|
36
|
+
"sidebar-island",
|
|
37
|
+
(el) => {
|
|
38
|
+
const tocs: TocItem[] = safeParseTocs(el.dataset.tocs);
|
|
39
|
+
return React.createElement(Sidebar, {
|
|
40
|
+
tocs,
|
|
41
|
+
title: el.dataset.title || "",
|
|
42
|
+
repoUrl: el.dataset.repo || "",
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
true
|
|
46
|
+
);
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
mountIsland(
|
|
49
|
+
"mobile-bar-island",
|
|
50
|
+
(el) => {
|
|
51
|
+
const tocs: TocItem[] = safeParseTocs(el.dataset.tocs);
|
|
52
|
+
return React.createElement(MobileBar, {
|
|
53
|
+
tocs,
|
|
54
|
+
title: el.dataset.title || "",
|
|
55
|
+
repoUrl: el.dataset.repo || "",
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
true
|
|
59
|
+
);
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
61
|
+
mountIsland("toc-island", (el) => {
|
|
62
|
+
const tocs: TocItem[] = safeParseTocs(el.dataset.tocs);
|
|
63
|
+
return React.createElement(Toc, { tocs });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
mountIsland("theme-island", () => React.createElement(ThemeToggle));
|
|
55
67
|
|
|
56
68
|
hydrateMdxContent();
|
|
57
69
|
}
|
|
@@ -64,7 +76,9 @@ function hydrateMdxContent() {
|
|
|
64
76
|
try {
|
|
65
77
|
const compiledSource = JSON.parse(sourceEl.textContent || "");
|
|
66
78
|
const components = createMdxComponents();
|
|
67
|
-
createRoot(island).render(
|
|
79
|
+
createRoot(island).render(
|
|
80
|
+
React.createElement(MDXRemote, { compiledSource, scope: {}, frontmatter: {}, components })
|
|
81
|
+
);
|
|
68
82
|
} catch (e) {
|
|
69
83
|
console.error("[mdx-hydrate]", e);
|
|
70
84
|
}
|
package/.docu/lib/fs-scanner.ts
CHANGED
|
@@ -62,7 +62,7 @@ function scanDir(dirPath: string, docsRoot: string): FileNode[] {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
for (const entry of entries) {
|
|
65
|
-
if (entry.startsWith(".")) continue;
|
|
65
|
+
if (entry.startsWith(".") || entry === "assets") continue;
|
|
66
66
|
|
|
67
67
|
const absPath = join(dirPath, entry);
|
|
68
68
|
|
|
@@ -107,7 +107,7 @@ function fileNodesToRoutes(nodes: FileNode[], parentHref = ""): DocuRoute[] {
|
|
|
107
107
|
if (isIndexFile) continue;
|
|
108
108
|
|
|
109
109
|
const segment = node.relPath.split("/").pop()!;
|
|
110
|
-
const href = parentHref ?
|
|
110
|
+
const href = parentHref ? `${parentHref}/${segment}` : `/${node.relPath}`;
|
|
111
111
|
|
|
112
112
|
routes.push({
|
|
113
113
|
title: toTitleCase(baseName),
|
|
@@ -116,10 +116,9 @@ function fileNodesToRoutes(nodes: FileNode[], parentHref = ""): DocuRoute[] {
|
|
|
116
116
|
} else {
|
|
117
117
|
const dirTitle = toTitleCase(node.name);
|
|
118
118
|
const segment = node.relPath.split("/").pop()!;
|
|
119
|
-
const dirHref = parentHref ?
|
|
119
|
+
const dirHref = parentHref ? `${parentHref}/${segment}` : `/${node.relPath}`;
|
|
120
120
|
|
|
121
|
-
const
|
|
122
|
-
const children = fileNodesToRoutes(node.children || [], fullDirHref);
|
|
121
|
+
const children = fileNodesToRoutes(node.children || [], dirHref);
|
|
123
122
|
|
|
124
123
|
if (children.length === 0) continue;
|
|
125
124
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface HtmlShellOptions {
|
|
2
|
+
title: string;
|
|
3
|
+
description: string;
|
|
4
|
+
body: string;
|
|
5
|
+
favicon: string;
|
|
6
|
+
css: string;
|
|
7
|
+
js: string;
|
|
8
|
+
nonce?: string;
|
|
9
|
+
extraScripts?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function htmlShell(opts: HtmlShellOptions): string {
|
|
13
|
+
const { title, description, body, favicon, css, js, nonce, extraScripts } = opts;
|
|
14
|
+
const nonceAttr = nonce ? ` nonce="${Bun.escapeHTML(nonce)}"` : "";
|
|
15
|
+
return `<!DOCTYPE html>
|
|
16
|
+
<html lang="en">
|
|
17
|
+
<head>
|
|
18
|
+
<meta charset="UTF-8">
|
|
19
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
20
|
+
<title>${Bun.escapeHTML(title)}</title>
|
|
21
|
+
<meta name="description" content="${Bun.escapeHTML(description)}">
|
|
22
|
+
<link rel="icon" type="image/x-icon" href="${Bun.escapeHTML(favicon)}">
|
|
23
|
+
<link rel="stylesheet" href="/assets/${Bun.escapeHTML(css)}">
|
|
24
|
+
<script${nonceAttr}>try{if(localStorage.getItem("theme")==="dark")document.documentElement.classList.add("dark")}catch(e){}</script>
|
|
25
|
+
</head>
|
|
26
|
+
<body>
|
|
27
|
+
<div id="root">${body}</div>
|
|
28
|
+
<script${nonceAttr} src="/assets/${Bun.escapeHTML(js)}"></script>${extraScripts ? `\n ${extraScripts}` : ""}
|
|
29
|
+
</body>
|
|
30
|
+
</html>`;
|
|
31
|
+
}
|
package/.docu/lib/hydrate.ts
CHANGED
|
@@ -70,7 +70,10 @@ export async function buildClientBundle(): Promise<{ js: string; css: string }>
|
|
|
70
70
|
throw new Error("Client bundle failed");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
if (!result.outputs[0]) {
|
|
74
|
+
throw new Error("Client bundle produced no output files");
|
|
75
|
+
}
|
|
76
|
+
const jsFile = result.outputs[0].path.split("/").pop()!;
|
|
74
77
|
const tmpCss = join(ASSETS_DIR, "_tmp.css");
|
|
75
78
|
const proc = Bun.spawn(
|
|
76
79
|
[
|
package/.docu/lib/paths.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve, join } from "node:path";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import type { DocuConfig } from "./types";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* FRAMEWORK_ROOT: Where the package code lives (.docu/components, .docu/pages, .docu/styles, .docu/lib)
|
|
@@ -26,9 +27,9 @@ export const DOCS_ASSETS_DIR = join(PROJECT_ROOT, "docs/assets");
|
|
|
26
27
|
export const DOCU_CONFIG_PATH = join(PROJECT_ROOT, "docu.json");
|
|
27
28
|
|
|
28
29
|
// Config singleton
|
|
29
|
-
let _config:
|
|
30
|
+
let _config: DocuConfig | null = null;
|
|
30
31
|
|
|
31
|
-
export function loadDocuConfig():
|
|
32
|
+
export function loadDocuConfig(): DocuConfig {
|
|
32
33
|
if (_config) return _config;
|
|
33
34
|
if (!existsSync(DOCU_CONFIG_PATH)) {
|
|
34
35
|
throw new Error(`docu.json not found at ${DOCU_CONFIG_PATH}`);
|
|
@@ -200,7 +200,11 @@ async function scanMdxFiles(dir: string, base = ""): Promise<{ path: string; abs
|
|
|
200
200
|
files.push(...(await scanMdxFiles(fullPath, relPath)));
|
|
201
201
|
} else if (entry.name.endsWith(".mdx") || entry.name.endsWith(".md")) {
|
|
202
202
|
if (entry.name === "index.mdx" && !base) continue;
|
|
203
|
-
|
|
203
|
+
let path = relPath.replace(/\.(mdx|md)$/, "");
|
|
204
|
+
if (/\/index$/.test(path)) {
|
|
205
|
+
path = path.replace(/\/index$/, "");
|
|
206
|
+
}
|
|
207
|
+
files.push({ path, absPath: fullPath });
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
return files;
|
|
@@ -212,13 +216,13 @@ export async function generateSearchIndex(docsDir?: string, outputDir?: string):
|
|
|
212
216
|
await mkdir(dist, { recursive: true });
|
|
213
217
|
|
|
214
218
|
const mdxFiles = await scanMdxFiles(docs);
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
const results = await Promise.all(
|
|
220
|
+
mdxFiles.map(async (file) => {
|
|
221
|
+
const raw = await readFile(file.absPath, "utf-8");
|
|
222
|
+
return extractRecords(file.path, raw);
|
|
223
|
+
})
|
|
224
|
+
);
|
|
225
|
+
const allRecords = results.flat();
|
|
222
226
|
|
|
223
227
|
await writeFile(join(dist, "search-index.json"), JSON.stringify(allRecords));
|
|
224
228
|
return allRecords.length;
|
package/.docu/lib/server.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { generateSearchIndex } from "./search-indexer";
|
|
|
24
24
|
import { logger } from "./logger";
|
|
25
25
|
import { initSentry, captureException } from "./sentry";
|
|
26
26
|
import { SECURITY_HEADERS, generateNonce, htmlResponse } from "./security";
|
|
27
|
+
import { htmlShell as createHtmlShell } from "./html";
|
|
27
28
|
|
|
28
29
|
const docuConfig = loadDocuConfig();
|
|
29
30
|
|
|
@@ -58,7 +59,7 @@ try {
|
|
|
58
59
|
const hmrClients = new Set<ReadableStreamDefaultController>();
|
|
59
60
|
|
|
60
61
|
function hmrScript(nonce: string): string {
|
|
61
|
-
return `<script nonce="${nonce}">
|
|
62
|
+
return `<script nonce="${Bun.escapeHTML(nonce)}">
|
|
62
63
|
(function(){
|
|
63
64
|
const es = new EventSource("/__hmr");
|
|
64
65
|
es.onmessage = function(e) {
|
|
@@ -93,27 +94,6 @@ process.on("SIGTERM", () => {
|
|
|
93
94
|
process.exit(0);
|
|
94
95
|
});
|
|
95
96
|
|
|
96
|
-
function htmlShell(title: string, description: string, body: string, nonce: string): string {
|
|
97
|
-
const favicon = docuConfig.meta?.favicon || "/favicon.ico";
|
|
98
|
-
return `<!DOCTYPE html>
|
|
99
|
-
<html lang="en">
|
|
100
|
-
<head>
|
|
101
|
-
<meta charset="UTF-8">
|
|
102
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
103
|
-
<title>${Bun.escapeHTML(title)}</title>
|
|
104
|
-
<meta name="description" content="${Bun.escapeHTML(description)}">
|
|
105
|
-
<link rel="icon" type="image/x-icon" href="${Bun.escapeHTML(favicon)}">
|
|
106
|
-
<link rel="stylesheet" href="/assets/${assetManifest.css}">
|
|
107
|
-
<script nonce="${nonce}">try{if(localStorage.getItem("theme")==="dark")document.documentElement.classList.add("dark")}catch(e){}</script>
|
|
108
|
-
</head>
|
|
109
|
-
<body>
|
|
110
|
-
<div id="root">${body}</div>
|
|
111
|
-
<script nonce="${nonce}" src="/assets/${assetManifest.js}"></script>
|
|
112
|
-
${hmrScript(nonce)}
|
|
113
|
-
</body>
|
|
114
|
-
</html>`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
97
|
function createHtmlResponse(
|
|
118
98
|
title: string,
|
|
119
99
|
description: string,
|
|
@@ -121,7 +101,17 @@ function createHtmlResponse(
|
|
|
121
101
|
status = 200
|
|
122
102
|
): Response {
|
|
123
103
|
const nonce = generateNonce();
|
|
124
|
-
const
|
|
104
|
+
const favicon = docuConfig.meta?.favicon || "/favicon.ico";
|
|
105
|
+
const html = createHtmlShell({
|
|
106
|
+
title,
|
|
107
|
+
description,
|
|
108
|
+
body,
|
|
109
|
+
favicon,
|
|
110
|
+
css: assetManifest.css,
|
|
111
|
+
js: assetManifest.js,
|
|
112
|
+
nonce,
|
|
113
|
+
extraScripts: hmrScript(nonce),
|
|
114
|
+
});
|
|
125
115
|
return htmlResponse(html, nonce, status);
|
|
126
116
|
}
|
|
127
117
|
|
|
@@ -165,6 +155,8 @@ async function getDocsForSlug(slug: string) {
|
|
|
165
155
|
});
|
|
166
156
|
const content = React.createElement(MDXRemote, {
|
|
167
157
|
compiledSource: serialized.compiledSource,
|
|
158
|
+
scope: {},
|
|
159
|
+
frontmatter: {},
|
|
168
160
|
components,
|
|
169
161
|
});
|
|
170
162
|
|
package/bin/cli.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
/* global process, console */
|
|
3
3
|
|
|
4
4
|
import { resolve, join } from "node:path";
|
|
5
|
-
import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { cpSync, existsSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
const __dirname = import.meta.dirname;
|
|
6
8
|
|
|
7
9
|
const COMMAND_MAP = {
|
|
8
10
|
dev: "server.ts",
|
|
@@ -16,7 +18,7 @@ const command = process.argv[2];
|
|
|
16
18
|
|
|
17
19
|
if (!command || command === "--help" || command === "-h") {
|
|
18
20
|
console.log(`
|
|
19
|
-
@docubook/flame —
|
|
21
|
+
@docubook/flame — A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.
|
|
20
22
|
|
|
21
23
|
Usage: flame <command>
|
|
22
24
|
|
|
@@ -35,26 +37,40 @@ if (!command || command === "--help" || command === "-h") {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
if (command === "init") {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
try {
|
|
41
|
+
const scaffoldDir = resolve(__dirname, "../template");
|
|
42
|
+
const targetDir = process.cwd();
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
if (!existsSync(scaffoldDir)) {
|
|
45
|
+
console.error("Template directory not found. Package may be corrupted.");
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
45
48
|
|
|
46
|
-
|
|
49
|
+
if (existsSync(join(targetDir, "docu.json"))) {
|
|
50
|
+
console.error("docu.json already exists. Aborting to avoid overwriting.");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
50
|
-
const flamePkg = JSON.parse(
|
|
51
|
-
readFileSync(resolve(import.meta.dirname, "../package.json"), "utf-8")
|
|
52
|
-
);
|
|
53
|
-
pkg.dependencies["@docubook/flame"] = `^${flamePkg.version}`;
|
|
54
|
-
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
54
|
+
cpSync(scaffoldDir, targetDir, { recursive: true });
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const gitignoreSrc = join(targetDir, "gitignore");
|
|
57
|
+
if (existsSync(gitignoreSrc)) {
|
|
58
|
+
renameSync(gitignoreSrc, join(targetDir, ".gitignore"));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pkgPath = join(targetDir, "package.json");
|
|
62
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
63
|
+
const flamePkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf-8"));
|
|
64
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
65
|
+
pkg.dependencies["@docubook/flame"] = `^${flamePkg.version}`;
|
|
66
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
67
|
+
|
|
68
|
+
console.log(`\n ✓ Project scaffolded!\n\n Next steps:\n bun install\n bun run dev\n`);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(`Failed to scaffold project: ${err.message}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
if (!(command in COMMAND_MAP)) {
|
|
@@ -62,5 +78,5 @@ if (!(command in COMMAND_MAP)) {
|
|
|
62
78
|
process.exit(1);
|
|
63
79
|
}
|
|
64
80
|
|
|
65
|
-
const scriptPath = resolve(
|
|
81
|
+
const scriptPath = resolve(__dirname, "../.docu/lib", COMMAND_MAP[command]);
|
|
66
82
|
await import(scriptPath);
|
package/package.json
CHANGED
package/template/package.json
CHANGED