@farming-labs/docs 0.0.2-beta.8 → 0.0.2
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.mjs +992 -40
- package/dist/index.d.mts +322 -21
- package/dist/index.mjs +3 -0
- package/package.json +14 -14
package/dist/cli/index.mjs
CHANGED
|
@@ -16,6 +16,8 @@ function detectFramework(cwd) {
|
|
|
16
16
|
};
|
|
17
17
|
if (allDeps["next"]) return "nextjs";
|
|
18
18
|
if (allDeps["@sveltejs/kit"]) return "sveltekit";
|
|
19
|
+
if (allDeps["astro"]) return "astro";
|
|
20
|
+
if (allDeps["nuxt"]) return "nuxt";
|
|
19
21
|
return null;
|
|
20
22
|
}
|
|
21
23
|
function detectPackageManager(cwd) {
|
|
@@ -65,7 +67,9 @@ const GLOBAL_CSS_CANDIDATES = [
|
|
|
65
67
|
"styles/globals.css",
|
|
66
68
|
"styles/global.css",
|
|
67
69
|
"src/styles/globals.css",
|
|
68
|
-
"src/styles/global.css"
|
|
70
|
+
"src/styles/global.css",
|
|
71
|
+
"assets/css/main.css",
|
|
72
|
+
"assets/main.css"
|
|
69
73
|
];
|
|
70
74
|
/**
|
|
71
75
|
* Find existing global CSS files in the project.
|
|
@@ -133,22 +137,78 @@ const THEME_INFO = {
|
|
|
133
137
|
factory: "fumadocs",
|
|
134
138
|
nextImport: "@farming-labs/theme",
|
|
135
139
|
svelteImport: "@farming-labs/svelte-theme",
|
|
140
|
+
astroImport: "@farming-labs/astro-theme",
|
|
141
|
+
nuxtImport: "@farming-labs/nuxt-theme",
|
|
136
142
|
nextCssImport: "default",
|
|
137
|
-
svelteCssTheme: "fumadocs"
|
|
143
|
+
svelteCssTheme: "fumadocs",
|
|
144
|
+
astroCssTheme: "fumadocs",
|
|
145
|
+
nuxtCssTheme: "fumadocs"
|
|
138
146
|
},
|
|
139
147
|
darksharp: {
|
|
140
148
|
factory: "darksharp",
|
|
141
149
|
nextImport: "@farming-labs/theme/darksharp",
|
|
142
150
|
svelteImport: "@farming-labs/svelte-theme/darksharp",
|
|
151
|
+
astroImport: "@farming-labs/astro-theme/darksharp",
|
|
152
|
+
nuxtImport: "@farming-labs/nuxt-theme/darksharp",
|
|
143
153
|
nextCssImport: "darksharp",
|
|
144
|
-
svelteCssTheme: "darksharp"
|
|
154
|
+
svelteCssTheme: "darksharp",
|
|
155
|
+
astroCssTheme: "darksharp",
|
|
156
|
+
nuxtCssTheme: "darksharp"
|
|
145
157
|
},
|
|
146
158
|
"pixel-border": {
|
|
147
159
|
factory: "pixelBorder",
|
|
148
160
|
nextImport: "@farming-labs/theme/pixel-border",
|
|
149
161
|
svelteImport: "@farming-labs/svelte-theme/pixel-border",
|
|
162
|
+
astroImport: "@farming-labs/astro-theme/pixel-border",
|
|
163
|
+
nuxtImport: "@farming-labs/nuxt-theme/pixel-border",
|
|
150
164
|
nextCssImport: "pixel-border",
|
|
151
|
-
svelteCssTheme: "pixel-border"
|
|
165
|
+
svelteCssTheme: "pixel-border",
|
|
166
|
+
astroCssTheme: "pixel-border",
|
|
167
|
+
nuxtCssTheme: "pixel-border"
|
|
168
|
+
},
|
|
169
|
+
colorful: {
|
|
170
|
+
factory: "colorful",
|
|
171
|
+
nextImport: "@farming-labs/theme/colorful",
|
|
172
|
+
svelteImport: "@farming-labs/svelte-theme/colorful",
|
|
173
|
+
astroImport: "@farming-labs/astro-theme/colorful",
|
|
174
|
+
nuxtImport: "@farming-labs/nuxt-theme/colorful",
|
|
175
|
+
nextCssImport: "colorful",
|
|
176
|
+
svelteCssTheme: "colorful",
|
|
177
|
+
astroCssTheme: "colorful",
|
|
178
|
+
nuxtCssTheme: "colorful"
|
|
179
|
+
},
|
|
180
|
+
darkbold: {
|
|
181
|
+
factory: "darkbold",
|
|
182
|
+
nextImport: "@farming-labs/theme/darkbold",
|
|
183
|
+
svelteImport: "@farming-labs/svelte-theme/darkbold",
|
|
184
|
+
astroImport: "@farming-labs/astro-theme/darkbold",
|
|
185
|
+
nuxtImport: "@farming-labs/nuxt-theme/darkbold",
|
|
186
|
+
nextCssImport: "darkbold",
|
|
187
|
+
svelteCssTheme: "darkbold",
|
|
188
|
+
astroCssTheme: "darkbold",
|
|
189
|
+
nuxtCssTheme: "darkbold"
|
|
190
|
+
},
|
|
191
|
+
shiny: {
|
|
192
|
+
factory: "shiny",
|
|
193
|
+
nextImport: "@farming-labs/theme/shiny",
|
|
194
|
+
svelteImport: "@farming-labs/svelte-theme/shiny",
|
|
195
|
+
astroImport: "@farming-labs/astro-theme/shiny",
|
|
196
|
+
nuxtImport: "@farming-labs/nuxt-theme/shiny",
|
|
197
|
+
nextCssImport: "shiny",
|
|
198
|
+
svelteCssTheme: "shiny",
|
|
199
|
+
astroCssTheme: "shiny",
|
|
200
|
+
nuxtCssTheme: "shiny"
|
|
201
|
+
},
|
|
202
|
+
greentree: {
|
|
203
|
+
factory: "greentree",
|
|
204
|
+
nextImport: "@farming-labs/theme/greentree",
|
|
205
|
+
svelteImport: "@farming-labs/svelte-theme/greentree",
|
|
206
|
+
astroImport: "@farming-labs/astro-theme/greentree",
|
|
207
|
+
nuxtImport: "@farming-labs/nuxt-theme/greentree",
|
|
208
|
+
nextCssImport: "greentree",
|
|
209
|
+
svelteCssTheme: "greentree",
|
|
210
|
+
astroCssTheme: "greentree",
|
|
211
|
+
nuxtCssTheme: "greentree"
|
|
152
212
|
}
|
|
153
213
|
};
|
|
154
214
|
function getThemeInfo(theme) {
|
|
@@ -162,21 +222,32 @@ function nextRootLayoutConfigImport(useAlias) {
|
|
|
162
222
|
function nextDocsLayoutConfigImport(useAlias) {
|
|
163
223
|
return useAlias ? "@/docs.config" : "../../docs.config";
|
|
164
224
|
}
|
|
165
|
-
/** Config import for SvelteKit src/lib/docs.server.ts → src/lib/docs.config
|
|
225
|
+
/** Config import for SvelteKit src/lib/docs.server.ts → src/lib/docs.config */
|
|
166
226
|
function svelteServerConfigImport(useAlias) {
|
|
167
|
-
return useAlias ? "$lib/docs.config
|
|
227
|
+
return useAlias ? "$lib/docs.config" : "./docs.config";
|
|
168
228
|
}
|
|
169
|
-
/** Config import for SvelteKit src/routes/{entry}/+layout.svelte → src/lib/docs.config
|
|
229
|
+
/** Config import for SvelteKit src/routes/{entry}/+layout.svelte → src/lib/docs.config */
|
|
170
230
|
function svelteLayoutConfigImport(useAlias) {
|
|
171
|
-
return useAlias ? "$lib/docs.config
|
|
231
|
+
return useAlias ? "$lib/docs.config" : "../../lib/docs.config";
|
|
172
232
|
}
|
|
173
|
-
/** Config import for SvelteKit src/routes/{entry}/[...slug]/+page.svelte → src/lib/docs.config
|
|
233
|
+
/** Config import for SvelteKit src/routes/{entry}/[...slug]/+page.svelte → src/lib/docs.config */
|
|
174
234
|
function sveltePageConfigImport(useAlias) {
|
|
175
|
-
return useAlias ? "$lib/docs.config
|
|
235
|
+
return useAlias ? "$lib/docs.config" : "../../../lib/docs.config";
|
|
176
236
|
}
|
|
177
|
-
/** Server import for SvelteKit +layout.server.js → src/lib/docs.server
|
|
237
|
+
/** Server import for SvelteKit +layout.server.js → src/lib/docs.server */
|
|
178
238
|
function svelteLayoutServerImport(useAlias) {
|
|
179
|
-
return useAlias ? "$lib/docs.server
|
|
239
|
+
return useAlias ? "$lib/docs.server" : "../../lib/docs.server";
|
|
240
|
+
}
|
|
241
|
+
function astroServerConfigImport(useAlias) {
|
|
242
|
+
return useAlias ? "@/lib/docs.config" : "./docs.config";
|
|
243
|
+
}
|
|
244
|
+
function astroPageConfigImport(useAlias, depth) {
|
|
245
|
+
if (useAlias) return "@/lib/docs.config";
|
|
246
|
+
return `${"../".repeat(depth)}lib/docs.config`;
|
|
247
|
+
}
|
|
248
|
+
function astroPageServerImport(useAlias, depth) {
|
|
249
|
+
if (useAlias) return "@/lib/docs.server";
|
|
250
|
+
return `${"../".repeat(depth)}lib/docs.server`;
|
|
180
251
|
}
|
|
181
252
|
function docsConfigTemplate(cfg) {
|
|
182
253
|
const t = getThemeInfo(cfg.theme);
|
|
@@ -292,7 +363,8 @@ const config = {
|
|
|
292
363
|
export default config;
|
|
293
364
|
`;
|
|
294
365
|
}
|
|
295
|
-
|
|
366
|
+
/** @param useAlias - When false, paths (e.g. @/*) are omitted so no alias is added. */
|
|
367
|
+
function tsconfigTemplate(useAlias = false) {
|
|
296
368
|
return `\
|
|
297
369
|
{
|
|
298
370
|
"compilerOptions": {
|
|
@@ -309,8 +381,7 @@ function tsconfigTemplate() {
|
|
|
309
381
|
"isolatedModules": true,
|
|
310
382
|
"jsx": "react-jsx",
|
|
311
383
|
"incremental": true,
|
|
312
|
-
"plugins": [{ "name": "next" }]
|
|
313
|
-
"paths": { "@/*": ["./*"] }
|
|
384
|
+
"plugins": [{ "name": "next" }]${useAlias ? ",\n \"paths\": { \"@/*\": [\"./*\"] }" : ""}
|
|
314
385
|
},
|
|
315
386
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
316
387
|
"exclude": ["node_modules"]
|
|
@@ -527,7 +598,17 @@ function svelteDocsServerTemplate(cfg) {
|
|
|
527
598
|
import { createDocsServer } from "@farming-labs/svelte/server";
|
|
528
599
|
import config from "${svelteServerConfigImport(cfg.useAlias)}";
|
|
529
600
|
|
|
530
|
-
|
|
601
|
+
// preload for production
|
|
602
|
+
const contentFiles = import.meta.glob("/${cfg.entry ?? "docs"}/**/*.{md,mdx,svx}", {
|
|
603
|
+
query: "?raw",
|
|
604
|
+
import: "default",
|
|
605
|
+
eager: true,
|
|
606
|
+
}) as Record<string, string>;
|
|
607
|
+
|
|
608
|
+
export const { load, GET, POST } = createDocsServer({
|
|
609
|
+
...config,
|
|
610
|
+
_preloadedContent: contentFiles,
|
|
611
|
+
});
|
|
531
612
|
`;
|
|
532
613
|
}
|
|
533
614
|
function svelteDocsLayoutTemplate(cfg) {
|
|
@@ -758,29 +839,741 @@ pnpm build
|
|
|
758
839
|
Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
759
840
|
`;
|
|
760
841
|
}
|
|
842
|
+
function astroDocsConfigTemplate(cfg) {
|
|
843
|
+
const t = getThemeInfo(cfg.theme);
|
|
844
|
+
return `\
|
|
845
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
846
|
+
import { ${t.factory} } from "${t.astroImport}";
|
|
847
|
+
|
|
848
|
+
export default defineDocs({
|
|
849
|
+
entry: "${cfg.entry}",
|
|
850
|
+
contentDir: "${cfg.entry}",
|
|
851
|
+
theme: ${t.factory}({
|
|
852
|
+
ui: {
|
|
853
|
+
colors: { primary: "#6366f1" },
|
|
854
|
+
},
|
|
855
|
+
}),
|
|
856
|
+
|
|
857
|
+
nav: {
|
|
858
|
+
title: "${cfg.projectName}",
|
|
859
|
+
url: "/${cfg.entry}",
|
|
860
|
+
},
|
|
861
|
+
|
|
862
|
+
breadcrumb: { enabled: true },
|
|
863
|
+
|
|
864
|
+
metadata: {
|
|
865
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
866
|
+
description: "Documentation for ${cfg.projectName}",
|
|
867
|
+
},
|
|
868
|
+
});
|
|
869
|
+
`;
|
|
870
|
+
}
|
|
871
|
+
function astroDocsServerTemplate(cfg) {
|
|
872
|
+
return `\
|
|
873
|
+
import { createDocsServer } from "@farming-labs/astro/server";
|
|
874
|
+
import config from "${astroServerConfigImport(cfg.useAlias)}";
|
|
875
|
+
|
|
876
|
+
const contentFiles = import.meta.glob("/${cfg.entry ?? "docs"}/**/*.{md,mdx}", {
|
|
877
|
+
query: "?raw",
|
|
878
|
+
import: "default",
|
|
879
|
+
eager: true,
|
|
880
|
+
}) as Record<string, string>;
|
|
881
|
+
|
|
882
|
+
export const { load, GET, POST } = createDocsServer({
|
|
883
|
+
...config,
|
|
884
|
+
_preloadedContent: contentFiles,
|
|
885
|
+
});
|
|
886
|
+
`;
|
|
887
|
+
}
|
|
888
|
+
const ASTRO_ADAPTER_INFO = {
|
|
889
|
+
vercel: {
|
|
890
|
+
pkg: "@astrojs/vercel",
|
|
891
|
+
import: "@astrojs/vercel"
|
|
892
|
+
},
|
|
893
|
+
netlify: {
|
|
894
|
+
pkg: "@astrojs/netlify",
|
|
895
|
+
import: "@astrojs/netlify"
|
|
896
|
+
},
|
|
897
|
+
node: {
|
|
898
|
+
pkg: "@astrojs/node",
|
|
899
|
+
import: "@astrojs/node"
|
|
900
|
+
},
|
|
901
|
+
cloudflare: {
|
|
902
|
+
pkg: "@astrojs/cloudflare",
|
|
903
|
+
import: "@astrojs/cloudflare"
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
function getAstroAdapterPkg(adapter) {
|
|
907
|
+
return ASTRO_ADAPTER_INFO[adapter]?.pkg ?? ASTRO_ADAPTER_INFO.vercel.pkg;
|
|
908
|
+
}
|
|
909
|
+
function astroConfigTemplate(adapter = "vercel") {
|
|
910
|
+
const info = ASTRO_ADAPTER_INFO[adapter] ?? ASTRO_ADAPTER_INFO.vercel;
|
|
911
|
+
const adapterCall = adapter === "node" ? `${adapter}({ mode: "standalone" })` : `${adapter}()`;
|
|
912
|
+
return `\
|
|
913
|
+
import { defineConfig } from "astro/config";
|
|
914
|
+
import ${adapter} from "${info.import}";
|
|
915
|
+
|
|
916
|
+
export default defineConfig({
|
|
917
|
+
output: "server",
|
|
918
|
+
adapter: ${adapterCall},
|
|
919
|
+
});
|
|
920
|
+
`;
|
|
921
|
+
}
|
|
922
|
+
function astroDocsPageTemplate(cfg) {
|
|
923
|
+
return `\
|
|
924
|
+
---
|
|
925
|
+
import DocsLayout from "@farming-labs/astro-theme/src/components/DocsLayout.astro";
|
|
926
|
+
import DocsContent from "@farming-labs/astro-theme/src/components/DocsContent.astro";
|
|
927
|
+
import SearchDialog from "@farming-labs/astro-theme/src/components/SearchDialog.astro";
|
|
928
|
+
import config from "${astroPageConfigImport(cfg.useAlias, 2)}";
|
|
929
|
+
import { load } from "${astroPageServerImport(cfg.useAlias, 2)}";
|
|
930
|
+
import "${`@farming-labs/astro-theme/${getThemeInfo(cfg.theme).astroCssTheme}/css`}";
|
|
931
|
+
|
|
932
|
+
const data = await load(Astro.url.pathname);
|
|
933
|
+
---
|
|
934
|
+
|
|
935
|
+
<html lang="en">
|
|
936
|
+
<head>
|
|
937
|
+
<meta charset="utf-8" />
|
|
938
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
939
|
+
<title>{data.title} – Docs</title>
|
|
940
|
+
</head>
|
|
941
|
+
<body>
|
|
942
|
+
<DocsLayout tree={data.tree} config={config}>
|
|
943
|
+
<DocsContent data={data} config={config} />
|
|
944
|
+
</DocsLayout>
|
|
945
|
+
<SearchDialog config={config} />
|
|
946
|
+
</body>
|
|
947
|
+
</html>
|
|
948
|
+
`;
|
|
949
|
+
}
|
|
950
|
+
function astroDocsIndexTemplate(cfg) {
|
|
951
|
+
return `\
|
|
952
|
+
---
|
|
953
|
+
import DocsLayout from "@farming-labs/astro-theme/src/components/DocsLayout.astro";
|
|
954
|
+
import DocsContent from "@farming-labs/astro-theme/src/components/DocsContent.astro";
|
|
955
|
+
import SearchDialog from "@farming-labs/astro-theme/src/components/SearchDialog.astro";
|
|
956
|
+
import config from "${astroPageConfigImport(cfg.useAlias, 2)}";
|
|
957
|
+
import { load } from "${astroPageServerImport(cfg.useAlias, 2)}";
|
|
958
|
+
import "${`@farming-labs/astro-theme/${getThemeInfo(cfg.theme).astroCssTheme}/css`}";
|
|
959
|
+
|
|
960
|
+
const data = await load(Astro.url.pathname);
|
|
961
|
+
---
|
|
962
|
+
|
|
963
|
+
<html lang="en">
|
|
964
|
+
<head>
|
|
965
|
+
<meta charset="utf-8" />
|
|
966
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
967
|
+
<title>{data.title} – Docs</title>
|
|
968
|
+
</head>
|
|
969
|
+
<body>
|
|
970
|
+
<DocsLayout tree={data.tree} config={config}>
|
|
971
|
+
<DocsContent data={data} config={config} />
|
|
972
|
+
</DocsLayout>
|
|
973
|
+
<SearchDialog config={config} />
|
|
974
|
+
</body>
|
|
975
|
+
</html>
|
|
976
|
+
`;
|
|
977
|
+
}
|
|
978
|
+
function astroApiRouteTemplate(cfg) {
|
|
979
|
+
return `\
|
|
980
|
+
import type { APIRoute } from "astro";
|
|
981
|
+
import { GET as docsGET, POST as docsPOST } from "${astroPageServerImport(cfg.useAlias, 2)}";
|
|
982
|
+
|
|
983
|
+
export const GET: APIRoute = async ({ request }) => {
|
|
984
|
+
return docsGET({ request });
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
988
|
+
return docsPOST({ request });
|
|
989
|
+
};
|
|
990
|
+
`;
|
|
991
|
+
}
|
|
992
|
+
function astroGlobalCssTemplate(theme) {
|
|
993
|
+
return `\
|
|
994
|
+
@import "@farming-labs/astro-theme/${theme}/css";
|
|
995
|
+
`;
|
|
996
|
+
}
|
|
997
|
+
function astroCssImportLine(theme) {
|
|
998
|
+
return `@import "@farming-labs/astro-theme/${theme}/css";`;
|
|
999
|
+
}
|
|
1000
|
+
function injectAstroCssImport(existingContent, theme) {
|
|
1001
|
+
const importLine = astroCssImportLine(theme);
|
|
1002
|
+
if (existingContent.includes(importLine)) return null;
|
|
1003
|
+
const lines = existingContent.split("\n");
|
|
1004
|
+
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
1005
|
+
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
1006
|
+
else lines.unshift(importLine);
|
|
1007
|
+
return lines.join("\n");
|
|
1008
|
+
}
|
|
1009
|
+
function astroWelcomePageTemplate(cfg) {
|
|
1010
|
+
return `\
|
|
1011
|
+
---
|
|
1012
|
+
title: "Documentation"
|
|
1013
|
+
description: "Welcome to ${cfg.projectName} documentation"
|
|
1014
|
+
---
|
|
1015
|
+
|
|
1016
|
+
# Welcome to ${cfg.projectName}
|
|
1017
|
+
|
|
1018
|
+
Get started with our documentation. Browse the pages on the left to learn more.
|
|
1019
|
+
|
|
1020
|
+
## Overview
|
|
1021
|
+
|
|
1022
|
+
This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
|
|
1023
|
+
|
|
1024
|
+
## Features
|
|
1025
|
+
|
|
1026
|
+
- **Markdown Support** — Write docs with standard Markdown
|
|
1027
|
+
- **Syntax Highlighting** — Code blocks with automatic highlighting
|
|
1028
|
+
- **Dark Mode** — Built-in theme switching
|
|
1029
|
+
- **Search** — Full-text search across all pages
|
|
1030
|
+
- **Responsive** — Works on any screen size
|
|
1031
|
+
|
|
1032
|
+
---
|
|
1033
|
+
|
|
1034
|
+
## Next Steps
|
|
1035
|
+
|
|
1036
|
+
Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
|
|
1037
|
+
`;
|
|
1038
|
+
}
|
|
1039
|
+
function astroInstallationPageTemplate(cfg) {
|
|
1040
|
+
const t = getThemeInfo(cfg.theme);
|
|
1041
|
+
return `\
|
|
1042
|
+
---
|
|
1043
|
+
title: "Installation"
|
|
1044
|
+
description: "How to install and set up ${cfg.projectName}"
|
|
1045
|
+
---
|
|
1046
|
+
|
|
1047
|
+
# Installation
|
|
1048
|
+
|
|
1049
|
+
Follow these steps to install and configure ${cfg.projectName}.
|
|
1050
|
+
|
|
1051
|
+
## Prerequisites
|
|
1052
|
+
|
|
1053
|
+
- Node.js 18+
|
|
1054
|
+
- A package manager (pnpm, npm, or yarn)
|
|
1055
|
+
|
|
1056
|
+
## Install Dependencies
|
|
1057
|
+
|
|
1058
|
+
\\\`\\\`\\\`bash
|
|
1059
|
+
pnpm add @farming-labs/docs @farming-labs/astro @farming-labs/astro-theme
|
|
1060
|
+
\\\`\\\`\\\`
|
|
1061
|
+
|
|
1062
|
+
## Configuration
|
|
1063
|
+
|
|
1064
|
+
Your project includes a \\\`docs.config.ts\\\` in \\\`src/lib/\\\`:
|
|
1065
|
+
|
|
1066
|
+
\\\`\\\`\\\`ts title="src/lib/docs.config.ts"
|
|
1067
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
1068
|
+
import { ${t.factory} } from "${t.astroImport}";
|
|
1069
|
+
|
|
1070
|
+
export default defineDocs({
|
|
1071
|
+
entry: "${cfg.entry}",
|
|
1072
|
+
contentDir: "${cfg.entry}",
|
|
1073
|
+
theme: ${t.factory}({
|
|
1074
|
+
ui: { colors: { primary: "#6366f1" } },
|
|
1075
|
+
}),
|
|
1076
|
+
});
|
|
1077
|
+
\\\`\\\`\\\`
|
|
1078
|
+
|
|
1079
|
+
## Project Structure
|
|
1080
|
+
|
|
1081
|
+
\\\`\\\`\\\`
|
|
1082
|
+
${cfg.entry}/ # Markdown content
|
|
1083
|
+
page.md # /${cfg.entry}
|
|
1084
|
+
installation/
|
|
1085
|
+
page.md # /${cfg.entry}/installation
|
|
1086
|
+
quickstart/
|
|
1087
|
+
page.md # /${cfg.entry}/quickstart
|
|
1088
|
+
src/
|
|
1089
|
+
lib/
|
|
1090
|
+
docs.config.ts # Docs configuration
|
|
1091
|
+
docs.server.ts # Server-side docs loader
|
|
1092
|
+
pages/
|
|
1093
|
+
${cfg.entry}/
|
|
1094
|
+
index.astro # Docs index page
|
|
1095
|
+
[...slug].astro # Dynamic doc page
|
|
1096
|
+
api/
|
|
1097
|
+
${cfg.entry}.ts # Search/AI API route
|
|
1098
|
+
\\\`\\\`\\\`
|
|
1099
|
+
|
|
1100
|
+
## What's Next?
|
|
1101
|
+
|
|
1102
|
+
Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
|
|
1103
|
+
`;
|
|
1104
|
+
}
|
|
1105
|
+
function astroQuickstartPageTemplate(cfg) {
|
|
1106
|
+
const t = getThemeInfo(cfg.theme);
|
|
1107
|
+
return `\
|
|
1108
|
+
---
|
|
1109
|
+
title: "Quickstart"
|
|
1110
|
+
description: "Get up and running in minutes"
|
|
1111
|
+
---
|
|
1112
|
+
|
|
1113
|
+
# Quickstart
|
|
1114
|
+
|
|
1115
|
+
This guide walks you through creating your first documentation page.
|
|
1116
|
+
|
|
1117
|
+
## Creating a Page
|
|
1118
|
+
|
|
1119
|
+
Create a new folder under \\\`${cfg.entry}/\\\` with a \\\`page.md\\\` file:
|
|
1120
|
+
|
|
1121
|
+
\\\`\\\`\\\`bash
|
|
1122
|
+
mkdir -p ${cfg.entry}/my-page
|
|
1123
|
+
\\\`\\\`\\\`
|
|
1124
|
+
|
|
1125
|
+
Then create \\\`${cfg.entry}/my-page/page.md\\\`:
|
|
1126
|
+
|
|
1127
|
+
\\\`\\\`\\\`md
|
|
1128
|
+
---
|
|
1129
|
+
title: "My Page"
|
|
1130
|
+
description: "A custom documentation page"
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
# My Page
|
|
1134
|
+
|
|
1135
|
+
Write your content here using **Markdown**.
|
|
1136
|
+
\\\`\\\`\\\`
|
|
1137
|
+
|
|
1138
|
+
Your page is now available at \\\`/${cfg.entry}/my-page\\\`.
|
|
1139
|
+
|
|
1140
|
+
## Customizing the Theme
|
|
1141
|
+
|
|
1142
|
+
Edit \\\`src/lib/docs.config.ts\\\` to change colors, typography, and component defaults:
|
|
1143
|
+
|
|
1144
|
+
\\\`\\\`\\\`ts title="src/lib/docs.config.ts"
|
|
1145
|
+
theme: ${t.factory}({
|
|
1146
|
+
ui: {
|
|
1147
|
+
colors: { primary: "#22c55e" },
|
|
1148
|
+
},
|
|
1149
|
+
}),
|
|
1150
|
+
\\\`\\\`\\\`
|
|
1151
|
+
|
|
1152
|
+
## Deploying
|
|
1153
|
+
|
|
1154
|
+
Build your docs for production:
|
|
1155
|
+
|
|
1156
|
+
\\\`\\\`\\\`bash
|
|
1157
|
+
pnpm build
|
|
1158
|
+
\\\`\\\`\\\`
|
|
1159
|
+
|
|
1160
|
+
Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
1161
|
+
`;
|
|
1162
|
+
}
|
|
1163
|
+
function nuxtDocsConfigTemplate(cfg) {
|
|
1164
|
+
const t = getThemeInfo(cfg.theme);
|
|
1165
|
+
return `\
|
|
1166
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
1167
|
+
import { ${t.factory} } from "${t.nuxtImport}";
|
|
1168
|
+
|
|
1169
|
+
export default defineDocs({
|
|
1170
|
+
entry: "${cfg.entry}",
|
|
1171
|
+
contentDir: "${cfg.entry}",
|
|
1172
|
+
theme: ${t.factory}({
|
|
1173
|
+
ui: {
|
|
1174
|
+
colors: { primary: "#6366f1" },
|
|
1175
|
+
},
|
|
1176
|
+
}),
|
|
1177
|
+
|
|
1178
|
+
nav: {
|
|
1179
|
+
title: "${cfg.projectName}",
|
|
1180
|
+
url: "/${cfg.entry}",
|
|
1181
|
+
},
|
|
1182
|
+
|
|
1183
|
+
breadcrumb: { enabled: true },
|
|
1184
|
+
|
|
1185
|
+
metadata: {
|
|
1186
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
1187
|
+
description: "Documentation for ${cfg.projectName}",
|
|
1188
|
+
},
|
|
1189
|
+
});
|
|
1190
|
+
`;
|
|
1191
|
+
}
|
|
1192
|
+
function nuxtDocsServerTemplate(cfg) {
|
|
1193
|
+
const contentDirName = cfg.entry ?? "docs";
|
|
1194
|
+
return `\
|
|
1195
|
+
import { createDocsServer } from "@farming-labs/nuxt/server";
|
|
1196
|
+
import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
|
|
1197
|
+
|
|
1198
|
+
const contentFiles = import.meta.glob("/${contentDirName}/**/*.{md,mdx}", {
|
|
1199
|
+
query: "?raw",
|
|
1200
|
+
import: "default",
|
|
1201
|
+
eager: true,
|
|
1202
|
+
}) as Record<string, string>;
|
|
1203
|
+
|
|
1204
|
+
export const docsServer = createDocsServer({
|
|
1205
|
+
...config,
|
|
1206
|
+
_preloadedContent: contentFiles,
|
|
1207
|
+
});
|
|
1208
|
+
`;
|
|
1209
|
+
}
|
|
1210
|
+
function nuxtServerApiDocsGetTemplate() {
|
|
1211
|
+
return `\
|
|
1212
|
+
import { getRequestURL } from "h3";
|
|
1213
|
+
import { docsServer } from "../utils/docs-server";
|
|
1214
|
+
|
|
1215
|
+
export default defineEventHandler((event) => {
|
|
1216
|
+
const url = getRequestURL(event);
|
|
1217
|
+
const request = new Request(url.href, {
|
|
1218
|
+
method: event.method,
|
|
1219
|
+
headers: event.headers,
|
|
1220
|
+
});
|
|
1221
|
+
return docsServer.GET({ request });
|
|
1222
|
+
});
|
|
1223
|
+
`;
|
|
1224
|
+
}
|
|
1225
|
+
function nuxtServerApiDocsPostTemplate() {
|
|
1226
|
+
return `\
|
|
1227
|
+
import { getRequestURL, readRawBody } from "h3";
|
|
1228
|
+
import { docsServer } from "../utils/docs-server";
|
|
1229
|
+
|
|
1230
|
+
export default defineEventHandler(async (event) => {
|
|
1231
|
+
const url = getRequestURL(event);
|
|
1232
|
+
const body = await readRawBody(event);
|
|
1233
|
+
const request = new Request(url.href, {
|
|
1234
|
+
method: "POST",
|
|
1235
|
+
headers: event.headers,
|
|
1236
|
+
body: body ?? undefined,
|
|
1237
|
+
});
|
|
1238
|
+
return docsServer.POST({ request });
|
|
1239
|
+
});
|
|
1240
|
+
`;
|
|
1241
|
+
}
|
|
1242
|
+
function nuxtServerApiDocsLoadTemplate() {
|
|
1243
|
+
return `\
|
|
1244
|
+
import { getQuery } from "h3";
|
|
1245
|
+
import { docsServer } from "../../utils/docs-server";
|
|
1246
|
+
|
|
1247
|
+
export default defineEventHandler(async (event) => {
|
|
1248
|
+
const query = getQuery(event);
|
|
1249
|
+
const pathname = (query.pathname as string) ?? "/docs";
|
|
1250
|
+
return docsServer.load(pathname);
|
|
1251
|
+
});
|
|
1252
|
+
`;
|
|
1253
|
+
}
|
|
1254
|
+
function nuxtDocsPageTemplate(cfg) {
|
|
1255
|
+
return `\
|
|
1256
|
+
<script setup lang="ts">
|
|
1257
|
+
import { DocsLayout, DocsContent } from "@farming-labs/nuxt-theme";
|
|
1258
|
+
import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
|
|
1259
|
+
|
|
1260
|
+
const route = useRoute();
|
|
1261
|
+
const pathname = computed(() => route.path);
|
|
1262
|
+
|
|
1263
|
+
const { data, error } = await useAsyncData(\`docs-\${pathname.value}\`, () =>
|
|
1264
|
+
$fetch("/api/docs/load", {
|
|
1265
|
+
query: { pathname: pathname.value },
|
|
1266
|
+
})
|
|
1267
|
+
);
|
|
1268
|
+
|
|
1269
|
+
if (error.value) {
|
|
1270
|
+
throw createError({
|
|
1271
|
+
statusCode: 404,
|
|
1272
|
+
statusMessage: "Page not found",
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
<\/script>
|
|
1276
|
+
|
|
1277
|
+
<template>
|
|
1278
|
+
<div v-if="data" class="fd-docs-wrapper">
|
|
1279
|
+
<DocsLayout :tree="data.tree" :config="config">
|
|
1280
|
+
<DocsContent :data="data" :config="config" />
|
|
1281
|
+
</DocsLayout>
|
|
1282
|
+
</div>
|
|
1283
|
+
</template>
|
|
1284
|
+
`;
|
|
1285
|
+
}
|
|
1286
|
+
function nuxtConfigTemplate(cfg) {
|
|
1287
|
+
return `\
|
|
1288
|
+
export default defineNuxtConfig({
|
|
1289
|
+
compatibilityDate: "2024-11-01",
|
|
1290
|
+
|
|
1291
|
+
css: ["@farming-labs/nuxt-theme/${getThemeInfo(cfg.theme).nuxtCssTheme}/css"],
|
|
1292
|
+
|
|
1293
|
+
vite: {
|
|
1294
|
+
optimizeDeps: {
|
|
1295
|
+
include: ["@farming-labs/docs", "@farming-labs/nuxt", "@farming-labs/nuxt-theme"],
|
|
1296
|
+
},
|
|
1297
|
+
},
|
|
1298
|
+
|
|
1299
|
+
nitro: {
|
|
1300
|
+
moduleSideEffects: ["@farming-labs/nuxt/server"],
|
|
1301
|
+
},
|
|
1302
|
+
});
|
|
1303
|
+
`;
|
|
1304
|
+
}
|
|
1305
|
+
function nuxtWelcomePageTemplate(cfg) {
|
|
1306
|
+
return `\
|
|
1307
|
+
---
|
|
1308
|
+
order: 1
|
|
1309
|
+
title: Documentation
|
|
1310
|
+
description: Welcome to ${cfg.projectName} documentation
|
|
1311
|
+
icon: book
|
|
1312
|
+
---
|
|
1313
|
+
|
|
1314
|
+
# Welcome to ${cfg.projectName}
|
|
1315
|
+
|
|
1316
|
+
Get started with our documentation. Browse the pages on the left to learn more.
|
|
1317
|
+
|
|
1318
|
+
## Overview
|
|
1319
|
+
|
|
1320
|
+
This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
|
|
1321
|
+
|
|
1322
|
+
## Features
|
|
1323
|
+
|
|
1324
|
+
- **Markdown Support** — Write docs with standard Markdown
|
|
1325
|
+
- **Syntax Highlighting** — Code blocks with automatic highlighting
|
|
1326
|
+
- **Dark Mode** — Built-in theme switching
|
|
1327
|
+
- **Search** — Full-text search across all pages (⌘K)
|
|
1328
|
+
- **Responsive** — Works on any screen size
|
|
1329
|
+
|
|
1330
|
+
---
|
|
1331
|
+
|
|
1332
|
+
## Next Steps
|
|
1333
|
+
|
|
1334
|
+
Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
|
|
1335
|
+
`;
|
|
1336
|
+
}
|
|
1337
|
+
function nuxtInstallationPageTemplate(cfg) {
|
|
1338
|
+
const t = getThemeInfo(cfg.theme);
|
|
1339
|
+
return `\
|
|
1340
|
+
---
|
|
1341
|
+
order: 3
|
|
1342
|
+
title: Installation
|
|
1343
|
+
description: How to install and set up ${cfg.projectName}
|
|
1344
|
+
icon: terminal
|
|
1345
|
+
---
|
|
1346
|
+
|
|
1347
|
+
# Installation
|
|
1348
|
+
|
|
1349
|
+
Follow these steps to install and configure ${cfg.projectName}.
|
|
1350
|
+
|
|
1351
|
+
## Prerequisites
|
|
1352
|
+
|
|
1353
|
+
- Node.js 18+
|
|
1354
|
+
- A package manager (pnpm, npm, or yarn)
|
|
1355
|
+
|
|
1356
|
+
## Install Dependencies
|
|
1357
|
+
|
|
1358
|
+
\`\`\`bash
|
|
1359
|
+
pnpm add @farming-labs/docs @farming-labs/nuxt @farming-labs/nuxt-theme
|
|
1360
|
+
\`\`\`
|
|
1361
|
+
|
|
1362
|
+
## Configuration
|
|
1363
|
+
|
|
1364
|
+
Your project includes a \`docs.config.ts\` at the root:
|
|
1365
|
+
|
|
1366
|
+
\`\`\`ts title="docs.config.ts"
|
|
1367
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
1368
|
+
import { ${t.factory} } from "${t.nuxtImport}";
|
|
1369
|
+
|
|
1370
|
+
export default defineDocs({
|
|
1371
|
+
entry: "${cfg.entry}",
|
|
1372
|
+
contentDir: "${cfg.entry}",
|
|
1373
|
+
theme: ${t.factory}({
|
|
1374
|
+
ui: { colors: { primary: "#6366f1" } },
|
|
1375
|
+
}),
|
|
1376
|
+
});
|
|
1377
|
+
\`\`\`
|
|
1378
|
+
|
|
1379
|
+
## Project Structure
|
|
1380
|
+
|
|
1381
|
+
\`\`\`
|
|
1382
|
+
${cfg.entry}/ # Markdown content
|
|
1383
|
+
page.md
|
|
1384
|
+
installation/page.md
|
|
1385
|
+
quickstart/page.md
|
|
1386
|
+
server/
|
|
1387
|
+
utils/docs-server.ts # createDocsServer + preloaded content
|
|
1388
|
+
api/docs/
|
|
1389
|
+
load.get.ts # Page data API
|
|
1390
|
+
docs.get.ts # Search API
|
|
1391
|
+
docs.post.ts # AI chat API
|
|
1392
|
+
pages/
|
|
1393
|
+
${cfg.entry}/[[...slug]].vue # Docs catch-all page
|
|
1394
|
+
docs.config.ts
|
|
1395
|
+
nuxt.config.ts
|
|
1396
|
+
\`\`\`
|
|
1397
|
+
|
|
1398
|
+
## What's Next?
|
|
1399
|
+
|
|
1400
|
+
Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
|
|
1401
|
+
`;
|
|
1402
|
+
}
|
|
1403
|
+
function nuxtQuickstartPageTemplate(cfg) {
|
|
1404
|
+
const t = getThemeInfo(cfg.theme);
|
|
1405
|
+
return `\
|
|
1406
|
+
---
|
|
1407
|
+
order: 2
|
|
1408
|
+
title: Quickstart
|
|
1409
|
+
description: Get up and running in minutes
|
|
1410
|
+
icon: rocket
|
|
1411
|
+
---
|
|
1412
|
+
|
|
1413
|
+
# Quickstart
|
|
1414
|
+
|
|
1415
|
+
This guide walks you through creating your first documentation page.
|
|
1416
|
+
|
|
1417
|
+
## Creating a Page
|
|
1418
|
+
|
|
1419
|
+
Create a new folder under \`${cfg.entry}/\` with a \`page.md\` file:
|
|
1420
|
+
|
|
1421
|
+
\`\`\`bash
|
|
1422
|
+
mkdir -p ${cfg.entry}/my-page
|
|
1423
|
+
\`\`\`
|
|
1424
|
+
|
|
1425
|
+
Then create \`${cfg.entry}/my-page/page.md\`:
|
|
1426
|
+
|
|
1427
|
+
\`\`\`md
|
|
1428
|
+
---
|
|
1429
|
+
title: "My Page"
|
|
1430
|
+
description: "A custom documentation page"
|
|
1431
|
+
---
|
|
1432
|
+
|
|
1433
|
+
# My Page
|
|
1434
|
+
|
|
1435
|
+
Write your content here using **Markdown**.
|
|
1436
|
+
\`\`\`
|
|
1437
|
+
|
|
1438
|
+
Your page is now available at \`/${cfg.entry}/my-page\`.
|
|
1439
|
+
|
|
1440
|
+
## Customizing the Theme
|
|
1441
|
+
|
|
1442
|
+
Edit \`docs.config.ts\` to change colors and typography:
|
|
1443
|
+
|
|
1444
|
+
\`\`\`ts
|
|
1445
|
+
theme: ${t.factory}({
|
|
1446
|
+
ui: {
|
|
1447
|
+
colors: { primary: "#22c55e" },
|
|
1448
|
+
},
|
|
1449
|
+
}),
|
|
1450
|
+
\`\`\`
|
|
1451
|
+
|
|
1452
|
+
## Deploying
|
|
1453
|
+
|
|
1454
|
+
Build your docs for production:
|
|
1455
|
+
|
|
1456
|
+
\`\`\`bash
|
|
1457
|
+
pnpm build
|
|
1458
|
+
\`\`\`
|
|
1459
|
+
|
|
1460
|
+
Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
1461
|
+
`;
|
|
1462
|
+
}
|
|
1463
|
+
function nuxtGlobalCssTemplate(theme) {
|
|
1464
|
+
return `\
|
|
1465
|
+
@import "@farming-labs/nuxt-theme/${theme}/css";
|
|
1466
|
+
`;
|
|
1467
|
+
}
|
|
1468
|
+
function nuxtCssImportLine(theme) {
|
|
1469
|
+
return `@import "@farming-labs/nuxt-theme/${theme}/css";`;
|
|
1470
|
+
}
|
|
1471
|
+
function injectNuxtCssImport(existingContent, theme) {
|
|
1472
|
+
const importLine = nuxtCssImportLine(theme);
|
|
1473
|
+
if (existingContent.includes(importLine)) return null;
|
|
1474
|
+
const lines = existingContent.split("\n");
|
|
1475
|
+
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
1476
|
+
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
1477
|
+
else lines.unshift(importLine);
|
|
1478
|
+
return lines.join("\n");
|
|
1479
|
+
}
|
|
761
1480
|
|
|
762
1481
|
//#endregion
|
|
763
1482
|
//#region src/cli/init.ts
|
|
764
|
-
|
|
1483
|
+
const EXAMPLES_REPO = "farming-labs/docs";
|
|
1484
|
+
const VALID_TEMPLATES = [
|
|
1485
|
+
"next",
|
|
1486
|
+
"nuxt",
|
|
1487
|
+
"sveltekit",
|
|
1488
|
+
"astro"
|
|
1489
|
+
];
|
|
1490
|
+
async function init(options = {}) {
|
|
765
1491
|
const cwd = process.cwd();
|
|
766
1492
|
p.intro(pc.bgCyan(pc.black(" @farming-labs/docs ")));
|
|
1493
|
+
if (options.template) {
|
|
1494
|
+
const template = options.template.toLowerCase();
|
|
1495
|
+
if (!VALID_TEMPLATES.includes(template)) {
|
|
1496
|
+
p.log.error(`Invalid ${pc.cyan("--template")}. Use one of: ${VALID_TEMPLATES.map((t) => pc.cyan(t)).join(", ")}`);
|
|
1497
|
+
process.exit(1);
|
|
1498
|
+
}
|
|
1499
|
+
let projectName = options.name?.trim();
|
|
1500
|
+
if (!projectName) {
|
|
1501
|
+
const nameAnswer = await p.text({
|
|
1502
|
+
message: "Project name? (we'll create this folder and bootstrap the app here)",
|
|
1503
|
+
placeholder: "my-docs",
|
|
1504
|
+
defaultValue: "my-docs",
|
|
1505
|
+
validate: (value) => {
|
|
1506
|
+
const v = (value ?? "").trim();
|
|
1507
|
+
if (!v) return "Project name is required";
|
|
1508
|
+
if (v.includes("/") || v.includes("\\")) return "Project name cannot contain path separators";
|
|
1509
|
+
if (v.includes(" ")) return "Project name cannot contain spaces";
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
if (p.isCancel(nameAnswer)) {
|
|
1513
|
+
p.outro(pc.red("Init cancelled."));
|
|
1514
|
+
process.exit(0);
|
|
1515
|
+
}
|
|
1516
|
+
projectName = nameAnswer.trim();
|
|
1517
|
+
}
|
|
1518
|
+
const templateLabel = template === "next" ? "Next.js" : template === "nuxt" ? "Nuxt" : template === "sveltekit" ? "SvelteKit" : "Astro";
|
|
1519
|
+
const targetDir = path.join(cwd, projectName);
|
|
1520
|
+
const fs = await import("node:fs");
|
|
1521
|
+
if (fs.existsSync(targetDir)) {
|
|
1522
|
+
p.log.error(`Directory ${pc.cyan(projectName)} already exists. Choose a different ${pc.cyan("--name")} or remove it.`);
|
|
1523
|
+
process.exit(1);
|
|
1524
|
+
}
|
|
1525
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
1526
|
+
p.log.step(`Bootstrapping project with ${pc.cyan(`'${projectName}'`)} (${templateLabel})...`);
|
|
1527
|
+
try {
|
|
1528
|
+
exec(`npx degit ${EXAMPLES_REPO}/examples/${template} . --force`, targetDir);
|
|
1529
|
+
} catch (err) {
|
|
1530
|
+
p.log.error("Failed to bootstrap. Check your connection and that the repo exists.");
|
|
1531
|
+
process.exit(1);
|
|
1532
|
+
}
|
|
1533
|
+
p.log.success(`Bootstrapped ${pc.cyan(`'${projectName}'`)}. Installing dependencies...`);
|
|
1534
|
+
const pm = detectPackageManager(targetDir);
|
|
1535
|
+
try {
|
|
1536
|
+
if (pm === "pnpm") exec("pnpm install", targetDir);
|
|
1537
|
+
else if (pm === "yarn") exec("yarn install", targetDir);
|
|
1538
|
+
else if (pm === "bun") exec("bun install", targetDir);
|
|
1539
|
+
else exec("npm install", targetDir);
|
|
1540
|
+
} catch {
|
|
1541
|
+
p.log.warn("Dependency install failed. Run your package manager install command manually.");
|
|
1542
|
+
}
|
|
1543
|
+
const devCmd = pm === "yarn" ? "yarn dev" : pm === "bun" ? "bun dev" : `${pm} run dev`;
|
|
1544
|
+
p.outro(pc.green(`Done! Run ${pc.cyan(`cd ${projectName} && ${devCmd}`)} to start the dev server.`));
|
|
1545
|
+
process.exit(0);
|
|
1546
|
+
}
|
|
767
1547
|
let framework = detectFramework(cwd);
|
|
768
1548
|
if (framework) {
|
|
769
|
-
const frameworkName = framework === "nextjs" ? "Next.js" : "SvelteKit";
|
|
1549
|
+
const frameworkName = framework === "nextjs" ? "Next.js" : framework === "sveltekit" ? "SvelteKit" : framework === "astro" ? "Astro" : "Nuxt";
|
|
770
1550
|
p.log.success(`Detected framework: ${pc.cyan(frameworkName)}`);
|
|
771
1551
|
} else {
|
|
772
1552
|
p.log.warn("Could not auto-detect a framework from " + pc.cyan("package.json") + ".");
|
|
773
1553
|
const picked = await p.select({
|
|
774
1554
|
message: "Which framework are you using?",
|
|
775
|
-
options: [
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1555
|
+
options: [
|
|
1556
|
+
{
|
|
1557
|
+
value: "nextjs",
|
|
1558
|
+
label: "Next.js",
|
|
1559
|
+
hint: "React framework with App Router"
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
value: "sveltekit",
|
|
1563
|
+
label: "SvelteKit",
|
|
1564
|
+
hint: "Svelte framework with file-based routing"
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
value: "astro",
|
|
1568
|
+
label: "Astro",
|
|
1569
|
+
hint: "Content-focused framework with island architecture"
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
value: "nuxt",
|
|
1573
|
+
label: "Nuxt",
|
|
1574
|
+
hint: "Vue 3 framework with file-based routing and Nitro server"
|
|
1575
|
+
}
|
|
1576
|
+
]
|
|
784
1577
|
});
|
|
785
1578
|
if (p.isCancel(picked)) {
|
|
786
1579
|
p.outro(pc.red("Init cancelled."));
|
|
@@ -805,6 +1598,26 @@ async function init() {
|
|
|
805
1598
|
value: "pixel-border",
|
|
806
1599
|
label: "Pixel Border",
|
|
807
1600
|
hint: "Rounded borders, pixel-perfect spacing, refined sidebar"
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
value: "colorful",
|
|
1604
|
+
label: "Colorful",
|
|
1605
|
+
hint: "Fumadocs-style neutral theme with description support"
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
value: "darkbold",
|
|
1609
|
+
label: "DarkBold",
|
|
1610
|
+
hint: "Pure monochrome, Geist typography, clean minimalism"
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
value: "shiny",
|
|
1614
|
+
label: "Shiny",
|
|
1615
|
+
hint: "Glossy, modern look with subtle shimmer effects"
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
value: "greentree",
|
|
1619
|
+
label: "GreenTree",
|
|
1620
|
+
hint: "Emerald green accent, Inter font, Mintlify-inspired"
|
|
808
1621
|
}
|
|
809
1622
|
]
|
|
810
1623
|
});
|
|
@@ -812,7 +1625,7 @@ async function init() {
|
|
|
812
1625
|
p.outro(pc.red("Init cancelled."));
|
|
813
1626
|
process.exit(0);
|
|
814
1627
|
}
|
|
815
|
-
const aliasHint = framework === "nextjs" ? `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)` : `Uses ${pc.cyan("$lib/")} prefix (SvelteKit built-in)`;
|
|
1628
|
+
const aliasHint = framework === "nextjs" ? `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)` : framework === "sveltekit" ? `Uses ${pc.cyan("$lib/")} prefix (SvelteKit built-in)` : framework === "nuxt" ? `Uses ${pc.cyan("~/")} prefix (Nuxt built-in)` : `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)`;
|
|
816
1629
|
const useAlias = await p.confirm({
|
|
817
1630
|
message: `Use path aliases for imports? ${pc.dim(aliasHint)}`,
|
|
818
1631
|
initialValue: false
|
|
@@ -821,6 +1634,37 @@ async function init() {
|
|
|
821
1634
|
p.outro(pc.red("Init cancelled."));
|
|
822
1635
|
process.exit(0);
|
|
823
1636
|
}
|
|
1637
|
+
let astroAdapter;
|
|
1638
|
+
if (framework === "astro") {
|
|
1639
|
+
const adapter = await p.select({
|
|
1640
|
+
message: "Where will you deploy?",
|
|
1641
|
+
options: [
|
|
1642
|
+
{
|
|
1643
|
+
value: "vercel",
|
|
1644
|
+
label: "Vercel",
|
|
1645
|
+
hint: "Recommended for most projects"
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
value: "netlify",
|
|
1649
|
+
label: "Netlify"
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
value: "cloudflare",
|
|
1653
|
+
label: "Cloudflare Pages"
|
|
1654
|
+
},
|
|
1655
|
+
{
|
|
1656
|
+
value: "node",
|
|
1657
|
+
label: "Node.js / Docker",
|
|
1658
|
+
hint: "Self-hosted standalone server"
|
|
1659
|
+
}
|
|
1660
|
+
]
|
|
1661
|
+
});
|
|
1662
|
+
if (p.isCancel(adapter)) {
|
|
1663
|
+
p.outro(pc.red("Init cancelled."));
|
|
1664
|
+
process.exit(0);
|
|
1665
|
+
}
|
|
1666
|
+
astroAdapter = adapter;
|
|
1667
|
+
}
|
|
824
1668
|
const entry = await p.text({
|
|
825
1669
|
message: "Where should your docs live?",
|
|
826
1670
|
placeholder: "docs",
|
|
@@ -838,7 +1682,7 @@ async function init() {
|
|
|
838
1682
|
const entryPath = entry;
|
|
839
1683
|
const detectedCssFiles = detectGlobalCssFiles(cwd);
|
|
840
1684
|
let globalCssRelPath;
|
|
841
|
-
const defaultCssPath = framework === "sveltekit" ? "src/app.css" : "app/globals.css";
|
|
1685
|
+
const defaultCssPath = framework === "sveltekit" ? "src/app.css" : framework === "astro" ? "src/styles/global.css" : framework === "nuxt" ? "assets/css/main.css" : "app/globals.css";
|
|
842
1686
|
if (detectedCssFiles.length === 1) {
|
|
843
1687
|
globalCssRelPath = detectedCssFiles[0];
|
|
844
1688
|
p.log.info(`Found global CSS at ${pc.cyan(globalCssRelPath)}`);
|
|
@@ -878,7 +1722,8 @@ async function init() {
|
|
|
878
1722
|
theme,
|
|
879
1723
|
projectName: pkgJson.name || "My Project",
|
|
880
1724
|
framework,
|
|
881
|
-
useAlias
|
|
1725
|
+
useAlias,
|
|
1726
|
+
astroAdapter
|
|
882
1727
|
};
|
|
883
1728
|
const s = p.spinner();
|
|
884
1729
|
s.start("Scaffolding docs files");
|
|
@@ -889,6 +1734,8 @@ async function init() {
|
|
|
889
1734
|
else skipped.push(rel);
|
|
890
1735
|
}
|
|
891
1736
|
if (framework === "sveltekit") scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written);
|
|
1737
|
+
else if (framework === "astro") scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written);
|
|
1738
|
+
else if (framework === "nuxt") scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written);
|
|
892
1739
|
else scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written);
|
|
893
1740
|
s.stop("Files scaffolded");
|
|
894
1741
|
if (written.length > 0) p.log.success(`Created ${written.length} file${written.length > 1 ? "s" : ""}:\n` + written.map((f) => ` ${pc.green("+")} ${f}`).join("\n"));
|
|
@@ -899,6 +1746,10 @@ async function init() {
|
|
|
899
1746
|
s2.start("Installing dependencies");
|
|
900
1747
|
try {
|
|
901
1748
|
if (framework === "sveltekit") exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/svelte @farming-labs/svelte-theme`, cwd);
|
|
1749
|
+
else if (framework === "astro") {
|
|
1750
|
+
const adapterPkg = getAstroAdapterPkg(cfg.astroAdapter ?? "vercel");
|
|
1751
|
+
exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/astro @farming-labs/astro-theme ${adapterPkg}`, cwd);
|
|
1752
|
+
} else if (framework === "nuxt") exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/nuxt @farming-labs/nuxt-theme`, cwd);
|
|
902
1753
|
else {
|
|
903
1754
|
exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/next @farming-labs/theme`, cwd);
|
|
904
1755
|
const devDeps = [
|
|
@@ -938,6 +1789,14 @@ async function init() {
|
|
|
938
1789
|
cmd: "npx",
|
|
939
1790
|
args: ["vite", "dev"],
|
|
940
1791
|
waitFor: "ready"
|
|
1792
|
+
} : framework === "astro" ? {
|
|
1793
|
+
cmd: "npx",
|
|
1794
|
+
args: ["astro", "dev"],
|
|
1795
|
+
waitFor: "ready"
|
|
1796
|
+
} : framework === "nuxt" ? {
|
|
1797
|
+
cmd: "npx",
|
|
1798
|
+
args: ["nuxt", "dev"],
|
|
1799
|
+
waitFor: "Local"
|
|
941
1800
|
} : {
|
|
942
1801
|
cmd: "npx",
|
|
943
1802
|
args: [
|
|
@@ -947,7 +1806,7 @@ async function init() {
|
|
|
947
1806
|
],
|
|
948
1807
|
waitFor: "Ready"
|
|
949
1808
|
};
|
|
950
|
-
const defaultPort = framework === "sveltekit" ? "5173" : "3000";
|
|
1809
|
+
const defaultPort = framework === "sveltekit" ? "5173" : framework === "astro" ? "4321" : framework === "nuxt" ? "3000" : "3000";
|
|
951
1810
|
try {
|
|
952
1811
|
const child = await spawnAndWaitFor(devCommand.cmd, devCommand.args, cwd, devCommand.waitFor, 6e4);
|
|
953
1812
|
const url = `http://localhost:${defaultPort}/${entryPath}`;
|
|
@@ -966,7 +1825,7 @@ async function init() {
|
|
|
966
1825
|
});
|
|
967
1826
|
});
|
|
968
1827
|
} catch (err) {
|
|
969
|
-
const manualCmd = framework === "sveltekit" ? "npx vite dev" : "npx next dev --webpack";
|
|
1828
|
+
const manualCmd = framework === "sveltekit" ? "npx vite dev" : framework === "astro" ? "npx astro dev" : framework === "nuxt" ? "npx nuxt dev" : "npx next dev --webpack";
|
|
970
1829
|
p.log.error(`Could not start dev server. Try running manually:
|
|
971
1830
|
${pc.cyan(manualCmd)}`);
|
|
972
1831
|
p.outro(pc.yellow("Setup complete. Start the server manually."));
|
|
@@ -996,7 +1855,7 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
996
1855
|
} else write(globalCssRelPath, globalCssTemplate(cfg.theme));
|
|
997
1856
|
write(`app/${cfg.entry}/layout.tsx`, docsLayoutTemplate(cfg));
|
|
998
1857
|
write("postcss.config.mjs", postcssConfigTemplate());
|
|
999
|
-
if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate());
|
|
1858
|
+
if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate(cfg.useAlias));
|
|
1000
1859
|
write(`app/${cfg.entry}/page.mdx`, welcomePageTemplate(cfg));
|
|
1001
1860
|
write(`app/${cfg.entry}/installation/page.mdx`, installationPageTemplate(cfg));
|
|
1002
1861
|
write(`app/${cfg.entry}/quickstart/page.mdx`, quickstartPageTemplate(cfg));
|
|
@@ -1014,6 +1873,10 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
|
|
|
1014
1873
|
fumadocs: "fumadocs",
|
|
1015
1874
|
darksharp: "darksharp",
|
|
1016
1875
|
"pixel-border": "pixel-border",
|
|
1876
|
+
colorful: "colorful",
|
|
1877
|
+
darkbold: "darkbold",
|
|
1878
|
+
shiny: "shiny",
|
|
1879
|
+
greentree: "greentree",
|
|
1017
1880
|
default: "fumadocs"
|
|
1018
1881
|
}[cfg.theme] || "fumadocs";
|
|
1019
1882
|
if (existingGlobalCss) {
|
|
@@ -1027,12 +1890,97 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
|
|
|
1027
1890
|
write(`${cfg.entry}/installation/page.md`, svelteInstallationPageTemplate(cfg));
|
|
1028
1891
|
write(`${cfg.entry}/quickstart/page.md`, svelteQuickstartPageTemplate(cfg));
|
|
1029
1892
|
}
|
|
1893
|
+
function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
1894
|
+
write("src/lib/docs.config.ts", astroDocsConfigTemplate(cfg));
|
|
1895
|
+
write("src/lib/docs.server.ts", astroDocsServerTemplate(cfg));
|
|
1896
|
+
if (!fileExists(path.join(cwd, "astro.config.mjs")) && !fileExists(path.join(cwd, "astro.config.ts"))) write("astro.config.mjs", astroConfigTemplate(cfg.astroAdapter ?? "vercel"));
|
|
1897
|
+
write(`src/pages/${cfg.entry}/index.astro`, astroDocsIndexTemplate(cfg));
|
|
1898
|
+
write(`src/pages/${cfg.entry}/[...slug].astro`, astroDocsPageTemplate(cfg));
|
|
1899
|
+
write(`src/pages/api/${cfg.entry}.ts`, astroApiRouteTemplate(cfg));
|
|
1900
|
+
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
1901
|
+
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
1902
|
+
const cssTheme = {
|
|
1903
|
+
fumadocs: "fumadocs",
|
|
1904
|
+
darksharp: "darksharp",
|
|
1905
|
+
"pixel-border": "pixel-border",
|
|
1906
|
+
colorful: "colorful",
|
|
1907
|
+
darkbold: "darkbold",
|
|
1908
|
+
shiny: "shiny",
|
|
1909
|
+
greentree: "greentree",
|
|
1910
|
+
default: "fumadocs"
|
|
1911
|
+
}[cfg.theme] || "fumadocs";
|
|
1912
|
+
if (existingGlobalCss) {
|
|
1913
|
+
const injected = injectAstroCssImport(existingGlobalCss, cssTheme);
|
|
1914
|
+
if (injected) {
|
|
1915
|
+
writeFileSafe(globalCssAbsPath, injected, true);
|
|
1916
|
+
written.push(globalCssRelPath + " (updated)");
|
|
1917
|
+
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
1918
|
+
} else write(globalCssRelPath, astroGlobalCssTemplate(cssTheme));
|
|
1919
|
+
write(`${cfg.entry}/page.md`, astroWelcomePageTemplate(cfg));
|
|
1920
|
+
write(`${cfg.entry}/installation/page.md`, astroInstallationPageTemplate(cfg));
|
|
1921
|
+
write(`${cfg.entry}/quickstart/page.md`, astroQuickstartPageTemplate(cfg));
|
|
1922
|
+
}
|
|
1923
|
+
function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
1924
|
+
write("docs.config.ts", nuxtDocsConfigTemplate(cfg));
|
|
1925
|
+
write("server/utils/docs-server.ts", nuxtDocsServerTemplate(cfg));
|
|
1926
|
+
write("server/api/docs.get.ts", nuxtServerApiDocsGetTemplate());
|
|
1927
|
+
write("server/api/docs.post.ts", nuxtServerApiDocsPostTemplate());
|
|
1928
|
+
write("server/api/docs/load.get.ts", nuxtServerApiDocsLoadTemplate());
|
|
1929
|
+
write(`pages/${cfg.entry}/[[...slug]].vue`, nuxtDocsPageTemplate(cfg));
|
|
1930
|
+
path.join(cwd, "nuxt.config.ts");
|
|
1931
|
+
if (!fileExists(path.join(cwd, "nuxt.config.ts")) && !fileExists(path.join(cwd, "nuxt.config.js"))) write("nuxt.config.ts", nuxtConfigTemplate(cfg));
|
|
1932
|
+
const cssTheme = {
|
|
1933
|
+
fumadocs: "fumadocs",
|
|
1934
|
+
darksharp: "darksharp",
|
|
1935
|
+
"pixel-border": "pixel-border",
|
|
1936
|
+
colorful: "colorful",
|
|
1937
|
+
darkbold: "darkbold",
|
|
1938
|
+
shiny: "shiny",
|
|
1939
|
+
greentree: "greentree",
|
|
1940
|
+
default: "fumadocs"
|
|
1941
|
+
}[cfg.theme] || "fumadocs";
|
|
1942
|
+
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
1943
|
+
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
1944
|
+
if (existingGlobalCss) {
|
|
1945
|
+
const injected = injectNuxtCssImport(existingGlobalCss, cssTheme);
|
|
1946
|
+
if (injected) {
|
|
1947
|
+
writeFileSafe(globalCssAbsPath, injected, true);
|
|
1948
|
+
written.push(globalCssRelPath + " (updated)");
|
|
1949
|
+
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
1950
|
+
} else write(globalCssRelPath, nuxtGlobalCssTemplate(cssTheme));
|
|
1951
|
+
write(`${cfg.entry}/page.md`, nuxtWelcomePageTemplate(cfg));
|
|
1952
|
+
write(`${cfg.entry}/installation/page.md`, nuxtInstallationPageTemplate(cfg));
|
|
1953
|
+
write(`${cfg.entry}/quickstart/page.md`, nuxtQuickstartPageTemplate(cfg));
|
|
1954
|
+
}
|
|
1030
1955
|
|
|
1031
1956
|
//#endregion
|
|
1032
1957
|
//#region src/cli/index.ts
|
|
1033
|
-
const
|
|
1958
|
+
const args = process.argv.slice(2);
|
|
1959
|
+
const command = args[0];
|
|
1960
|
+
/** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs */
|
|
1961
|
+
function parseFlags(argv) {
|
|
1962
|
+
const flags = {};
|
|
1963
|
+
for (let i = 0; i < argv.length; i++) {
|
|
1964
|
+
const arg = argv[i];
|
|
1965
|
+
if (arg.startsWith("--") && arg.includes("=")) {
|
|
1966
|
+
const [key, value] = arg.slice(2).split("=");
|
|
1967
|
+
flags[key] = value;
|
|
1968
|
+
} else if (arg.startsWith("--") && argv[i + 1] && !argv[i + 1].startsWith("--")) {
|
|
1969
|
+
flags[arg.slice(2)] = argv[i + 1];
|
|
1970
|
+
i++;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return flags;
|
|
1974
|
+
}
|
|
1034
1975
|
async function main() {
|
|
1035
|
-
|
|
1976
|
+
const flags = parseFlags(args);
|
|
1977
|
+
const initOptions = {
|
|
1978
|
+
template: flags.template,
|
|
1979
|
+
name: flags.name,
|
|
1980
|
+
theme: flags.theme,
|
|
1981
|
+
entry: flags.entry
|
|
1982
|
+
};
|
|
1983
|
+
if (!command || command === "init") await init(initOptions);
|
|
1036
1984
|
else if (command === "--help" || command === "-h") printHelp();
|
|
1037
1985
|
else if (command === "--version" || command === "-v") printVersion();
|
|
1038
1986
|
else {
|
|
@@ -1053,11 +2001,15 @@ ${pc.dim("Commands:")}
|
|
|
1053
2001
|
${pc.cyan("init")} Scaffold docs in your project (default)
|
|
1054
2002
|
|
|
1055
2003
|
${pc.dim("Supported frameworks:")}
|
|
1056
|
-
Next.js, SvelteKit
|
|
1057
|
-
|
|
1058
|
-
${pc.dim("Options:")}
|
|
1059
|
-
${pc.cyan("
|
|
1060
|
-
${pc.cyan("
|
|
2004
|
+
Next.js, SvelteKit, Astro, Nuxt
|
|
2005
|
+
|
|
2006
|
+
${pc.dim("Options for init:")}
|
|
2007
|
+
${pc.cyan("--template <name>")} Bootstrap a project (${pc.dim("next")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}); use with ${pc.cyan("--name")}
|
|
2008
|
+
${pc.cyan("--name <project>")} Project folder name when using ${pc.cyan("--template")}; prompt if omitted (e.g. ${pc.dim("my-docs")})
|
|
2009
|
+
${pc.cyan("--theme <name>")} Skip theme prompt (e.g. ${pc.dim("darksharp")}, ${pc.dim("greentree")})
|
|
2010
|
+
${pc.cyan("--entry <path>")} Skip entry path prompt (e.g. ${pc.dim("docs")})
|
|
2011
|
+
${pc.cyan("-h, --help")} Show this help message
|
|
2012
|
+
${pc.cyan("-v, --version")} Show version
|
|
1061
2013
|
`);
|
|
1062
2014
|
}
|
|
1063
2015
|
function printVersion() {
|