@fumadocs/cli 1.5.0 → 1.6.1
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/dist/ai-C19ZOP53.js +100 -0
- package/dist/ai-C19ZOP53.js.map +1 -0
- package/dist/all-BBf7I8hG.js +27 -0
- package/dist/all-BBf7I8hG.js.map +1 -0
- package/dist/codemod/index.d.ts +2 -0
- package/dist/codemod/index.js +2 -0
- package/dist/codemod-auol-p4R.js +404 -0
- package/dist/codemod-auol-p4R.js.map +1 -0
- package/dist/config-Bfg4yG8S.d.ts +54 -0
- package/dist/config-Bfg4yG8S.d.ts.map +1 -0
- package/dist/config-Bw0kmF01.js +97 -0
- package/dist/config-Bw0kmF01.js.map +1 -0
- package/dist/config.d.ts +2 -52
- package/dist/config.js +1 -72
- package/dist/docs--WqEHTyl.js +738 -0
- package/dist/docs--WqEHTyl.js.map +1 -0
- package/dist/epub-jc_2B886.js +52 -0
- package/dist/epub-jc_2B886.js.map +1 -0
- package/dist/features/ai.d.ts +25 -0
- package/dist/features/ai.d.ts.map +1 -0
- package/dist/features/ai.js +2 -0
- package/dist/features/all.d.ts +5 -0
- package/dist/features/all.d.ts.map +1 -0
- package/dist/features/all.js +2 -0
- package/dist/features/docs/index.d.ts +10 -0
- package/dist/features/docs/index.d.ts.map +1 -0
- package/dist/features/docs/index.js +2 -0
- package/dist/features/epub.d.ts +5 -0
- package/dist/features/epub.d.ts.map +1 -0
- package/dist/features/epub.js +2 -0
- package/dist/features/feedback.d.ts +5 -0
- package/dist/features/feedback.d.ts.map +1 -0
- package/dist/features/feedback.js +2 -0
- package/dist/features/index.d.ts +2 -0
- package/dist/features/index.js +2 -0
- package/dist/features/lint.d.ts +7 -0
- package/dist/features/lint.d.ts.map +1 -0
- package/dist/features/lint.js +2 -0
- package/dist/features/llms.d.ts +27 -0
- package/dist/features/llms.d.ts.map +1 -0
- package/dist/features/llms.js +2 -0
- package/dist/features/mcp.d.ts +9 -0
- package/dist/features/mcp.d.ts.map +1 -0
- package/dist/features/mcp.js +2 -0
- package/dist/features/og.d.ts +8 -0
- package/dist/features/og.d.ts.map +1 -0
- package/dist/features/og.js +2 -0
- package/dist/features/search.d.ts +84 -0
- package/dist/features/search.d.ts.map +1 -0
- package/dist/features/search.js +2 -0
- package/dist/features/utils.d.ts +2 -0
- package/dist/features/utils.js +2 -0
- package/dist/features/webmcp.d.ts +12 -0
- package/dist/features/webmcp.d.ts.map +1 -0
- package/dist/features/webmcp.js +2 -0
- package/dist/features-CXFCgOWD.js +132 -0
- package/dist/features-CXFCgOWD.js.map +1 -0
- package/dist/feedback-Brj092vC.js +37 -0
- package/dist/feedback-Brj092vC.js.map +1 -0
- package/dist/fs-D6J9dOEx.js +14 -0
- package/dist/fs-D6J9dOEx.js.map +1 -0
- package/dist/index-Ck0EPdgu.d.ts +126 -0
- package/dist/index-Ck0EPdgu.d.ts.map +1 -0
- package/dist/index-Cs7hw2un.d.ts +76 -0
- package/dist/index-Cs7hw2un.d.ts.map +1 -0
- package/dist/index-DI66DoV8.d.ts +64 -0
- package/dist/index-DI66DoV8.d.ts.map +1 -0
- package/dist/index.js +155 -139
- package/dist/index.js.map +1 -1
- package/dist/{installer-Ci3Hf9it.js → installer-CzhVfs7i.js} +81 -8
- package/dist/installer-CzhVfs7i.js.map +1 -0
- package/dist/lint-zkXUULgO.js +137 -0
- package/dist/lint-zkXUULgO.js.map +1 -0
- package/dist/llms-Br0RJXXg.js +228 -0
- package/dist/llms-Br0RJXXg.js.map +1 -0
- package/dist/mcp-Ce_W93we.js +67 -0
- package/dist/mcp-Ce_W93we.js.map +1 -0
- package/dist/og-LFlX1Bl4.js +142 -0
- package/dist/og-LFlX1Bl4.js.map +1 -0
- package/dist/project/index.d.ts +2 -0
- package/dist/project/index.js +2 -0
- package/dist/project-DwRVTm4p.js +110 -0
- package/dist/project-DwRVTm4p.js.map +1 -0
- package/dist/registry/installer.d.ts +4 -5
- package/dist/registry/installer.d.ts.map +1 -1
- package/dist/registry/installer.js +1 -1
- package/dist/schema.json +1 -1
- package/dist/search-WfSdiGx-.js +381 -0
- package/dist/search-WfSdiGx-.js.map +1 -0
- package/dist/utils-CxJ7kw39.d.ts +43 -0
- package/dist/utils-CxJ7kw39.d.ts.map +1 -0
- package/dist/utils-DbMe7h6c.js +98 -0
- package/dist/utils-DbMe7h6c.js.map +1 -0
- package/dist/webmcp-DyTLrsXL.js +135 -0
- package/dist/webmcp-DyTLrsXL.js.map +1 -0
- package/package.json +28 -7
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/installer-Ci3Hf9it.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { i as initConfig, n as createOrLoadConfig } from "./config-Bw0kmF01.js";
|
|
3
|
+
import { T as parseSourceFile } from "./codemod-auol-p4R.js";
|
|
4
|
+
import { t as exists } from "./fs-D6J9dOEx.js";
|
|
5
|
+
import { i as loadProject, n as frameworks } from "./project-DwRVTm4p.js";
|
|
6
|
+
import { n as isCancel$1, r as UIRegistries, t as FumadocsComponentInstaller } from "./installer-CzhVfs7i.js";
|
|
7
|
+
import { t as runFeature } from "./features-CXFCgOWD.js";
|
|
8
|
+
import { t as features } from "./all-BBf7I8hG.js";
|
|
4
9
|
import fs from "node:fs/promises";
|
|
5
10
|
import path from "node:path";
|
|
6
|
-
import {
|
|
11
|
+
import { cac } from "cac";
|
|
7
12
|
import picocolors from "picocolors";
|
|
8
13
|
import { x } from "tinyexec";
|
|
9
|
-
import { autocompleteMultiselect, cancel, group, intro,
|
|
14
|
+
import { autocompleteMultiselect, cancel, confirm, group, intro, log, note, outro, select, spinner } from "@clack/prompts";
|
|
10
15
|
import { exec } from "node:child_process";
|
|
11
16
|
import { promisify } from "node:util";
|
|
12
17
|
import { HttpRegistryConnector, LocalRegistryConnector } from "fuma-cli/registry/connector";
|
|
@@ -61,13 +66,7 @@ async function runTree(args) {
|
|
|
61
66
|
}
|
|
62
67
|
//#endregion
|
|
63
68
|
//#region package.json
|
|
64
|
-
var version = "1.
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/commands/shared.ts
|
|
67
|
-
const UIRegistries = {
|
|
68
|
-
"base-ui": "fumadocs/base-ui",
|
|
69
|
-
"radix-ui": "fumadocs/radix-ui"
|
|
70
|
-
};
|
|
69
|
+
var version = "1.6.1";
|
|
71
70
|
//#endregion
|
|
72
71
|
//#region src/commands/customise.ts
|
|
73
72
|
async function customise(config, connector) {
|
|
@@ -87,9 +86,7 @@ async function customise(config, connector) {
|
|
|
87
86
|
subRegistry,
|
|
88
87
|
name: "layouts/docs"
|
|
89
88
|
}],
|
|
90
|
-
print()
|
|
91
|
-
printLayout(["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"]);
|
|
92
|
-
}
|
|
89
|
+
print: () => printLayout(config, ["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"])
|
|
93
90
|
},
|
|
94
91
|
hint: "the default docs layout"
|
|
95
92
|
},
|
|
@@ -101,9 +98,7 @@ async function customise(config, connector) {
|
|
|
101
98
|
subRegistry,
|
|
102
99
|
name: "layouts/notebook"
|
|
103
100
|
}],
|
|
104
|
-
print()
|
|
105
|
-
printLayout(["fumadocs-ui/layouts/notebook", "@/layouts/notebook"], ["fumadocs-ui/layouts/notebook/page", "@/layouts/notebook/page"]);
|
|
106
|
-
}
|
|
101
|
+
print: () => printLayout(config, ["fumadocs-ui/layouts/notebook", "@/layouts/notebook"], ["fumadocs-ui/layouts/notebook/page", "@/layouts/notebook/page"])
|
|
107
102
|
},
|
|
108
103
|
hint: "a more compact version of docs layout"
|
|
109
104
|
},
|
|
@@ -115,9 +110,7 @@ async function customise(config, connector) {
|
|
|
115
110
|
subRegistry,
|
|
116
111
|
name: "layouts/flux"
|
|
117
112
|
}],
|
|
118
|
-
print()
|
|
119
|
-
printLayout(["fumadocs-ui/layouts/flux", "@/layouts/flux"], ["fumadocs-ui/layouts/flux/page", "@/layouts/flux/page"]);
|
|
120
|
-
}
|
|
113
|
+
print: () => printLayout(config, ["fumadocs-ui/layouts/flux", "@/layouts/flux"], ["fumadocs-ui/layouts/flux/page", "@/layouts/flux/page"])
|
|
121
114
|
},
|
|
122
115
|
hint: "the experimental variant of docs layout"
|
|
123
116
|
},
|
|
@@ -129,9 +122,7 @@ async function customise(config, connector) {
|
|
|
129
122
|
subRegistry,
|
|
130
123
|
name: "layouts/glass"
|
|
131
124
|
}],
|
|
132
|
-
print()
|
|
133
|
-
printLayout(["fumadocs-ui/layouts/glass", "@/layouts/glass"], ["fumadocs-ui/layouts/glass/page", "@/layouts/glass/page"]);
|
|
134
|
-
}
|
|
125
|
+
print: () => printLayout(config, ["fumadocs-ui/layouts/glass", "@/layouts/glass"], ["fumadocs-ui/layouts/glass/page", "@/layouts/glass/page"])
|
|
135
126
|
},
|
|
136
127
|
hint: "a docs layout with floating, translucent panels"
|
|
137
128
|
},
|
|
@@ -143,9 +134,7 @@ async function customise(config, connector) {
|
|
|
143
134
|
subRegistry,
|
|
144
135
|
name: "layouts/home"
|
|
145
136
|
}],
|
|
146
|
-
print()
|
|
147
|
-
printLayout(["fumadocs-ui/layouts/home", `@/layouts/home`]);
|
|
148
|
-
}
|
|
137
|
+
print: () => printLayout(config, ["fumadocs-ui/layouts/home", "@/layouts/home"])
|
|
149
138
|
},
|
|
150
139
|
hint: "the layout for other non-docs pages"
|
|
151
140
|
}
|
|
@@ -168,9 +157,7 @@ async function customise(config, connector) {
|
|
|
168
157
|
value: {
|
|
169
158
|
id: "docs-min",
|
|
170
159
|
targets: [{ name: "layouts/docs-min" }],
|
|
171
|
-
print()
|
|
172
|
-
printLayout(["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"]);
|
|
173
|
-
}
|
|
160
|
+
print: () => printLayout(config, ["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"])
|
|
174
161
|
}
|
|
175
162
|
},
|
|
176
163
|
...info.unlistedIndexes.flatMap((index) => {
|
|
@@ -229,15 +216,39 @@ async function customise(config, connector) {
|
|
|
229
216
|
process.exit(0);
|
|
230
217
|
} })).target;
|
|
231
218
|
for (const target of targetInfo.targets) await installer.installInteractive(target.name, target.subRegistry);
|
|
232
|
-
targetInfo.print?.();
|
|
219
|
+
await targetInfo.print?.();
|
|
233
220
|
outro(picocolors.bold("Have fun!"));
|
|
234
221
|
}
|
|
235
|
-
|
|
222
|
+
/** point the imports of route files at the installed layouts */
|
|
223
|
+
async function rewriteLayoutImports(config, map) {
|
|
224
|
+
const dir = path.join(config.baseDir, frameworks[config.framework].routesDir);
|
|
225
|
+
const entries = await fs.readdir(dir, {
|
|
226
|
+
recursive: true,
|
|
227
|
+
withFileTypes: true
|
|
228
|
+
}).catch(() => []);
|
|
229
|
+
const updated = [];
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
if (!entry.isFile() || !entry.name.endsWith(".tsx")) continue;
|
|
232
|
+
const file = path.join(entry.parentPath, entry.name);
|
|
233
|
+
const source = parseSourceFile(file, await fs.readFile(file, "utf-8"));
|
|
234
|
+
for (const node of source.program.body) {
|
|
235
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
236
|
+
const to = map.get(node.source.value);
|
|
237
|
+
if (to) source.s.overwrite(node.source.start + 1, node.source.end - 1, to);
|
|
238
|
+
}
|
|
239
|
+
if (!source.s.hasChanged()) continue;
|
|
240
|
+
await source.save();
|
|
241
|
+
updated.push(file);
|
|
242
|
+
}
|
|
243
|
+
return updated;
|
|
244
|
+
}
|
|
245
|
+
async function printLayout(config, ...maps) {
|
|
236
246
|
intro(picocolors.bold("What is Next?"));
|
|
247
|
+
const updated = await rewriteLayoutImports(config, new Map(maps));
|
|
237
248
|
log.info([
|
|
238
249
|
"You can check the installed layouts in `layouts` folder.",
|
|
239
250
|
picocolors.dim("---"),
|
|
240
|
-
"Open your `layout.tsx` files, replace the imports of components:",
|
|
251
|
+
updated.length > 0 ? `Updated the imports of ${updated.join(", ")}.` : "Open your `layout.tsx` files, replace the imports of components:",
|
|
241
252
|
...maps.map(([from, to]) => picocolors.greenBright(`"${from}" -> "${to}"`))
|
|
242
253
|
].join("\n"));
|
|
243
254
|
}
|
|
@@ -474,7 +485,7 @@ async function add(input, connector, config) {
|
|
|
474
485
|
message: "Select components to install",
|
|
475
486
|
options: groups.flat().sort((a, b) => a.label.localeCompare(b.label))
|
|
476
487
|
});
|
|
477
|
-
if (isCancel(value)) {
|
|
488
|
+
if (isCancel$1(value)) {
|
|
478
489
|
outro("Ended");
|
|
479
490
|
return;
|
|
480
491
|
}
|
|
@@ -487,16 +498,6 @@ async function add(input, connector, config) {
|
|
|
487
498
|
outro(picocolors.bold(picocolors.greenBright("Successful")));
|
|
488
499
|
}
|
|
489
500
|
//#endregion
|
|
490
|
-
//#region src/utils/fs.ts
|
|
491
|
-
async function exists(pathLike) {
|
|
492
|
-
try {
|
|
493
|
-
await fs.access(pathLike);
|
|
494
|
-
return true;
|
|
495
|
-
} catch {
|
|
496
|
-
return false;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
//#endregion
|
|
500
501
|
//#region src/commands/export-epub.ts
|
|
501
502
|
const execAsync = promisify(exec);
|
|
502
503
|
async function readPackageJson(cwd) {
|
|
@@ -517,60 +518,6 @@ const EPUB_BUILD_PATHS = {
|
|
|
517
518
|
"react-router-spa": "build/client/export/epub",
|
|
518
519
|
waku: "dist/public/export/epub"
|
|
519
520
|
};
|
|
520
|
-
const API_ROUTE_TEMPLATE = `import { source } from '@/lib/source';
|
|
521
|
-
import { exportEpub } from 'fumadocs-epub';
|
|
522
|
-
|
|
523
|
-
export const revalidate = false;
|
|
524
|
-
|
|
525
|
-
export async function GET(request: Request): Promise<Response> {
|
|
526
|
-
// Require EXPORT_SECRET to prevent unauthenticated abuse. Pass via Authorization: Bearer <secret>
|
|
527
|
-
const secret = process.env.EXPORT_SECRET;
|
|
528
|
-
if (!secret) {
|
|
529
|
-
return new Response('EXPORT_SECRET is not configured. Set it in your environment to protect this endpoint.', { status: 503 });
|
|
530
|
-
}
|
|
531
|
-
const authHeader = request.headers.get('authorization');
|
|
532
|
-
const token = authHeader?.replace(/^Bearer\\s+/i, '') ?? '';
|
|
533
|
-
if (token !== secret) {
|
|
534
|
-
return new Response('Unauthorized', { status: authHeader ? 403 : 401 });
|
|
535
|
-
}
|
|
536
|
-
const buffer = await exportEpub({
|
|
537
|
-
source,
|
|
538
|
-
title: 'Documentation',
|
|
539
|
-
author: 'Your Team',
|
|
540
|
-
description: 'Exported documentation',
|
|
541
|
-
cover: '/cover.png',
|
|
542
|
-
});
|
|
543
|
-
return new Response(new Uint8Array(buffer), {
|
|
544
|
-
headers: {
|
|
545
|
-
'Content-Type': 'application/epub+zip',
|
|
546
|
-
'Content-Disposition': 'attachment; filename="docs.epub"',
|
|
547
|
-
},
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
`;
|
|
551
|
-
async function findAppDir(cwd) {
|
|
552
|
-
for (const appPath of ["app", "src/app"]) {
|
|
553
|
-
const fullPath = path.join(cwd, appPath);
|
|
554
|
-
if (await exists(fullPath)) return fullPath;
|
|
555
|
-
}
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
async function scaffoldEpubRoute(cwd) {
|
|
559
|
-
const appDir = await findAppDir(cwd);
|
|
560
|
-
if (!appDir) {
|
|
561
|
-
console.error(picocolors.red("Could not find app directory (app/ or src/app/)"));
|
|
562
|
-
return false;
|
|
563
|
-
}
|
|
564
|
-
const routePath = path.join(appDir, "export", "epub", "route.ts");
|
|
565
|
-
if (await exists(routePath)) {
|
|
566
|
-
console.log(picocolors.yellow("EPUB route already exists at"), routePath);
|
|
567
|
-
return true;
|
|
568
|
-
}
|
|
569
|
-
await fs.mkdir(path.dirname(routePath), { recursive: true });
|
|
570
|
-
await fs.writeFile(routePath, API_ROUTE_TEMPLATE);
|
|
571
|
-
console.log(picocolors.green("Created EPUB route at"), routePath);
|
|
572
|
-
return true;
|
|
573
|
-
}
|
|
574
521
|
async function exportEpub(options) {
|
|
575
522
|
const cwd = process.cwd();
|
|
576
523
|
const outputPath = path.resolve(cwd, options.output ?? "docs.epub");
|
|
@@ -583,39 +530,6 @@ async function exportEpub(options) {
|
|
|
583
530
|
process.exit(1);
|
|
584
531
|
}
|
|
585
532
|
const pkg = await readPackageJson(cwd);
|
|
586
|
-
const hasNextConfig = await exists(path.join(cwd, "next.config.js")) || await exists(path.join(cwd, "next.config.ts")) || await exists(path.join(cwd, "next.config.mjs"));
|
|
587
|
-
const hasNextInPkg = !!(pkg ? {
|
|
588
|
-
...pkg.dependencies,
|
|
589
|
-
...pkg.devDependencies,
|
|
590
|
-
...pkg.peerDependencies
|
|
591
|
-
} : {}).next;
|
|
592
|
-
const hasAppOrPages = await exists(path.join(cwd, "app")) || await exists(path.join(cwd, "pages")) || await exists(path.join(cwd, "src", "app")) || await exists(path.join(cwd, "src", "pages"));
|
|
593
|
-
if (!(hasNextConfig || hasNextInPkg && hasAppOrPages) && framework === "next") {
|
|
594
|
-
console.error(picocolors.red("Next.js project not found. Run this command from a Fumadocs Next.js project root."));
|
|
595
|
-
process.exit(1);
|
|
596
|
-
}
|
|
597
|
-
if (framework === "next") {
|
|
598
|
-
spin.start("Scaffolding EPUB route");
|
|
599
|
-
const scaffolded = await scaffoldEpubRoute(cwd);
|
|
600
|
-
spin.stop(scaffolded ? "EPUB route ready" : "Scaffolding failed");
|
|
601
|
-
if (!scaffolded) process.exit(1);
|
|
602
|
-
}
|
|
603
|
-
if (options.scaffoldOnly) {
|
|
604
|
-
console.log(picocolors.cyan("\nTo export:"));
|
|
605
|
-
console.log(" 1. Add fumadocs-epub to your dependencies: pnpm add fumadocs-epub");
|
|
606
|
-
console.log(" 2. Ensure includeProcessedMarkdown: true in your docs collection config");
|
|
607
|
-
if (framework === "next") {
|
|
608
|
-
console.log(" 3. Set EXPORT_SECRET in your environment to protect the /export/epub endpoint");
|
|
609
|
-
console.log(" 4. Run production build: pnpm build");
|
|
610
|
-
console.log(" 5. Start the server (e.g. pnpm start) and keep it running");
|
|
611
|
-
console.log(" 6. Run: fumadocs export epub --framework next");
|
|
612
|
-
} else {
|
|
613
|
-
console.log(` 3. Add a prerender route that outputs EPUB to ${buildPath}`);
|
|
614
|
-
console.log(" 4. Run production build: pnpm build");
|
|
615
|
-
console.log(` 5. Run: fumadocs export epub --framework ${framework}`);
|
|
616
|
-
}
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
533
|
if (!pkg) {
|
|
620
534
|
console.error(picocolors.red("Cannot read or parse package.json. Ensure it exists and is valid JSON."));
|
|
621
535
|
process.exit(1);
|
|
@@ -653,6 +567,7 @@ async function exportEpub(options) {
|
|
|
653
567
|
});
|
|
654
568
|
if (!res.ok) {
|
|
655
569
|
if (res.status === 401 || res.status === 403) console.error(picocolors.red("Auth failed. Check that EXPORT_SECRET matches the value in your app."));
|
|
570
|
+
else if (res.status === 404) console.error(picocolors.red("Export route not found, run `fumadocs feature epub` to create it."));
|
|
656
571
|
else console.error(picocolors.red(`Server returned ${res.status}. Ensure the app is running (e.g. pnpm start) on port ${port}.`));
|
|
657
572
|
process.exit(1);
|
|
658
573
|
}
|
|
@@ -684,31 +599,124 @@ async function exportEpub(options) {
|
|
|
684
599
|
spin.stop(picocolors.green(`EPUB saved to ${outputPath}`));
|
|
685
600
|
}
|
|
686
601
|
//#endregion
|
|
602
|
+
//#region src/commands/feature.ts
|
|
603
|
+
function registerFeatureCommands(cli, createConnector) {
|
|
604
|
+
const ids = features.map((feature) => feature.id).join(", ");
|
|
605
|
+
register(cli.command("feature <id>", `configure a feature on your project (${ids})`), features).action(async (id, options) => {
|
|
606
|
+
const feature = features.find((item) => item.id === id);
|
|
607
|
+
if (!feature) throw new Error(`unknown feature: ${id}, available: ${ids}`);
|
|
608
|
+
await run(feature, options, createConnector(options.dir));
|
|
609
|
+
});
|
|
610
|
+
register(cli.command("init", "set up Fumadocs on an existing app, same as `feature docs`"), [features[0]]).action(async (options) => {
|
|
611
|
+
await run(features[0], options, createConnector(options.dir));
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
function register(command, list) {
|
|
615
|
+
command.option("--dir <string>", "the root url or directory to resolve registry").option("-y, --yes", "skip prompts, overwrite existing files").option("--no-install", "write dependencies to package.json without installing");
|
|
616
|
+
for (const feature of list) for (const [key, option] of Object.entries(feature.options ?? {})) if ("choices" in option) command.option(`--${key} <value>`, `${option.message} (${option.choices.map((choice) => choice.value).join(", ")})`);
|
|
617
|
+
else command.option(`--${key}`, option.message);
|
|
618
|
+
return command;
|
|
619
|
+
}
|
|
620
|
+
async function run(feature, options, connector) {
|
|
621
|
+
intro(picocolors.bgBlack(picocolors.whiteBright(feature.title)));
|
|
622
|
+
const config = await createOrLoadConfig(options.config);
|
|
623
|
+
const project = await loadProject(config);
|
|
624
|
+
log.info(`${project.framework}${project.static ? " (static)" : ""}, ${project.packageManager}`);
|
|
625
|
+
const values = {};
|
|
626
|
+
for (const [key, option] of Object.entries(feature.options ?? {})) {
|
|
627
|
+
const given = options[key];
|
|
628
|
+
if ("choices" in option) {
|
|
629
|
+
if (given !== void 0) {
|
|
630
|
+
if (!option.choices.some((choice) => choice.value === given)) throw new Error(`invalid value for --${key}: ${given}`);
|
|
631
|
+
values[key] = given;
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const value = await select({
|
|
635
|
+
message: option.message,
|
|
636
|
+
options: option.choices
|
|
637
|
+
});
|
|
638
|
+
if (isCancel$1(value)) {
|
|
639
|
+
cancel("Stopped.");
|
|
640
|
+
process.exit(0);
|
|
641
|
+
}
|
|
642
|
+
values[key] = value;
|
|
643
|
+
} else if (given !== void 0) values[key] = given === true;
|
|
644
|
+
else if (options.yes) values[key] = option.initialValue ?? false;
|
|
645
|
+
else {
|
|
646
|
+
const value = await confirm({
|
|
647
|
+
message: option.message,
|
|
648
|
+
initialValue: option.initialValue
|
|
649
|
+
});
|
|
650
|
+
if (isCancel$1(value)) {
|
|
651
|
+
cancel("Stopped.");
|
|
652
|
+
process.exit(0);
|
|
653
|
+
}
|
|
654
|
+
values[key] = value;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
const spin = spinner();
|
|
658
|
+
const io = {
|
|
659
|
+
installDependencies: options.install !== false,
|
|
660
|
+
log: (message) => spin.message(message),
|
|
661
|
+
async confirmOverwrite(file) {
|
|
662
|
+
if (options.yes) return true;
|
|
663
|
+
spin.clear();
|
|
664
|
+
const value = await confirm({
|
|
665
|
+
message: `Overwrite ${file}?`,
|
|
666
|
+
initialValue: false
|
|
667
|
+
});
|
|
668
|
+
if (isCancel$1(value)) {
|
|
669
|
+
cancel("Stopped.");
|
|
670
|
+
process.exit(0);
|
|
671
|
+
}
|
|
672
|
+
spin.start();
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
spin.start(`Applying ${feature.title}`);
|
|
677
|
+
try {
|
|
678
|
+
const { notes } = await runFeature(feature, values, {
|
|
679
|
+
project,
|
|
680
|
+
connector,
|
|
681
|
+
io
|
|
682
|
+
});
|
|
683
|
+
spin.stop(`${feature.title} applied`);
|
|
684
|
+
if (notes.length > 0) note(notes.join("\n\n"), "What is Next?");
|
|
685
|
+
} catch (e) {
|
|
686
|
+
spin.error(e instanceof Error ? e.message : String(e));
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
outro(picocolors.bold(picocolors.greenBright("Done")));
|
|
690
|
+
}
|
|
691
|
+
//#endregion
|
|
687
692
|
//#region src/index.ts
|
|
688
|
-
const
|
|
689
|
-
|
|
693
|
+
const cli = cac("fumadocs");
|
|
694
|
+
cli.option("--config <string>", "path to the config file");
|
|
695
|
+
cli.command("", "init a `cli.json` config file").action(async () => {
|
|
690
696
|
if (await initConfig()) console.log(picocolors.green("Initialized a `./cli.json` config file."));
|
|
691
697
|
else console.log(picocolors.redBright("A config file already exists."));
|
|
692
698
|
});
|
|
693
|
-
|
|
699
|
+
cli.command("customise", "simple way to customize layouts with Fumadocs UI").alias("customize").option("--dir <string>", "the root url or directory to resolve registry").action(async (options) => {
|
|
694
700
|
await customise(await createOrLoadConfig(options.config), createClientFromDir(options.dir));
|
|
695
701
|
});
|
|
696
702
|
const dirShortcuts = {
|
|
697
703
|
":preview": "https://preview.fumadocs.dev/registry",
|
|
698
704
|
":dev": "http://localhost:3000/registry"
|
|
699
705
|
};
|
|
700
|
-
|
|
706
|
+
cli.command("add [...components]", "add a new component to your docs").option("--dir <string>", "the root url or directory to resolve registry").action(async (input, options) => {
|
|
701
707
|
const config = await createOrLoadConfig(options.config);
|
|
702
708
|
await add(input, createClientFromDir(options.dir), config);
|
|
703
709
|
});
|
|
704
|
-
|
|
710
|
+
registerFeatureCommands(cli, createClientFromDir);
|
|
711
|
+
cli.command("export <format>", "export documentation to various formats (run after production build)").option("--framework <name>", "framework: next, astro, tanstack-start, react-router, waku").option("--output <path>", "output file path", { default: "docs.epub" }).action(async (format, options) => {
|
|
712
|
+
if (format !== "epub") throw new Error(`unsupported format: ${format}, only \`epub\` is supported.`);
|
|
713
|
+
if (!options.framework) throw new Error("option `--framework <name>` is required.");
|
|
705
714
|
await exportEpub({
|
|
706
|
-
output: options.output,
|
|
707
715
|
framework: options.framework,
|
|
708
|
-
|
|
716
|
+
output: options.output
|
|
709
717
|
});
|
|
710
718
|
});
|
|
711
|
-
|
|
719
|
+
cli.command("tree [json_or_args] [output]", "generate a file tree for the Files component, from a directory or JSON output of `tree`").option("--js", "output as JavaScript file").option("--no-root", "remove the root node").option("--import-name <name>", "where to import components (JS only)").action(async (str, output, { js, root, importName }) => {
|
|
712
720
|
const jsExtensions = [
|
|
713
721
|
".js",
|
|
714
722
|
".tsx",
|
|
@@ -731,7 +739,15 @@ function createClientFromDir(dir = "https://fumadocs.dev/registry") {
|
|
|
731
739
|
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
732
740
|
return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryConnector(dir) : new LocalRegistryConnector(dir);
|
|
733
741
|
}
|
|
734
|
-
|
|
742
|
+
cli.help();
|
|
743
|
+
cli.version(version);
|
|
744
|
+
try {
|
|
745
|
+
cli.parse(process.argv, { run: false });
|
|
746
|
+
await cli.runMatchedCommand();
|
|
747
|
+
} catch (e) {
|
|
748
|
+
console.error(picocolors.redBright(e instanceof Error ? e.message : String(e)));
|
|
749
|
+
process.exit(1);
|
|
750
|
+
}
|
|
735
751
|
//#endregion
|
|
736
752
|
export {};
|
|
737
753
|
|