@fumadocs/cli 1.4.1 → 1.6.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/dist/ai-BVuNjYXU.js +100 -0
- package/dist/ai-BVuNjYXU.js.map +1 -0
- package/dist/all-aUHxtoXL.js +27 -0
- package/dist/all-aUHxtoXL.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-CT2jj39D.d.ts +52 -0
- package/dist/config-CT2jj39D.d.ts.map +1 -0
- package/dist/config-D6uv73fs.js +94 -0
- package/dist/config-D6uv73fs.js.map +1 -0
- package/dist/config.d.ts +2 -52
- package/dist/config.js +1 -72
- package/dist/docs-DeIEl_Kt.js +738 -0
- package/dist/docs-DeIEl_Kt.js.map +1 -0
- package/dist/epub-CnQFJh09.js +52 -0
- package/dist/epub-CnQFJh09.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-C1kf2s8j.js +132 -0
- package/dist/features-C1kf2s8j.js.map +1 -0
- package/dist/feedback-COmWtc5C.js +37 -0
- package/dist/feedback-COmWtc5C.js.map +1 -0
- package/dist/fs-D6J9dOEx.js +14 -0
- package/dist/fs-D6J9dOEx.js.map +1 -0
- package/dist/index-BCVGQxn9.d.ts +64 -0
- package/dist/index-BCVGQxn9.d.ts.map +1 -0
- package/dist/index-CR9T40ox.d.ts +76 -0
- package/dist/index-CR9T40ox.d.ts.map +1 -0
- package/dist/index-Ck0EPdgu.d.ts +126 -0
- package/dist/index-Ck0EPdgu.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +198 -124
- package/dist/index.js.map +1 -1
- package/dist/{installer-Chjj_R2z.js → installer-D0Obb2Md.js} +21 -8
- package/dist/installer-D0Obb2Md.js.map +1 -0
- package/dist/lint-zkXUULgO.js +137 -0
- package/dist/lint-zkXUULgO.js.map +1 -0
- package/dist/llms-CONd8Vnn.js +228 -0
- package/dist/llms-CONd8Vnn.js.map +1 -0
- package/dist/mcp-BT8BLZZ-.js +67 -0
- package/dist/mcp-BT8BLZZ-.js.map +1 -0
- package/dist/og-DRDOyQ_t.js +142 -0
- package/dist/og-DRDOyQ_t.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-iphnSVVY.js +381 -0
- package/dist/search-iphnSVVY.js.map +1 -0
- package/dist/utils-B1Xy4l_a.d.ts +43 -0
- package/dist/utils-B1Xy4l_a.d.ts.map +1 -0
- package/dist/utils-DbMe7h6c.js +98 -0
- package/dist/utils-DbMe7h6c.js.map +1 -0
- package/dist/webmcp-C2PM_9md.js +135 -0
- package/dist/webmcp-C2PM_9md.js.map +1 -0
- package/package.json +27 -7
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/installer-Chjj_R2z.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { t as FumadocsComponentInstaller } from "./installer-
|
|
2
|
+
import { i as initConfig, n as createOrLoadConfig } from "./config-D6uv73fs.js";
|
|
3
|
+
import { n as isCancel$1, t as FumadocsComponentInstaller } from "./installer-D0Obb2Md.js";
|
|
4
|
+
import { t as exists } from "./fs-D6J9dOEx.js";
|
|
5
|
+
import { i as loadProject } from "./project-DwRVTm4p.js";
|
|
6
|
+
import { t as runFeature } from "./features-C1kf2s8j.js";
|
|
7
|
+
import { t as features } from "./all-aUHxtoXL.js";
|
|
4
8
|
import fs from "node:fs/promises";
|
|
5
9
|
import path from "node:path";
|
|
6
|
-
import {
|
|
10
|
+
import { cac } from "cac";
|
|
7
11
|
import picocolors from "picocolors";
|
|
8
12
|
import { x } from "tinyexec";
|
|
9
|
-
import { autocompleteMultiselect, cancel, group, intro,
|
|
13
|
+
import { autocompleteMultiselect, cancel, confirm, group, intro, log, note, outro, select, spinner } from "@clack/prompts";
|
|
10
14
|
import { exec } from "node:child_process";
|
|
11
15
|
import { promisify } from "node:util";
|
|
12
16
|
import { HttpRegistryConnector, LocalRegistryConnector } from "fuma-cli/registry/connector";
|
|
@@ -61,7 +65,7 @@ async function runTree(args) {
|
|
|
61
65
|
}
|
|
62
66
|
//#endregion
|
|
63
67
|
//#region package.json
|
|
64
|
-
var version = "1.
|
|
68
|
+
var version = "1.6.0";
|
|
65
69
|
//#endregion
|
|
66
70
|
//#region src/commands/shared.ts
|
|
67
71
|
const UIRegistries = {
|
|
@@ -121,6 +125,20 @@ async function customise(config, connector) {
|
|
|
121
125
|
},
|
|
122
126
|
hint: "the experimental variant of docs layout"
|
|
123
127
|
},
|
|
128
|
+
{
|
|
129
|
+
label: "Glass Layout",
|
|
130
|
+
value: {
|
|
131
|
+
id: "glass",
|
|
132
|
+
targets: [{
|
|
133
|
+
subRegistry,
|
|
134
|
+
name: "layouts/glass"
|
|
135
|
+
}],
|
|
136
|
+
print() {
|
|
137
|
+
printLayout(["fumadocs-ui/layouts/glass", "@/layouts/glass"], ["fumadocs-ui/layouts/glass/page", "@/layouts/glass/page"]);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
hint: "a docs layout with floating, translucent panels"
|
|
141
|
+
},
|
|
124
142
|
{
|
|
125
143
|
label: "Home Layout",
|
|
126
144
|
value: {
|
|
@@ -179,7 +197,8 @@ async function customise(config, connector) {
|
|
|
179
197
|
at: `@/layouts/${selected.id}/page/slots/${name}`,
|
|
180
198
|
layoutId: selected.id,
|
|
181
199
|
name,
|
|
182
|
-
isPage: true
|
|
200
|
+
isPage: true,
|
|
201
|
+
uiLibrary: config.uiLibrary
|
|
183
202
|
});
|
|
184
203
|
}
|
|
185
204
|
}
|
|
@@ -199,7 +218,8 @@ async function customise(config, connector) {
|
|
|
199
218
|
at: `@/layouts/${selected.id}/slots/${name}`,
|
|
200
219
|
layoutId: selected.id,
|
|
201
220
|
name,
|
|
202
|
-
isPage: false
|
|
221
|
+
isPage: false,
|
|
222
|
+
uiLibrary: config.uiLibrary
|
|
203
223
|
});
|
|
204
224
|
}
|
|
205
225
|
}
|
|
@@ -225,19 +245,65 @@ function printLayout(...maps) {
|
|
|
225
245
|
...maps.map(([from, to]) => picocolors.greenBright(`"${from}" -> "${to}"`))
|
|
226
246
|
].join("\n"));
|
|
227
247
|
}
|
|
228
|
-
function printSlot({ at, layoutId, name, isPage }) {
|
|
248
|
+
function printSlot({ at, layoutId, name, isPage, uiLibrary }) {
|
|
229
249
|
intro(picocolors.bold("What is Next?"));
|
|
230
250
|
log.info(`You can check the installed layout slot in "${at}".`);
|
|
231
251
|
const code = getSlotCode({
|
|
232
252
|
at,
|
|
233
253
|
layoutId,
|
|
234
254
|
name,
|
|
235
|
-
isPage
|
|
255
|
+
isPage,
|
|
256
|
+
uiLibrary
|
|
236
257
|
});
|
|
237
|
-
if (code)
|
|
238
|
-
|
|
258
|
+
if (code) {
|
|
259
|
+
const layoutComponent = layoutId === "glass" ? "<GlassLayout />" : "<DocsLayout />";
|
|
260
|
+
if (isPage) log.info(`${picocolors.bold("At your <DocsPage /> component, update your \"slots\" prop:")}\n\n${code}`);
|
|
261
|
+
else log.info(`${picocolors.bold(`At your ${layoutComponent} component, update your "slots" prop:`)}\n\n${code}`);
|
|
262
|
+
}
|
|
239
263
|
}
|
|
240
|
-
function getSlotCode({ at, layoutId, name, isPage }) {
|
|
264
|
+
function getSlotCode({ at, layoutId, name, isPage, uiLibrary }) {
|
|
265
|
+
if (layoutId === "glass") {
|
|
266
|
+
if (isPage) return;
|
|
267
|
+
switch (name) {
|
|
268
|
+
case "header": return `import { Header } from '${at}';
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<GlassLayout
|
|
272
|
+
slots={{
|
|
273
|
+
header: Header,
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
...
|
|
277
|
+
</GlassLayout>
|
|
278
|
+
);`;
|
|
279
|
+
case "sidebar": {
|
|
280
|
+
const imports = [
|
|
281
|
+
"Sidebar",
|
|
282
|
+
"SidebarDrawer",
|
|
283
|
+
"SidebarProvider",
|
|
284
|
+
"useSidebar"
|
|
285
|
+
];
|
|
286
|
+
if (uiLibrary === "base-ui") imports.push("drawerHandle");
|
|
287
|
+
return `import { ${imports.join(", ")} } from '${at}';
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<GlassLayout
|
|
291
|
+
slots={{
|
|
292
|
+
sidebar: {
|
|
293
|
+
main: Sidebar,
|
|
294
|
+
provider: SidebarProvider,
|
|
295
|
+
use: useSidebar,
|
|
296
|
+
drawer: SidebarDrawer,${uiLibrary === "base-ui" ? "\n drawerHandle," : ""}
|
|
297
|
+
},
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
...
|
|
301
|
+
</GlassLayout>
|
|
302
|
+
);`;
|
|
303
|
+
}
|
|
304
|
+
default: return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
241
307
|
if (isPage) switch (name) {
|
|
242
308
|
case "toc":
|
|
243
309
|
if (layoutId === "flux") return `import { TOCProvider, TOC } from '${at}';
|
|
@@ -400,16 +466,19 @@ async function add(input, connector, config) {
|
|
|
400
466
|
hint: item.description
|
|
401
467
|
}));
|
|
402
468
|
}
|
|
469
|
+
const groups = await Promise.all([
|
|
470
|
+
scan(void 0, "common"),
|
|
471
|
+
scan("fumadocs/sanity", "sanity"),
|
|
472
|
+
scan("fumadocs/openapi", "openapi"),
|
|
473
|
+
scan("fumadocs/api-docs", "api-docs"),
|
|
474
|
+
scan(subRegistry, "ui")
|
|
475
|
+
]);
|
|
403
476
|
spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
|
|
404
477
|
const value = await autocompleteMultiselect({
|
|
405
478
|
message: "Select components to install",
|
|
406
|
-
options:
|
|
407
|
-
...await scan(void 0, "common"),
|
|
408
|
-
...await scan("fumadocs/sanity", "sanity"),
|
|
409
|
-
...await scan(subRegistry, "ui")
|
|
410
|
-
].sort((a, b) => a.label.localeCompare(b.label))
|
|
479
|
+
options: groups.flat().sort((a, b) => a.label.localeCompare(b.label))
|
|
411
480
|
});
|
|
412
|
-
if (isCancel(value)) {
|
|
481
|
+
if (isCancel$1(value)) {
|
|
413
482
|
outro("Ended");
|
|
414
483
|
return;
|
|
415
484
|
}
|
|
@@ -422,16 +491,6 @@ async function add(input, connector, config) {
|
|
|
422
491
|
outro(picocolors.bold(picocolors.greenBright("Successful")));
|
|
423
492
|
}
|
|
424
493
|
//#endregion
|
|
425
|
-
//#region src/utils/fs.ts
|
|
426
|
-
async function exists(pathLike) {
|
|
427
|
-
try {
|
|
428
|
-
await fs.access(pathLike);
|
|
429
|
-
return true;
|
|
430
|
-
} catch {
|
|
431
|
-
return false;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
//#endregion
|
|
435
494
|
//#region src/commands/export-epub.ts
|
|
436
495
|
const execAsync = promisify(exec);
|
|
437
496
|
async function readPackageJson(cwd) {
|
|
@@ -452,60 +511,6 @@ const EPUB_BUILD_PATHS = {
|
|
|
452
511
|
"react-router-spa": "build/client/export/epub",
|
|
453
512
|
waku: "dist/public/export/epub"
|
|
454
513
|
};
|
|
455
|
-
const API_ROUTE_TEMPLATE = `import { source } from '@/lib/source';
|
|
456
|
-
import { exportEpub } from 'fumadocs-epub';
|
|
457
|
-
|
|
458
|
-
export const revalidate = false;
|
|
459
|
-
|
|
460
|
-
export async function GET(request: Request): Promise<Response> {
|
|
461
|
-
// Require EXPORT_SECRET to prevent unauthenticated abuse. Pass via Authorization: Bearer <secret>
|
|
462
|
-
const secret = process.env.EXPORT_SECRET;
|
|
463
|
-
if (!secret) {
|
|
464
|
-
return new Response('EXPORT_SECRET is not configured. Set it in your environment to protect this endpoint.', { status: 503 });
|
|
465
|
-
}
|
|
466
|
-
const authHeader = request.headers.get('authorization');
|
|
467
|
-
const token = authHeader?.replace(/^Bearer\\s+/i, '') ?? '';
|
|
468
|
-
if (token !== secret) {
|
|
469
|
-
return new Response('Unauthorized', { status: authHeader ? 403 : 401 });
|
|
470
|
-
}
|
|
471
|
-
const buffer = await exportEpub({
|
|
472
|
-
source,
|
|
473
|
-
title: 'Documentation',
|
|
474
|
-
author: 'Your Team',
|
|
475
|
-
description: 'Exported documentation',
|
|
476
|
-
cover: '/cover.png',
|
|
477
|
-
});
|
|
478
|
-
return new Response(new Uint8Array(buffer), {
|
|
479
|
-
headers: {
|
|
480
|
-
'Content-Type': 'application/epub+zip',
|
|
481
|
-
'Content-Disposition': 'attachment; filename="docs.epub"',
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
`;
|
|
486
|
-
async function findAppDir(cwd) {
|
|
487
|
-
for (const appPath of ["app", "src/app"]) {
|
|
488
|
-
const fullPath = path.join(cwd, appPath);
|
|
489
|
-
if (await exists(fullPath)) return fullPath;
|
|
490
|
-
}
|
|
491
|
-
return null;
|
|
492
|
-
}
|
|
493
|
-
async function scaffoldEpubRoute(cwd) {
|
|
494
|
-
const appDir = await findAppDir(cwd);
|
|
495
|
-
if (!appDir) {
|
|
496
|
-
console.error(picocolors.red("Could not find app directory (app/ or src/app/)"));
|
|
497
|
-
return false;
|
|
498
|
-
}
|
|
499
|
-
const routePath = path.join(appDir, "export", "epub", "route.ts");
|
|
500
|
-
if (await exists(routePath)) {
|
|
501
|
-
console.log(picocolors.yellow("EPUB route already exists at"), routePath);
|
|
502
|
-
return true;
|
|
503
|
-
}
|
|
504
|
-
await fs.mkdir(path.dirname(routePath), { recursive: true });
|
|
505
|
-
await fs.writeFile(routePath, API_ROUTE_TEMPLATE);
|
|
506
|
-
console.log(picocolors.green("Created EPUB route at"), routePath);
|
|
507
|
-
return true;
|
|
508
|
-
}
|
|
509
514
|
async function exportEpub(options) {
|
|
510
515
|
const cwd = process.cwd();
|
|
511
516
|
const outputPath = path.resolve(cwd, options.output ?? "docs.epub");
|
|
@@ -518,39 +523,6 @@ async function exportEpub(options) {
|
|
|
518
523
|
process.exit(1);
|
|
519
524
|
}
|
|
520
525
|
const pkg = await readPackageJson(cwd);
|
|
521
|
-
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"));
|
|
522
|
-
const hasNextInPkg = !!(pkg ? {
|
|
523
|
-
...pkg.dependencies,
|
|
524
|
-
...pkg.devDependencies,
|
|
525
|
-
...pkg.peerDependencies
|
|
526
|
-
} : {}).next;
|
|
527
|
-
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"));
|
|
528
|
-
if (!(hasNextConfig || hasNextInPkg && hasAppOrPages) && framework === "next") {
|
|
529
|
-
console.error(picocolors.red("Next.js project not found. Run this command from a Fumadocs Next.js project root."));
|
|
530
|
-
process.exit(1);
|
|
531
|
-
}
|
|
532
|
-
if (framework === "next") {
|
|
533
|
-
spin.start("Scaffolding EPUB route");
|
|
534
|
-
const scaffolded = await scaffoldEpubRoute(cwd);
|
|
535
|
-
spin.stop(scaffolded ? "EPUB route ready" : "Scaffolding failed");
|
|
536
|
-
if (!scaffolded) process.exit(1);
|
|
537
|
-
}
|
|
538
|
-
if (options.scaffoldOnly) {
|
|
539
|
-
console.log(picocolors.cyan("\nTo export:"));
|
|
540
|
-
console.log(" 1. Add fumadocs-epub to your dependencies: pnpm add fumadocs-epub");
|
|
541
|
-
console.log(" 2. Ensure includeProcessedMarkdown: true in your docs collection config");
|
|
542
|
-
if (framework === "next") {
|
|
543
|
-
console.log(" 3. Set EXPORT_SECRET in your environment to protect the /export/epub endpoint");
|
|
544
|
-
console.log(" 4. Run production build: pnpm build");
|
|
545
|
-
console.log(" 5. Start the server (e.g. pnpm start) and keep it running");
|
|
546
|
-
console.log(" 6. Run: fumadocs export epub --framework next");
|
|
547
|
-
} else {
|
|
548
|
-
console.log(` 3. Add a prerender route that outputs EPUB to ${buildPath}`);
|
|
549
|
-
console.log(" 4. Run production build: pnpm build");
|
|
550
|
-
console.log(` 5. Run: fumadocs export epub --framework ${framework}`);
|
|
551
|
-
}
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
526
|
if (!pkg) {
|
|
555
527
|
console.error(picocolors.red("Cannot read or parse package.json. Ensure it exists and is valid JSON."));
|
|
556
528
|
process.exit(1);
|
|
@@ -588,6 +560,7 @@ async function exportEpub(options) {
|
|
|
588
560
|
});
|
|
589
561
|
if (!res.ok) {
|
|
590
562
|
if (res.status === 401 || res.status === 403) console.error(picocolors.red("Auth failed. Check that EXPORT_SECRET matches the value in your app."));
|
|
563
|
+
else if (res.status === 404) console.error(picocolors.red("Export route not found, run `fumadocs feature epub` to create it."));
|
|
591
564
|
else console.error(picocolors.red(`Server returned ${res.status}. Ensure the app is running (e.g. pnpm start) on port ${port}.`));
|
|
592
565
|
process.exit(1);
|
|
593
566
|
}
|
|
@@ -619,31 +592,124 @@ async function exportEpub(options) {
|
|
|
619
592
|
spin.stop(picocolors.green(`EPUB saved to ${outputPath}`));
|
|
620
593
|
}
|
|
621
594
|
//#endregion
|
|
595
|
+
//#region src/commands/feature.ts
|
|
596
|
+
function registerFeatureCommands(cli, createConnector) {
|
|
597
|
+
const ids = features.map((feature) => feature.id).join(", ");
|
|
598
|
+
register(cli.command("feature <id>", `configure a feature on your project (${ids})`), features).action(async (id, options) => {
|
|
599
|
+
const feature = features.find((item) => item.id === id);
|
|
600
|
+
if (!feature) throw new Error(`unknown feature: ${id}, available: ${ids}`);
|
|
601
|
+
await run(feature, options, createConnector(options.dir));
|
|
602
|
+
});
|
|
603
|
+
register(cli.command("init", "set up Fumadocs on an existing app, same as `feature docs`"), [features[0]]).action(async (options) => {
|
|
604
|
+
await run(features[0], options, createConnector(options.dir));
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
function register(command, list) {
|
|
608
|
+
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");
|
|
609
|
+
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(", ")})`);
|
|
610
|
+
else command.option(`--${key}`, option.message);
|
|
611
|
+
return command;
|
|
612
|
+
}
|
|
613
|
+
async function run(feature, options, connector) {
|
|
614
|
+
intro(picocolors.bgBlack(picocolors.whiteBright(feature.title)));
|
|
615
|
+
const config = await createOrLoadConfig(options.config);
|
|
616
|
+
const project = await loadProject(config);
|
|
617
|
+
log.info(`${project.framework}${project.static ? " (static)" : ""}, ${project.packageManager}`);
|
|
618
|
+
const values = {};
|
|
619
|
+
for (const [key, option] of Object.entries(feature.options ?? {})) {
|
|
620
|
+
const given = options[key];
|
|
621
|
+
if ("choices" in option) {
|
|
622
|
+
if (given !== void 0) {
|
|
623
|
+
if (!option.choices.some((choice) => choice.value === given)) throw new Error(`invalid value for --${key}: ${given}`);
|
|
624
|
+
values[key] = given;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
const value = await select({
|
|
628
|
+
message: option.message,
|
|
629
|
+
options: option.choices
|
|
630
|
+
});
|
|
631
|
+
if (isCancel$1(value)) {
|
|
632
|
+
cancel("Stopped.");
|
|
633
|
+
process.exit(0);
|
|
634
|
+
}
|
|
635
|
+
values[key] = value;
|
|
636
|
+
} else if (given !== void 0) values[key] = given === true;
|
|
637
|
+
else if (options.yes) values[key] = option.initialValue ?? false;
|
|
638
|
+
else {
|
|
639
|
+
const value = await confirm({
|
|
640
|
+
message: option.message,
|
|
641
|
+
initialValue: option.initialValue
|
|
642
|
+
});
|
|
643
|
+
if (isCancel$1(value)) {
|
|
644
|
+
cancel("Stopped.");
|
|
645
|
+
process.exit(0);
|
|
646
|
+
}
|
|
647
|
+
values[key] = value;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const spin = spinner();
|
|
651
|
+
const io = {
|
|
652
|
+
installDependencies: options.install !== false,
|
|
653
|
+
log: (message) => spin.message(message),
|
|
654
|
+
async confirmOverwrite(file) {
|
|
655
|
+
if (options.yes) return true;
|
|
656
|
+
spin.clear();
|
|
657
|
+
const value = await confirm({
|
|
658
|
+
message: `Overwrite ${file}?`,
|
|
659
|
+
initialValue: false
|
|
660
|
+
});
|
|
661
|
+
if (isCancel$1(value)) {
|
|
662
|
+
cancel("Stopped.");
|
|
663
|
+
process.exit(0);
|
|
664
|
+
}
|
|
665
|
+
spin.start();
|
|
666
|
+
return value;
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
spin.start(`Applying ${feature.title}`);
|
|
670
|
+
try {
|
|
671
|
+
const { notes } = await runFeature(feature, values, {
|
|
672
|
+
project,
|
|
673
|
+
connector,
|
|
674
|
+
io
|
|
675
|
+
});
|
|
676
|
+
spin.stop(`${feature.title} applied`);
|
|
677
|
+
if (notes.length > 0) note(notes.join("\n\n"), "What is Next?");
|
|
678
|
+
} catch (e) {
|
|
679
|
+
spin.error(e instanceof Error ? e.message : String(e));
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
outro(picocolors.bold(picocolors.greenBright("Done")));
|
|
683
|
+
}
|
|
684
|
+
//#endregion
|
|
622
685
|
//#region src/index.ts
|
|
623
|
-
const
|
|
624
|
-
|
|
686
|
+
const cli = cac("fumadocs");
|
|
687
|
+
cli.option("--config <string>", "path to the config file");
|
|
688
|
+
cli.command("", "init a `cli.json` config file").action(async () => {
|
|
625
689
|
if (await initConfig()) console.log(picocolors.green("Initialized a `./cli.json` config file."));
|
|
626
690
|
else console.log(picocolors.redBright("A config file already exists."));
|
|
627
691
|
});
|
|
628
|
-
|
|
692
|
+
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) => {
|
|
629
693
|
await customise(await createOrLoadConfig(options.config), createClientFromDir(options.dir));
|
|
630
694
|
});
|
|
631
695
|
const dirShortcuts = {
|
|
632
696
|
":preview": "https://preview.fumadocs.dev/registry",
|
|
633
697
|
":dev": "http://localhost:3000/registry"
|
|
634
698
|
};
|
|
635
|
-
|
|
699
|
+
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) => {
|
|
636
700
|
const config = await createOrLoadConfig(options.config);
|
|
637
701
|
await add(input, createClientFromDir(options.dir), config);
|
|
638
702
|
});
|
|
639
|
-
|
|
703
|
+
registerFeatureCommands(cli, createClientFromDir);
|
|
704
|
+
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) => {
|
|
705
|
+
if (format !== "epub") throw new Error(`unsupported format: ${format}, only \`epub\` is supported.`);
|
|
706
|
+
if (!options.framework) throw new Error("option `--framework <name>` is required.");
|
|
640
707
|
await exportEpub({
|
|
641
|
-
output: options.output,
|
|
642
708
|
framework: options.framework,
|
|
643
|
-
|
|
709
|
+
output: options.output
|
|
644
710
|
});
|
|
645
711
|
});
|
|
646
|
-
|
|
712
|
+
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 }) => {
|
|
647
713
|
const jsExtensions = [
|
|
648
714
|
".js",
|
|
649
715
|
".tsx",
|
|
@@ -666,7 +732,15 @@ function createClientFromDir(dir = "https://fumadocs.dev/registry") {
|
|
|
666
732
|
if (dir in dirShortcuts) dir = dirShortcuts[dir];
|
|
667
733
|
return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryConnector(dir) : new LocalRegistryConnector(dir);
|
|
668
734
|
}
|
|
669
|
-
|
|
735
|
+
cli.help();
|
|
736
|
+
cli.version(version);
|
|
737
|
+
try {
|
|
738
|
+
cli.parse(process.argv, { run: false });
|
|
739
|
+
await cli.runMatchedCommand();
|
|
740
|
+
} catch (e) {
|
|
741
|
+
console.error(picocolors.redBright(e instanceof Error ? e.message : String(e)));
|
|
742
|
+
process.exit(1);
|
|
743
|
+
}
|
|
670
744
|
//#endregion
|
|
671
745
|
export {};
|
|
672
746
|
|