@farming-labs/docs 0.0.37 → 0.0.43
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/cli/index.d.mts +1 -1
- package/dist/cli/index.mjs +325 -88
- package/dist/index.d.mts +3 -1283
- package/dist/index.mjs +2 -1
- package/dist/server.d.mts +29 -0
- package/dist/server.mjs +521 -0
- package/dist/types-Cg_hJBKj.d.mts +1330 -0
- package/package.json +7 -1
package/dist/cli/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/cli/index.d.ts
|
|
2
2
|
/** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs, --framework astro (exported for tests). */
|
|
3
|
-
declare function parseFlags(argv: string[]): Record<string, string | undefined>;
|
|
3
|
+
declare function parseFlags(argv: string[]): Record<string, string | boolean | undefined>;
|
|
4
4
|
//#endregion
|
|
5
5
|
export { parseFlags };
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import pc from "picocolors";
|
|
4
5
|
import * as p from "@clack/prompts";
|
|
5
|
-
import fs from "node:fs";
|
|
6
6
|
import { execSync, spawn } from "node:child_process";
|
|
7
7
|
|
|
8
8
|
//#region src/cli/utils.ts
|
|
@@ -273,6 +273,11 @@ function renderI18nConfig(cfg, indent = " ") {
|
|
|
273
273
|
if (!i18n || i18n.locales.length === 0) return "";
|
|
274
274
|
return `${indent}i18n: {\n${indent} locales: [${i18n.locales.map((locale) => `"${locale}"`).join(", ")}],\n${indent} defaultLocale: "${i18n.defaultLocale}",\n${indent}},\n`;
|
|
275
275
|
}
|
|
276
|
+
function renderApiReferenceConfig(cfg, indent = " ") {
|
|
277
|
+
const apiReference = cfg.apiReference;
|
|
278
|
+
if (!apiReference) return "";
|
|
279
|
+
return `${indent}apiReference: {\n${indent} enabled: true,\n${indent} path: "${apiReference.path}",\n${indent} routeRoot: "${apiReference.routeRoot}",\n${indent}},\n`;
|
|
280
|
+
}
|
|
276
281
|
function toLocaleImportName(locale) {
|
|
277
282
|
return `LocalePage_${locale.replace(/[^a-zA-Z0-9_$]/g, "_")}`;
|
|
278
283
|
}
|
|
@@ -329,6 +334,10 @@ function nextDocsLayoutConfigImport(useAlias, nextAppDir = "app") {
|
|
|
329
334
|
if (useAlias) return "@/docs.config";
|
|
330
335
|
return nextAppDir === "src/app" ? "../../../docs.config" : "../../docs.config";
|
|
331
336
|
}
|
|
337
|
+
function nextApiReferenceConfigImport(useAlias, nextAppDir = "app", filePath) {
|
|
338
|
+
if (useAlias) return "@/docs.config";
|
|
339
|
+
return relativeImport(filePath, "docs.config.ts");
|
|
340
|
+
}
|
|
332
341
|
/** Config import for SvelteKit src/lib/docs.server.ts → src/lib/docs.config */
|
|
333
342
|
function svelteServerConfigImport(useAlias) {
|
|
334
343
|
return useAlias ? "$lib/docs.config" : "./docs.config";
|
|
@@ -365,7 +374,7 @@ import { ${exportName} } from "${cfg.useAlias ? "@/themes/" + cfg.customThemeNam
|
|
|
365
374
|
|
|
366
375
|
export default defineDocs({
|
|
367
376
|
entry: "${cfg.entry}",
|
|
368
|
-
${renderI18nConfig(cfg)} theme: ${exportName}({
|
|
377
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
|
|
369
378
|
ui: {
|
|
370
379
|
colors: { primary: "#6366f1" },
|
|
371
380
|
},
|
|
@@ -385,7 +394,7 @@ import { ${t.factory} } from "${t.nextImport}";
|
|
|
385
394
|
|
|
386
395
|
export default defineDocs({
|
|
387
396
|
entry: "${cfg.entry}",
|
|
388
|
-
${renderI18nConfig(cfg)} theme: ${t.factory}({
|
|
397
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
|
|
389
398
|
ui: {
|
|
390
399
|
colors: { primary: "#6366f1" },
|
|
391
400
|
},
|
|
@@ -523,6 +532,17 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|
|
523
532
|
}
|
|
524
533
|
`;
|
|
525
534
|
}
|
|
535
|
+
function nextApiReferenceRouteTemplate(cfg, filePath) {
|
|
536
|
+
const appDir = cfg.nextAppDir ?? "app";
|
|
537
|
+
return `
|
|
538
|
+
import docsConfig from "${nextApiReferenceConfigImport(cfg.useAlias, appDir, filePath)}";
|
|
539
|
+
import { createNextApiReference } from "@farming-labs/next/api-reference";
|
|
540
|
+
|
|
541
|
+
export const GET = createNextApiReference(docsConfig);
|
|
542
|
+
|
|
543
|
+
export const revalidate = false;
|
|
544
|
+
`;
|
|
545
|
+
}
|
|
526
546
|
function nextLocaleDocPageTemplate(defaultLocale) {
|
|
527
547
|
return `\
|
|
528
548
|
import type { ComponentType } from "react";
|
|
@@ -789,7 +809,7 @@ import { ${exportName} } from "./themes/${cfg.customThemeName.replace(/\.ts$/i,
|
|
|
789
809
|
export default defineDocs({
|
|
790
810
|
entry: "${cfg.entry}",
|
|
791
811
|
contentDir: "${cfg.entry}",
|
|
792
|
-
theme: ${exportName}({
|
|
812
|
+
${renderApiReferenceConfig(cfg)} theme: ${exportName}({
|
|
793
813
|
ui: {
|
|
794
814
|
colors: { primary: "#6366f1" },
|
|
795
815
|
},
|
|
@@ -817,7 +837,7 @@ import { ${t.factory} } from "${t.nextImport}";
|
|
|
817
837
|
export default defineDocs({
|
|
818
838
|
entry: "${cfg.entry}",
|
|
819
839
|
contentDir: "${cfg.entry}",
|
|
820
|
-
theme: ${t.factory}({
|
|
840
|
+
${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
|
|
821
841
|
ui: {
|
|
822
842
|
colors: { primary: "#6366f1" },
|
|
823
843
|
},
|
|
@@ -948,6 +968,24 @@ export const Route = createFileRoute("/api/docs")({
|
|
|
948
968
|
});
|
|
949
969
|
`;
|
|
950
970
|
}
|
|
971
|
+
function tanstackApiReferenceRouteTemplate(opts) {
|
|
972
|
+
const routePath = `/${opts.apiReferencePath}${opts.catchAll ? "/$" : "/"}`;
|
|
973
|
+
return `\
|
|
974
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
975
|
+
import { createTanstackApiReference } from "@farming-labs/tanstack-start/api-reference";
|
|
976
|
+
import docsConfig from "${tanstackDocsConfigImport(opts.filePath)}";
|
|
977
|
+
|
|
978
|
+
const handler = createTanstackApiReference(docsConfig);
|
|
979
|
+
|
|
980
|
+
export const Route = createFileRoute("${routePath}")({
|
|
981
|
+
server: {
|
|
982
|
+
handlers: {
|
|
983
|
+
GET: handler,
|
|
984
|
+
},
|
|
985
|
+
},
|
|
986
|
+
});
|
|
987
|
+
`;
|
|
988
|
+
}
|
|
951
989
|
function tanstackRootRouteTemplate(globalCssRelPath) {
|
|
952
990
|
return `\
|
|
953
991
|
import appCss from "${relativeAssetPath("src/routes/__root.tsx", globalCssRelPath)}?url";
|
|
@@ -1197,7 +1235,7 @@ import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/
|
|
|
1197
1235
|
export default defineDocs({
|
|
1198
1236
|
entry: "${cfg.entry}",
|
|
1199
1237
|
contentDir: "${cfg.entry}",
|
|
1200
|
-
${renderI18nConfig(cfg)} theme: ${exportName}({
|
|
1238
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
|
|
1201
1239
|
ui: {
|
|
1202
1240
|
colors: { primary: "#6366f1" },
|
|
1203
1241
|
},
|
|
@@ -1225,7 +1263,7 @@ import { ${t.factory} } from "${t.svelteImport}";
|
|
|
1225
1263
|
export default defineDocs({
|
|
1226
1264
|
entry: "${cfg.entry}",
|
|
1227
1265
|
contentDir: "${cfg.entry}",
|
|
1228
|
-
${renderI18nConfig(cfg)} theme: ${t.factory}({
|
|
1266
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
|
|
1229
1267
|
ui: {
|
|
1230
1268
|
colors: { primary: "#6366f1" },
|
|
1231
1269
|
},
|
|
@@ -1294,6 +1332,14 @@ function svelteDocsPageTemplate(cfg) {
|
|
|
1294
1332
|
<DocsContent {data} {config} />
|
|
1295
1333
|
`;
|
|
1296
1334
|
}
|
|
1335
|
+
function svelteApiReferenceRouteTemplate(filePath, useAlias) {
|
|
1336
|
+
return `\
|
|
1337
|
+
import { createSvelteApiReference } from "@farming-labs/svelte/api-reference";
|
|
1338
|
+
import config from "${useAlias ? "$lib/docs.config" : relativeImport(filePath, "src/lib/docs.config.ts")}";
|
|
1339
|
+
|
|
1340
|
+
export const GET = createSvelteApiReference(config);
|
|
1341
|
+
`;
|
|
1342
|
+
}
|
|
1297
1343
|
function svelteRootLayoutTemplate(globalCssRelPath) {
|
|
1298
1344
|
let cssImport;
|
|
1299
1345
|
if (globalCssRelPath.startsWith("src/")) cssImport = "./" + globalCssRelPath.slice(4);
|
|
@@ -1507,7 +1553,7 @@ import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/
|
|
|
1507
1553
|
export default defineDocs({
|
|
1508
1554
|
entry: "${cfg.entry}",
|
|
1509
1555
|
contentDir: "${cfg.entry}",
|
|
1510
|
-
${renderI18nConfig(cfg)} theme: ${exportName}({
|
|
1556
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
|
|
1511
1557
|
ui: {
|
|
1512
1558
|
colors: { primary: "#6366f1" },
|
|
1513
1559
|
},
|
|
@@ -1535,7 +1581,7 @@ import { ${t.factory} } from "${t.astroImport}";
|
|
|
1535
1581
|
export default defineDocs({
|
|
1536
1582
|
entry: "${cfg.entry}",
|
|
1537
1583
|
contentDir: "${cfg.entry}",
|
|
1538
|
-
${renderI18nConfig(cfg)} theme: ${t.factory}({
|
|
1584
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
|
|
1539
1585
|
ui: {
|
|
1540
1586
|
colors: { primary: "#6366f1" },
|
|
1541
1587
|
},
|
|
@@ -1676,6 +1722,14 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
1676
1722
|
};
|
|
1677
1723
|
`;
|
|
1678
1724
|
}
|
|
1725
|
+
function astroApiReferenceRouteTemplate(filePath) {
|
|
1726
|
+
return `\
|
|
1727
|
+
import { createAstroApiReference } from "@farming-labs/astro/api-reference";
|
|
1728
|
+
import config from "${relativeImport(filePath, "src/lib/docs.config.ts")}";
|
|
1729
|
+
|
|
1730
|
+
export const GET = createAstroApiReference(config);
|
|
1731
|
+
`;
|
|
1732
|
+
}
|
|
1679
1733
|
function astroGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
|
|
1680
1734
|
if (theme === "custom" && customThemeName && globalCssRelPath) return `\
|
|
1681
1735
|
@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
|
|
@@ -1863,7 +1917,7 @@ import { ${exportName} } from "${cfg.useAlias ? "~/themes/" + cfg.customThemeNam
|
|
|
1863
1917
|
export default defineDocs({
|
|
1864
1918
|
entry: "${cfg.entry}",
|
|
1865
1919
|
contentDir: "${cfg.entry}",
|
|
1866
|
-
${renderI18nConfig(cfg)} theme: ${exportName}({
|
|
1920
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
|
|
1867
1921
|
ui: {
|
|
1868
1922
|
colors: { primary: "#6366f1" },
|
|
1869
1923
|
},
|
|
@@ -1891,7 +1945,7 @@ import { ${t.factory} } from "${t.nuxtImport}";
|
|
|
1891
1945
|
export default defineDocs({
|
|
1892
1946
|
entry: "${cfg.entry}",
|
|
1893
1947
|
contentDir: "${cfg.entry}",
|
|
1894
|
-
${renderI18nConfig(cfg)} theme: ${t.factory}({
|
|
1948
|
+
${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
|
|
1895
1949
|
ui: {
|
|
1896
1950
|
colors: { primary: "#6366f1" },
|
|
1897
1951
|
},
|
|
@@ -1973,6 +2027,14 @@ export default defineEventHandler(async (event) => {
|
|
|
1973
2027
|
});
|
|
1974
2028
|
`;
|
|
1975
2029
|
}
|
|
2030
|
+
function nuxtServerApiReferenceRouteTemplate(filePath, useAlias) {
|
|
2031
|
+
return `\
|
|
2032
|
+
import { defineApiReferenceHandler } from "@farming-labs/nuxt/api-reference";
|
|
2033
|
+
import config from "${useAlias ? "~/docs.config" : relativeImport(filePath, "docs.config.ts")}";
|
|
2034
|
+
|
|
2035
|
+
export default defineApiReferenceHandler(config);
|
|
2036
|
+
`;
|
|
2037
|
+
}
|
|
1976
2038
|
function nuxtDocsPageTemplate(cfg) {
|
|
1977
2039
|
return `\
|
|
1978
2040
|
<script setup lang="ts">
|
|
@@ -2295,6 +2357,55 @@ function normalizeEntryPath(entry) {
|
|
|
2295
2357
|
function getTanstackDocsRouteDir(entry) {
|
|
2296
2358
|
return path.posix.join("src/routes", normalizeEntryPath(entry));
|
|
2297
2359
|
}
|
|
2360
|
+
function normalizeApiRouteRoot(routeRoot) {
|
|
2361
|
+
return routeRoot.replace(/^\/+|\/+$/g, "");
|
|
2362
|
+
}
|
|
2363
|
+
function detectApiRouteRoot(cwd, framework, nextAppDir = "app") {
|
|
2364
|
+
const defaultRoot = "api";
|
|
2365
|
+
const detectFromRecursiveRouteFiles = (baseDir, matcher) => {
|
|
2366
|
+
if (!fs.existsSync(baseDir)) return null;
|
|
2367
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
2368
|
+
const walk = (dir, prefix = "") => {
|
|
2369
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
2370
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2371
|
+
const fullPath = path.join(dir, entry.name);
|
|
2372
|
+
if (entry.isDirectory()) {
|
|
2373
|
+
walk(fullPath, relativePath);
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
if (!matcher(entry, relativePath)) continue;
|
|
2377
|
+
const [topLevel] = relativePath.split("/");
|
|
2378
|
+
if (!topLevel) continue;
|
|
2379
|
+
candidates.set(topLevel, (candidates.get(topLevel) ?? 0) + 1);
|
|
2380
|
+
}
|
|
2381
|
+
};
|
|
2382
|
+
walk(baseDir);
|
|
2383
|
+
return Array.from(candidates.entries()).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
2384
|
+
};
|
|
2385
|
+
if (framework === "nextjs") {
|
|
2386
|
+
const appRoot = path.join(cwd, nextAppDir);
|
|
2387
|
+
if (fs.existsSync(path.join(appRoot, defaultRoot))) return defaultRoot;
|
|
2388
|
+
return detectFromRecursiveRouteFiles(appRoot, (_entry, relativePath) => /\/?route\.(?:[cm]?[jt]sx?)$/i.test(relativePath)) ?? defaultRoot;
|
|
2389
|
+
}
|
|
2390
|
+
if (framework === "tanstack-start") {
|
|
2391
|
+
const routesRoot = path.join(cwd, "src/routes");
|
|
2392
|
+
if (fs.existsSync(path.join(routesRoot, defaultRoot))) return defaultRoot;
|
|
2393
|
+
return detectFromRecursiveRouteFiles(routesRoot, (_entry, relativePath) => /(?:^|\/)[^/]+\.(?:[cm]?[jt]sx?)$/i.test(relativePath)) ?? defaultRoot;
|
|
2394
|
+
}
|
|
2395
|
+
if (framework === "sveltekit") {
|
|
2396
|
+
const routesRoot = path.join(cwd, "src/routes");
|
|
2397
|
+
if (fs.existsSync(path.join(routesRoot, defaultRoot))) return defaultRoot;
|
|
2398
|
+
return detectFromRecursiveRouteFiles(routesRoot, (_entry, relativePath) => /\/?\+server\.(?:[cm]?[jt]s)$/i.test(relativePath)) ?? defaultRoot;
|
|
2399
|
+
}
|
|
2400
|
+
if (framework === "astro") {
|
|
2401
|
+
const pagesRoot = path.join(cwd, "src/pages");
|
|
2402
|
+
if (fs.existsSync(path.join(pagesRoot, defaultRoot))) return defaultRoot;
|
|
2403
|
+
return detectFromRecursiveRouteFiles(pagesRoot, (entry, relativePath) => /\.(?:[cm]?[jt]s)$/i.test(relativePath) && !relativePath.endsWith(".d.ts") && entry.isFile()) ?? defaultRoot;
|
|
2404
|
+
}
|
|
2405
|
+
const serverRoot = path.join(cwd, "server");
|
|
2406
|
+
if (fs.existsSync(path.join(serverRoot, defaultRoot))) return defaultRoot;
|
|
2407
|
+
return detectFromRecursiveRouteFiles(serverRoot, (entry, relativePath) => /\.(?:[cm]?[jt]s)$/i.test(relativePath) && !relativePath.endsWith(".d.ts") && entry.isFile()) ?? defaultRoot;
|
|
2408
|
+
}
|
|
2298
2409
|
async function init(options = {}) {
|
|
2299
2410
|
const cwd = process.cwd();
|
|
2300
2411
|
p.intro(pc.bgCyan(pc.black(" @farming-labs/docs ")));
|
|
@@ -2499,54 +2610,66 @@ async function init(options = {}) {
|
|
|
2499
2610
|
nextAppDir = useSrcApp ? "src/app" : "app";
|
|
2500
2611
|
}
|
|
2501
2612
|
}
|
|
2502
|
-
const
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2613
|
+
const themeOptions = [
|
|
2614
|
+
{
|
|
2615
|
+
value: "fumadocs",
|
|
2616
|
+
label: "Fumadocs (Default)",
|
|
2617
|
+
hint: "Clean, modern docs theme with sidebar, search, and dark mode"
|
|
2618
|
+
},
|
|
2619
|
+
{
|
|
2620
|
+
value: "darksharp",
|
|
2621
|
+
label: "Darksharp",
|
|
2622
|
+
hint: "All-black, sharp edges, zero-radius look"
|
|
2623
|
+
},
|
|
2624
|
+
{
|
|
2625
|
+
value: "pixel-border",
|
|
2626
|
+
label: "Pixel Border",
|
|
2627
|
+
hint: "Rounded borders, pixel-perfect spacing, refined sidebar"
|
|
2628
|
+
},
|
|
2629
|
+
{
|
|
2630
|
+
value: "colorful",
|
|
2631
|
+
label: "Colorful",
|
|
2632
|
+
hint: "Fumadocs-style neutral theme with description support"
|
|
2633
|
+
},
|
|
2634
|
+
{
|
|
2635
|
+
value: "darkbold",
|
|
2636
|
+
label: "DarkBold",
|
|
2637
|
+
hint: "Pure monochrome, Geist typography, clean minimalism"
|
|
2638
|
+
},
|
|
2639
|
+
{
|
|
2640
|
+
value: "shiny",
|
|
2641
|
+
label: "Shiny",
|
|
2642
|
+
hint: "Glossy, modern look with subtle shimmer effects"
|
|
2643
|
+
},
|
|
2644
|
+
{
|
|
2645
|
+
value: "greentree",
|
|
2646
|
+
label: "GreenTree",
|
|
2647
|
+
hint: "Emerald green accent, Inter font, Mintlify-inspired"
|
|
2648
|
+
},
|
|
2649
|
+
{
|
|
2650
|
+
value: "custom",
|
|
2651
|
+
label: "Create your own theme",
|
|
2652
|
+
hint: "Scaffold a new theme file + CSS in themes/ (name asked next)"
|
|
2653
|
+
}
|
|
2654
|
+
];
|
|
2655
|
+
const themeValues = new Set(themeOptions.map((option) => option.value));
|
|
2656
|
+
let theme;
|
|
2657
|
+
if (options.theme) {
|
|
2658
|
+
if (!themeValues.has(options.theme)) {
|
|
2659
|
+
p.log.error(`Invalid ${pc.cyan("--theme")}. Use one of: ${themeOptions.map((option) => pc.cyan(option.value)).join(", ")}`);
|
|
2660
|
+
process.exit(1);
|
|
2661
|
+
}
|
|
2662
|
+
theme = options.theme;
|
|
2663
|
+
} else {
|
|
2664
|
+
const themeAnswer = await p.select({
|
|
2665
|
+
message: "Which theme would you like to use?",
|
|
2666
|
+
options: themeOptions
|
|
2667
|
+
});
|
|
2668
|
+
if (p.isCancel(themeAnswer)) {
|
|
2669
|
+
p.outro(pc.red("Init cancelled."));
|
|
2670
|
+
process.exit(0);
|
|
2671
|
+
}
|
|
2672
|
+
theme = themeAnswer;
|
|
2550
2673
|
}
|
|
2551
2674
|
const defaultThemeName = "my-theme";
|
|
2552
2675
|
let customThemeName;
|
|
@@ -2609,21 +2732,84 @@ async function init(options = {}) {
|
|
|
2609
2732
|
astroAdapter = adapter;
|
|
2610
2733
|
}
|
|
2611
2734
|
const defaultEntry = "docs";
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
if (v.startsWith("/")) return "Use a relative path (no leading /)";
|
|
2619
|
-
if (v.includes(" ")) return "Path cannot contain spaces";
|
|
2735
|
+
let entryPath;
|
|
2736
|
+
if (options.entry) {
|
|
2737
|
+
const normalizedEntry = options.entry.trim();
|
|
2738
|
+
if (normalizedEntry.startsWith("/")) {
|
|
2739
|
+
p.log.error("Use a relative path for --entry (no leading /)");
|
|
2740
|
+
process.exit(1);
|
|
2620
2741
|
}
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2742
|
+
if (normalizedEntry.includes(" ")) {
|
|
2743
|
+
p.log.error("Path passed to --entry cannot contain spaces");
|
|
2744
|
+
process.exit(1);
|
|
2745
|
+
}
|
|
2746
|
+
entryPath = normalizedEntry || defaultEntry;
|
|
2747
|
+
} else {
|
|
2748
|
+
const entry = await p.text({
|
|
2749
|
+
message: "Where should your docs live?",
|
|
2750
|
+
placeholder: defaultEntry,
|
|
2751
|
+
defaultValue: defaultEntry,
|
|
2752
|
+
validate: (value) => {
|
|
2753
|
+
const v = (value ?? "").trim();
|
|
2754
|
+
if (v.startsWith("/")) return "Use a relative path (no leading /)";
|
|
2755
|
+
if (v.includes(" ")) return "Path cannot contain spaces";
|
|
2756
|
+
}
|
|
2757
|
+
});
|
|
2758
|
+
if (p.isCancel(entry)) {
|
|
2759
|
+
p.outro(pc.red("Init cancelled."));
|
|
2760
|
+
process.exit(0);
|
|
2761
|
+
}
|
|
2762
|
+
entryPath = entry.trim() || defaultEntry;
|
|
2763
|
+
}
|
|
2764
|
+
const defaultApiRouteRoot = normalizeApiRouteRoot(options.apiRouteRoot?.trim() || detectApiRouteRoot(cwd, framework, nextAppDir));
|
|
2765
|
+
let apiReferenceConfig;
|
|
2766
|
+
let enableApiReference;
|
|
2767
|
+
if (typeof options.apiReference === "boolean") enableApiReference = options.apiReference;
|
|
2768
|
+
else if (typeof options.apiRouteRoot === "string" && options.apiRouteRoot.trim()) enableApiReference = true;
|
|
2769
|
+
else {
|
|
2770
|
+
const apiReferenceAnswer = await p.confirm({
|
|
2771
|
+
message: "Do you want to scaffold API reference support?",
|
|
2772
|
+
initialValue: false
|
|
2773
|
+
});
|
|
2774
|
+
if (p.isCancel(apiReferenceAnswer)) {
|
|
2775
|
+
p.outro(pc.red("Init cancelled."));
|
|
2776
|
+
process.exit(0);
|
|
2777
|
+
}
|
|
2778
|
+
enableApiReference = apiReferenceAnswer;
|
|
2779
|
+
}
|
|
2780
|
+
if (enableApiReference) {
|
|
2781
|
+
let routeRoot = options.apiRouteRoot?.trim();
|
|
2782
|
+
if (!routeRoot) {
|
|
2783
|
+
const routeRootAnswer = await p.text({
|
|
2784
|
+
message: "API route root to scan?",
|
|
2785
|
+
placeholder: defaultApiRouteRoot,
|
|
2786
|
+
defaultValue: defaultApiRouteRoot,
|
|
2787
|
+
validate: (value) => {
|
|
2788
|
+
const normalizedValue = normalizeApiRouteRoot((value ?? "").trim());
|
|
2789
|
+
if (!normalizedValue) return "Route root cannot be empty";
|
|
2790
|
+
if (normalizedValue.includes(" ")) return "Route root cannot contain spaces";
|
|
2791
|
+
}
|
|
2792
|
+
});
|
|
2793
|
+
if (p.isCancel(routeRootAnswer)) {
|
|
2794
|
+
p.outro(pc.red("Init cancelled."));
|
|
2795
|
+
process.exit(0);
|
|
2796
|
+
}
|
|
2797
|
+
routeRoot = routeRootAnswer.trim() || defaultApiRouteRoot;
|
|
2798
|
+
}
|
|
2799
|
+
const normalizedRouteRoot = normalizeApiRouteRoot(routeRoot);
|
|
2800
|
+
if (!normalizedRouteRoot) {
|
|
2801
|
+
p.log.error("Route root cannot be empty");
|
|
2802
|
+
process.exit(1);
|
|
2803
|
+
}
|
|
2804
|
+
if (normalizedRouteRoot.includes(" ")) {
|
|
2805
|
+
p.log.error("Route root cannot contain spaces");
|
|
2806
|
+
process.exit(1);
|
|
2807
|
+
}
|
|
2808
|
+
apiReferenceConfig = {
|
|
2809
|
+
path: "api-reference",
|
|
2810
|
+
routeRoot: normalizedRouteRoot
|
|
2811
|
+
};
|
|
2625
2812
|
}
|
|
2626
|
-
const entryPath = entry.trim() || defaultEntry;
|
|
2627
2813
|
let docsI18n;
|
|
2628
2814
|
if (framework === "tanstack-start") p.log.info("Skipping i18n scaffold for TanStack Start. Configure localized routes manually if needed.");
|
|
2629
2815
|
else {
|
|
@@ -2733,6 +2919,7 @@ async function init(options = {}) {
|
|
|
2733
2919
|
useAlias,
|
|
2734
2920
|
astroAdapter,
|
|
2735
2921
|
i18n: docsI18n,
|
|
2922
|
+
apiReference: apiReferenceConfig,
|
|
2736
2923
|
...framework === "nextjs" && { nextAppDir }
|
|
2737
2924
|
};
|
|
2738
2925
|
const s = p.spinner();
|
|
@@ -2929,6 +3116,10 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
2929
3116
|
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
2930
3117
|
} else write(globalCssRelPath, globalCssTemplate(cfg.theme, cfg.customThemeName, globalCssRelPath));
|
|
2931
3118
|
write(`${appDir}/${cfg.entry}/layout.tsx`, docsLayoutTemplate(cfg));
|
|
3119
|
+
if (cfg.apiReference) {
|
|
3120
|
+
const apiReferenceRoute = `${appDir}/${cfg.apiReference.path}/[[...slug]]/route.ts`;
|
|
3121
|
+
write(apiReferenceRoute, nextApiReferenceRouteTemplate(cfg, apiReferenceRoute));
|
|
3122
|
+
}
|
|
2932
3123
|
write("postcss.config.mjs", postcssConfigTemplate());
|
|
2933
3124
|
if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate(cfg.useAlias));
|
|
2934
3125
|
if (cfg.i18n?.locales.length) {
|
|
@@ -3001,6 +3192,22 @@ function scaffoldTanstackStart(cwd, cfg, globalCssRelPath, write, skipped, writt
|
|
|
3001
3192
|
projectName: cfg.projectName
|
|
3002
3193
|
}));
|
|
3003
3194
|
write(apiRoute, tanstackApiDocsRouteTemplate(cfg.useAlias, apiRoute));
|
|
3195
|
+
if (cfg.apiReference) {
|
|
3196
|
+
const apiReferenceIndexRoute = `src/routes/${cfg.apiReference.path}.index.ts`;
|
|
3197
|
+
const apiReferenceCatchAllRoute = `src/routes/${cfg.apiReference.path}.$.ts`;
|
|
3198
|
+
write(apiReferenceIndexRoute, tanstackApiReferenceRouteTemplate({
|
|
3199
|
+
filePath: apiReferenceIndexRoute,
|
|
3200
|
+
useAlias: cfg.useAlias,
|
|
3201
|
+
apiReferencePath: cfg.apiReference.path,
|
|
3202
|
+
catchAll: false
|
|
3203
|
+
}));
|
|
3204
|
+
write(apiReferenceCatchAllRoute, tanstackApiReferenceRouteTemplate({
|
|
3205
|
+
filePath: apiReferenceCatchAllRoute,
|
|
3206
|
+
useAlias: cfg.useAlias,
|
|
3207
|
+
apiReferencePath: cfg.apiReference.path,
|
|
3208
|
+
catchAll: true
|
|
3209
|
+
}));
|
|
3210
|
+
}
|
|
3004
3211
|
const rootRoutePath = path.join(cwd, "src/routes/__root.tsx");
|
|
3005
3212
|
const existingRootRoute = readFileSafe(rootRoutePath);
|
|
3006
3213
|
if (!existingRootRoute) write("src/routes/__root.tsx", tanstackRootRouteTemplate(globalCssRelPath), true);
|
|
@@ -3049,6 +3256,12 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
|
|
|
3049
3256
|
write(`src/routes/${cfg.entry}/+layout.server.js`, svelteDocsLayoutServerTemplate(cfg));
|
|
3050
3257
|
write(`src/routes/${cfg.entry}/[...slug]/+page.svelte`, svelteDocsPageTemplate(cfg));
|
|
3051
3258
|
if (cfg.i18n?.locales.length) write(`src/routes/${cfg.entry}/+page.svelte`, svelteDocsPageTemplate(cfg));
|
|
3259
|
+
if (cfg.apiReference) {
|
|
3260
|
+
const apiReferenceIndexRoute = `src/routes/${cfg.apiReference.path}/+server.ts`;
|
|
3261
|
+
const apiReferenceCatchAllRoute = `src/routes/${cfg.apiReference.path}/[...slug]/+server.ts`;
|
|
3262
|
+
write(apiReferenceIndexRoute, svelteApiReferenceRouteTemplate(apiReferenceIndexRoute, cfg.useAlias));
|
|
3263
|
+
write(apiReferenceCatchAllRoute, svelteApiReferenceRouteTemplate(apiReferenceCatchAllRoute, cfg.useAlias));
|
|
3264
|
+
}
|
|
3052
3265
|
if (!readFileSafe(path.join(cwd, "src/routes/+layout.svelte"))) write("src/routes/+layout.svelte", svelteRootLayoutTemplate(globalCssRelPath));
|
|
3053
3266
|
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
3054
3267
|
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
@@ -3087,6 +3300,12 @@ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
3087
3300
|
write(`src/pages/${cfg.entry}/index.astro`, astroDocsIndexTemplate(cfg));
|
|
3088
3301
|
write(`src/pages/${cfg.entry}/[...slug].astro`, astroDocsPageTemplate(cfg));
|
|
3089
3302
|
write(`src/pages/api/${cfg.entry}.ts`, astroApiRouteTemplate(cfg));
|
|
3303
|
+
if (cfg.apiReference) {
|
|
3304
|
+
const apiReferenceIndexRoute = `src/pages/${cfg.apiReference.path}/index.ts`;
|
|
3305
|
+
const apiReferenceCatchAllRoute = `src/pages/${cfg.apiReference.path}/[...slug].ts`;
|
|
3306
|
+
write(apiReferenceIndexRoute, astroApiReferenceRouteTemplate(apiReferenceIndexRoute));
|
|
3307
|
+
write(apiReferenceCatchAllRoute, astroApiReferenceRouteTemplate(apiReferenceCatchAllRoute));
|
|
3308
|
+
}
|
|
3090
3309
|
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
3091
3310
|
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
3092
3311
|
const cssTheme = {
|
|
@@ -3124,6 +3343,12 @@ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
3124
3343
|
write("server/api/docs.post.ts", nuxtServerApiDocsPostTemplate());
|
|
3125
3344
|
write("server/api/docs/load.get.ts", nuxtServerApiDocsLoadTemplate());
|
|
3126
3345
|
write(`pages/${cfg.entry}/[[...slug]].vue`, nuxtDocsPageTemplate(cfg));
|
|
3346
|
+
if (cfg.apiReference) {
|
|
3347
|
+
const apiReferenceIndexRoute = `server/routes/${cfg.apiReference.path}/index.ts`;
|
|
3348
|
+
const apiReferenceCatchAllRoute = `server/routes/${cfg.apiReference.path}/[...slug].ts`;
|
|
3349
|
+
write(apiReferenceIndexRoute, nuxtServerApiReferenceRouteTemplate(apiReferenceIndexRoute, cfg.useAlias));
|
|
3350
|
+
write(apiReferenceCatchAllRoute, nuxtServerApiReferenceRouteTemplate(apiReferenceCatchAllRoute, cfg.useAlias));
|
|
3351
|
+
}
|
|
3127
3352
|
path.join(cwd, "nuxt.config.ts");
|
|
3128
3353
|
if (!fileExists(path.join(cwd, "nuxt.config.ts")) && !fileExists(path.join(cwd, "nuxt.config.js"))) write("nuxt.config.ts", nuxtConfigTemplate(cfg));
|
|
3129
3354
|
const cssTheme = {
|
|
@@ -3156,10 +3381,11 @@ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
3156
3381
|
//#region src/cli/upgrade.ts
|
|
3157
3382
|
/**
|
|
3158
3383
|
* Upgrade @farming-labs/* packages to latest.
|
|
3159
|
-
* Detects framework from package.json by default, or use --framework (next, nuxt, sveltekit, astro).
|
|
3384
|
+
* Detects framework from package.json by default, or use --framework (next, tanstack-start, nuxt, sveltekit, astro).
|
|
3160
3385
|
*/
|
|
3161
3386
|
const PRESETS = [
|
|
3162
3387
|
"next",
|
|
3388
|
+
"tanstack-start",
|
|
3163
3389
|
"nuxt",
|
|
3164
3390
|
"sveltekit",
|
|
3165
3391
|
"astro"
|
|
@@ -3170,6 +3396,11 @@ const PACKAGES_BY_FRAMEWORK = {
|
|
|
3170
3396
|
"@farming-labs/theme",
|
|
3171
3397
|
"@farming-labs/next"
|
|
3172
3398
|
],
|
|
3399
|
+
"tanstack-start": [
|
|
3400
|
+
"@farming-labs/docs",
|
|
3401
|
+
"@farming-labs/theme",
|
|
3402
|
+
"@farming-labs/tanstack-start"
|
|
3403
|
+
],
|
|
3173
3404
|
nuxt: [
|
|
3174
3405
|
"@farming-labs/docs",
|
|
3175
3406
|
"@farming-labs/nuxt",
|
|
@@ -3223,11 +3454,7 @@ async function upgrade(options = {}) {
|
|
|
3223
3454
|
} else {
|
|
3224
3455
|
const detected = detectFramework(cwd);
|
|
3225
3456
|
if (!detected) {
|
|
3226
|
-
p.log.error("Could not detect a supported framework (Next.js, Nuxt, SvelteKit, Astro). Use " + pc.cyan("--framework <next|nuxt|sveltekit|astro>") + " to specify.");
|
|
3227
|
-
process.exit(1);
|
|
3228
|
-
}
|
|
3229
|
-
if (detected === "tanstack-start") {
|
|
3230
|
-
p.log.error("TanStack Start is supported by " + pc.cyan("init") + " but not by " + pc.cyan("upgrade") + " yet. Upgrade the docs packages manually for now.");
|
|
3457
|
+
p.log.error("Could not detect a supported framework (Next.js, TanStack Start, Nuxt, SvelteKit, Astro). Use " + pc.cyan("--framework <next|tanstack-start|nuxt|sveltekit|astro>") + " to specify.");
|
|
3231
3458
|
process.exit(1);
|
|
3232
3459
|
}
|
|
3233
3460
|
framework = detected;
|
|
@@ -3289,29 +3516,36 @@ const command = args[0];
|
|
|
3289
3516
|
/** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs, --framework astro (exported for tests). */
|
|
3290
3517
|
function parseFlags(argv) {
|
|
3291
3518
|
const flags = {};
|
|
3519
|
+
const booleanFlags = new Set(["api-reference"]);
|
|
3292
3520
|
for (let i = 0; i < argv.length; i++) {
|
|
3293
3521
|
const arg = argv[i];
|
|
3294
3522
|
if (arg.startsWith("--") && arg.includes("=")) {
|
|
3295
3523
|
const [key, value] = arg.slice(2).split("=");
|
|
3296
|
-
flags[key] =
|
|
3524
|
+
if (key.startsWith("no-")) flags[key.slice(3)] = false;
|
|
3525
|
+
else if (booleanFlags.has(key) && value === "true") flags[key] = true;
|
|
3526
|
+
else if (booleanFlags.has(key) && value === "false") flags[key] = false;
|
|
3527
|
+
else flags[key] = value;
|
|
3297
3528
|
} else if (arg.startsWith("--") && argv[i + 1] && !argv[i + 1].startsWith("--")) {
|
|
3298
3529
|
flags[arg.slice(2)] = argv[i + 1];
|
|
3299
3530
|
i++;
|
|
3300
|
-
}
|
|
3531
|
+
} else if (arg.startsWith("--no-")) flags[arg.slice(5)] = false;
|
|
3532
|
+
else if (arg.startsWith("--") && booleanFlags.has(arg.slice(2))) flags[arg.slice(2)] = true;
|
|
3301
3533
|
}
|
|
3302
3534
|
return flags;
|
|
3303
3535
|
}
|
|
3304
3536
|
async function main() {
|
|
3305
3537
|
const flags = parseFlags(args);
|
|
3306
3538
|
const initOptions = {
|
|
3307
|
-
template: flags.template,
|
|
3308
|
-
name: flags.name,
|
|
3309
|
-
theme: flags.theme,
|
|
3310
|
-
entry: flags.entry
|
|
3539
|
+
template: typeof flags.template === "string" ? flags.template : void 0,
|
|
3540
|
+
name: typeof flags.name === "string" ? flags.name : void 0,
|
|
3541
|
+
theme: typeof flags.theme === "string" ? flags.theme : void 0,
|
|
3542
|
+
entry: typeof flags.entry === "string" ? flags.entry : void 0,
|
|
3543
|
+
apiReference: typeof flags["api-reference"] === "boolean" ? flags["api-reference"] : void 0,
|
|
3544
|
+
apiRouteRoot: typeof flags["api-route-root"] === "string" ? flags["api-route-root"] : void 0
|
|
3311
3545
|
};
|
|
3312
3546
|
if (!command || command === "init") await init(initOptions);
|
|
3313
3547
|
else if (command === "upgrade") await upgrade({
|
|
3314
|
-
framework: flags.framework ?? (args[1] && !args[1].startsWith("--") ? args[1] : void 0),
|
|
3548
|
+
framework: (typeof flags.framework === "string" ? flags.framework : void 0) ?? (args[1] && !args[1].startsWith("--") ? args[1] : void 0),
|
|
3315
3549
|
tag: args.includes("--beta") ? "beta" : "latest"
|
|
3316
3550
|
});
|
|
3317
3551
|
else if (command === "--help" || command === "-h") printHelp();
|
|
@@ -3335,16 +3569,19 @@ ${pc.dim("Commands:")}
|
|
|
3335
3569
|
${pc.cyan("upgrade")} Upgrade @farming-labs/* packages to latest (auto-detect or use --framework)
|
|
3336
3570
|
|
|
3337
3571
|
${pc.dim("Supported frameworks:")}
|
|
3338
|
-
Next.js, SvelteKit, Astro, Nuxt
|
|
3572
|
+
Next.js, TanStack Start, SvelteKit, Astro, Nuxt
|
|
3339
3573
|
|
|
3340
3574
|
${pc.dim("Options for init:")}
|
|
3341
3575
|
${pc.cyan("--template <name>")} Bootstrap a project (${pc.dim("next")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}, ${pc.dim("tanstack-start")}); use with ${pc.cyan("--name")}
|
|
3342
3576
|
${pc.cyan("--name <project>")} Project folder name when using ${pc.cyan("--template")}; prompt if omitted (e.g. ${pc.dim("my-docs")})
|
|
3343
3577
|
${pc.cyan("--theme <name>")} Skip theme prompt (e.g. ${pc.dim("darksharp")}, ${pc.dim("greentree")})
|
|
3344
3578
|
${pc.cyan("--entry <path>")} Skip entry path prompt (e.g. ${pc.dim("docs")})
|
|
3579
|
+
${pc.cyan("--api-reference")} Scaffold API reference support during ${pc.cyan("init")}
|
|
3580
|
+
${pc.cyan("--no-api-reference")} Skip API reference scaffold during ${pc.cyan("init")}
|
|
3581
|
+
${pc.cyan("--api-route-root <path>")} Override the API route root scanned by ${pc.cyan("apiReference.routeRoot")} (e.g. ${pc.dim("api")}, ${pc.dim("internal-api")})
|
|
3345
3582
|
|
|
3346
3583
|
${pc.dim("Options for upgrade:")}
|
|
3347
|
-
${pc.cyan("--framework <name>")} Explicit framework (${pc.dim("next")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}); omit to auto-detect
|
|
3584
|
+
${pc.cyan("--framework <name>")} Explicit framework (${pc.dim("next")}, ${pc.dim("tanstack-start")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}); omit to auto-detect
|
|
3348
3585
|
${pc.cyan("--latest")} Install latest stable (default)
|
|
3349
3586
|
${pc.cyan("--beta")} Install beta versions
|
|
3350
3587
|
|