@farming-labs/docs 0.0.2-beta.9 → 0.0.3-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {
@@ -178,6 +238,17 @@ function sveltePageConfigImport(useAlias) {
178
238
  function svelteLayoutServerImport(useAlias) {
179
239
  return useAlias ? "$lib/docs.server" : "../../lib/docs.server";
180
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`;
251
+ }
181
252
  function docsConfigTemplate(cfg) {
182
253
  const t = getThemeInfo(cfg.theme);
183
254
  return `\
@@ -292,7 +363,8 @@ const config = {
292
363
  export default config;
293
364
  `;
294
365
  }
295
- function tsconfigTemplate() {
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
- export const { load, GET, POST } = createDocsServer(config);
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,736 @@ 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
- async function init() {
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 with pnpm...`);
1534
+ try {
1535
+ exec("pnpm install", targetDir);
1536
+ } catch {
1537
+ p.log.warn("pnpm install failed. Run pnpm install manually inside the project.");
1538
+ }
1539
+ p.outro(pc.green(`Done! Run ${pc.cyan(`cd ${projectName} && pnpm run dev`)} to start the dev server.`));
1540
+ process.exit(0);
1541
+ }
767
1542
  let framework = detectFramework(cwd);
768
1543
  if (framework) {
769
- const frameworkName = framework === "nextjs" ? "Next.js" : "SvelteKit";
1544
+ const frameworkName = framework === "nextjs" ? "Next.js" : framework === "sveltekit" ? "SvelteKit" : framework === "astro" ? "Astro" : "Nuxt";
770
1545
  p.log.success(`Detected framework: ${pc.cyan(frameworkName)}`);
771
1546
  } else {
772
1547
  p.log.warn("Could not auto-detect a framework from " + pc.cyan("package.json") + ".");
773
1548
  const picked = await p.select({
774
1549
  message: "Which framework are you using?",
775
- options: [{
776
- value: "nextjs",
777
- label: "Next.js",
778
- hint: "React framework with App Router"
779
- }, {
780
- value: "sveltekit",
781
- label: "SvelteKit",
782
- hint: "Svelte framework with file-based routing"
783
- }]
1550
+ options: [
1551
+ {
1552
+ value: "nextjs",
1553
+ label: "Next.js",
1554
+ hint: "React framework with App Router"
1555
+ },
1556
+ {
1557
+ value: "sveltekit",
1558
+ label: "SvelteKit",
1559
+ hint: "Svelte framework with file-based routing"
1560
+ },
1561
+ {
1562
+ value: "astro",
1563
+ label: "Astro",
1564
+ hint: "Content-focused framework with island architecture"
1565
+ },
1566
+ {
1567
+ value: "nuxt",
1568
+ label: "Nuxt",
1569
+ hint: "Vue 3 framework with file-based routing and Nitro server"
1570
+ }
1571
+ ]
784
1572
  });
785
1573
  if (p.isCancel(picked)) {
786
1574
  p.outro(pc.red("Init cancelled."));
@@ -805,6 +1593,26 @@ async function init() {
805
1593
  value: "pixel-border",
806
1594
  label: "Pixel Border",
807
1595
  hint: "Rounded borders, pixel-perfect spacing, refined sidebar"
1596
+ },
1597
+ {
1598
+ value: "colorful",
1599
+ label: "Colorful",
1600
+ hint: "Fumadocs-style neutral theme with description support"
1601
+ },
1602
+ {
1603
+ value: "darkbold",
1604
+ label: "DarkBold",
1605
+ hint: "Pure monochrome, Geist typography, clean minimalism"
1606
+ },
1607
+ {
1608
+ value: "shiny",
1609
+ label: "Shiny",
1610
+ hint: "Glossy, modern look with subtle shimmer effects"
1611
+ },
1612
+ {
1613
+ value: "greentree",
1614
+ label: "GreenTree",
1615
+ hint: "Emerald green accent, Inter font, Mintlify-inspired"
808
1616
  }
809
1617
  ]
810
1618
  });
@@ -812,7 +1620,7 @@ async function init() {
812
1620
  p.outro(pc.red("Init cancelled."));
813
1621
  process.exit(0);
814
1622
  }
815
- const aliasHint = framework === "nextjs" ? `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)` : `Uses ${pc.cyan("$lib/")} prefix (SvelteKit built-in)`;
1623
+ 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
1624
  const useAlias = await p.confirm({
817
1625
  message: `Use path aliases for imports? ${pc.dim(aliasHint)}`,
818
1626
  initialValue: false
@@ -821,6 +1629,37 @@ async function init() {
821
1629
  p.outro(pc.red("Init cancelled."));
822
1630
  process.exit(0);
823
1631
  }
1632
+ let astroAdapter;
1633
+ if (framework === "astro") {
1634
+ const adapter = await p.select({
1635
+ message: "Where will you deploy?",
1636
+ options: [
1637
+ {
1638
+ value: "vercel",
1639
+ label: "Vercel",
1640
+ hint: "Recommended for most projects"
1641
+ },
1642
+ {
1643
+ value: "netlify",
1644
+ label: "Netlify"
1645
+ },
1646
+ {
1647
+ value: "cloudflare",
1648
+ label: "Cloudflare Pages"
1649
+ },
1650
+ {
1651
+ value: "node",
1652
+ label: "Node.js / Docker",
1653
+ hint: "Self-hosted standalone server"
1654
+ }
1655
+ ]
1656
+ });
1657
+ if (p.isCancel(adapter)) {
1658
+ p.outro(pc.red("Init cancelled."));
1659
+ process.exit(0);
1660
+ }
1661
+ astroAdapter = adapter;
1662
+ }
824
1663
  const entry = await p.text({
825
1664
  message: "Where should your docs live?",
826
1665
  placeholder: "docs",
@@ -838,7 +1677,7 @@ async function init() {
838
1677
  const entryPath = entry;
839
1678
  const detectedCssFiles = detectGlobalCssFiles(cwd);
840
1679
  let globalCssRelPath;
841
- const defaultCssPath = framework === "sveltekit" ? "src/app.css" : "app/globals.css";
1680
+ const defaultCssPath = framework === "sveltekit" ? "src/app.css" : framework === "astro" ? "src/styles/global.css" : framework === "nuxt" ? "assets/css/main.css" : "app/globals.css";
842
1681
  if (detectedCssFiles.length === 1) {
843
1682
  globalCssRelPath = detectedCssFiles[0];
844
1683
  p.log.info(`Found global CSS at ${pc.cyan(globalCssRelPath)}`);
@@ -878,7 +1717,8 @@ async function init() {
878
1717
  theme,
879
1718
  projectName: pkgJson.name || "My Project",
880
1719
  framework,
881
- useAlias
1720
+ useAlias,
1721
+ astroAdapter
882
1722
  };
883
1723
  const s = p.spinner();
884
1724
  s.start("Scaffolding docs files");
@@ -889,6 +1729,8 @@ async function init() {
889
1729
  else skipped.push(rel);
890
1730
  }
891
1731
  if (framework === "sveltekit") scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written);
1732
+ else if (framework === "astro") scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written);
1733
+ else if (framework === "nuxt") scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written);
892
1734
  else scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written);
893
1735
  s.stop("Files scaffolded");
894
1736
  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 +1741,10 @@ async function init() {
899
1741
  s2.start("Installing dependencies");
900
1742
  try {
901
1743
  if (framework === "sveltekit") exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/svelte @farming-labs/svelte-theme`, cwd);
1744
+ else if (framework === "astro") {
1745
+ const adapterPkg = getAstroAdapterPkg(cfg.astroAdapter ?? "vercel");
1746
+ exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/astro @farming-labs/astro-theme ${adapterPkg}`, cwd);
1747
+ } else if (framework === "nuxt") exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/nuxt @farming-labs/nuxt-theme`, cwd);
902
1748
  else {
903
1749
  exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/next @farming-labs/theme`, cwd);
904
1750
  const devDeps = [
@@ -938,6 +1784,14 @@ async function init() {
938
1784
  cmd: "npx",
939
1785
  args: ["vite", "dev"],
940
1786
  waitFor: "ready"
1787
+ } : framework === "astro" ? {
1788
+ cmd: "npx",
1789
+ args: ["astro", "dev"],
1790
+ waitFor: "ready"
1791
+ } : framework === "nuxt" ? {
1792
+ cmd: "npx",
1793
+ args: ["nuxt", "dev"],
1794
+ waitFor: "Local"
941
1795
  } : {
942
1796
  cmd: "npx",
943
1797
  args: [
@@ -947,7 +1801,7 @@ async function init() {
947
1801
  ],
948
1802
  waitFor: "Ready"
949
1803
  };
950
- const defaultPort = framework === "sveltekit" ? "5173" : "3000";
1804
+ const defaultPort = framework === "sveltekit" ? "5173" : framework === "astro" ? "4321" : framework === "nuxt" ? "3000" : "3000";
951
1805
  try {
952
1806
  const child = await spawnAndWaitFor(devCommand.cmd, devCommand.args, cwd, devCommand.waitFor, 6e4);
953
1807
  const url = `http://localhost:${defaultPort}/${entryPath}`;
@@ -966,7 +1820,7 @@ async function init() {
966
1820
  });
967
1821
  });
968
1822
  } catch (err) {
969
- const manualCmd = framework === "sveltekit" ? "npx vite dev" : "npx next dev --webpack";
1823
+ const manualCmd = framework === "sveltekit" ? "npx vite dev" : framework === "astro" ? "npx astro dev" : framework === "nuxt" ? "npx nuxt dev" : "npx next dev --webpack";
970
1824
  p.log.error(`Could not start dev server. Try running manually:
971
1825
  ${pc.cyan(manualCmd)}`);
972
1826
  p.outro(pc.yellow("Setup complete. Start the server manually."));
@@ -996,7 +1850,7 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
996
1850
  } else write(globalCssRelPath, globalCssTemplate(cfg.theme));
997
1851
  write(`app/${cfg.entry}/layout.tsx`, docsLayoutTemplate(cfg));
998
1852
  write("postcss.config.mjs", postcssConfigTemplate());
999
- if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate());
1853
+ if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate(cfg.useAlias));
1000
1854
  write(`app/${cfg.entry}/page.mdx`, welcomePageTemplate(cfg));
1001
1855
  write(`app/${cfg.entry}/installation/page.mdx`, installationPageTemplate(cfg));
1002
1856
  write(`app/${cfg.entry}/quickstart/page.mdx`, quickstartPageTemplate(cfg));
@@ -1014,6 +1868,10 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
1014
1868
  fumadocs: "fumadocs",
1015
1869
  darksharp: "darksharp",
1016
1870
  "pixel-border": "pixel-border",
1871
+ colorful: "colorful",
1872
+ darkbold: "darkbold",
1873
+ shiny: "shiny",
1874
+ greentree: "greentree",
1017
1875
  default: "fumadocs"
1018
1876
  }[cfg.theme] || "fumadocs";
1019
1877
  if (existingGlobalCss) {
@@ -1027,12 +1885,97 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
1027
1885
  write(`${cfg.entry}/installation/page.md`, svelteInstallationPageTemplate(cfg));
1028
1886
  write(`${cfg.entry}/quickstart/page.md`, svelteQuickstartPageTemplate(cfg));
1029
1887
  }
1888
+ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
1889
+ write("src/lib/docs.config.ts", astroDocsConfigTemplate(cfg));
1890
+ write("src/lib/docs.server.ts", astroDocsServerTemplate(cfg));
1891
+ if (!fileExists(path.join(cwd, "astro.config.mjs")) && !fileExists(path.join(cwd, "astro.config.ts"))) write("astro.config.mjs", astroConfigTemplate(cfg.astroAdapter ?? "vercel"));
1892
+ write(`src/pages/${cfg.entry}/index.astro`, astroDocsIndexTemplate(cfg));
1893
+ write(`src/pages/${cfg.entry}/[...slug].astro`, astroDocsPageTemplate(cfg));
1894
+ write(`src/pages/api/${cfg.entry}.ts`, astroApiRouteTemplate(cfg));
1895
+ const globalCssAbsPath = path.join(cwd, globalCssRelPath);
1896
+ const existingGlobalCss = readFileSafe(globalCssAbsPath);
1897
+ const cssTheme = {
1898
+ fumadocs: "fumadocs",
1899
+ darksharp: "darksharp",
1900
+ "pixel-border": "pixel-border",
1901
+ colorful: "colorful",
1902
+ darkbold: "darkbold",
1903
+ shiny: "shiny",
1904
+ greentree: "greentree",
1905
+ default: "fumadocs"
1906
+ }[cfg.theme] || "fumadocs";
1907
+ if (existingGlobalCss) {
1908
+ const injected = injectAstroCssImport(existingGlobalCss, cssTheme);
1909
+ if (injected) {
1910
+ writeFileSafe(globalCssAbsPath, injected, true);
1911
+ written.push(globalCssRelPath + " (updated)");
1912
+ } else skipped.push(globalCssRelPath + " (already configured)");
1913
+ } else write(globalCssRelPath, astroGlobalCssTemplate(cssTheme));
1914
+ write(`${cfg.entry}/page.md`, astroWelcomePageTemplate(cfg));
1915
+ write(`${cfg.entry}/installation/page.md`, astroInstallationPageTemplate(cfg));
1916
+ write(`${cfg.entry}/quickstart/page.md`, astroQuickstartPageTemplate(cfg));
1917
+ }
1918
+ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
1919
+ write("docs.config.ts", nuxtDocsConfigTemplate(cfg));
1920
+ write("server/utils/docs-server.ts", nuxtDocsServerTemplate(cfg));
1921
+ write("server/api/docs.get.ts", nuxtServerApiDocsGetTemplate());
1922
+ write("server/api/docs.post.ts", nuxtServerApiDocsPostTemplate());
1923
+ write("server/api/docs/load.get.ts", nuxtServerApiDocsLoadTemplate());
1924
+ write(`pages/${cfg.entry}/[[...slug]].vue`, nuxtDocsPageTemplate(cfg));
1925
+ path.join(cwd, "nuxt.config.ts");
1926
+ if (!fileExists(path.join(cwd, "nuxt.config.ts")) && !fileExists(path.join(cwd, "nuxt.config.js"))) write("nuxt.config.ts", nuxtConfigTemplate(cfg));
1927
+ const cssTheme = {
1928
+ fumadocs: "fumadocs",
1929
+ darksharp: "darksharp",
1930
+ "pixel-border": "pixel-border",
1931
+ colorful: "colorful",
1932
+ darkbold: "darkbold",
1933
+ shiny: "shiny",
1934
+ greentree: "greentree",
1935
+ default: "fumadocs"
1936
+ }[cfg.theme] || "fumadocs";
1937
+ const globalCssAbsPath = path.join(cwd, globalCssRelPath);
1938
+ const existingGlobalCss = readFileSafe(globalCssAbsPath);
1939
+ if (existingGlobalCss) {
1940
+ const injected = injectNuxtCssImport(existingGlobalCss, cssTheme);
1941
+ if (injected) {
1942
+ writeFileSafe(globalCssAbsPath, injected, true);
1943
+ written.push(globalCssRelPath + " (updated)");
1944
+ } else skipped.push(globalCssRelPath + " (already configured)");
1945
+ } else write(globalCssRelPath, nuxtGlobalCssTemplate(cssTheme));
1946
+ write(`${cfg.entry}/page.md`, nuxtWelcomePageTemplate(cfg));
1947
+ write(`${cfg.entry}/installation/page.md`, nuxtInstallationPageTemplate(cfg));
1948
+ write(`${cfg.entry}/quickstart/page.md`, nuxtQuickstartPageTemplate(cfg));
1949
+ }
1030
1950
 
1031
1951
  //#endregion
1032
1952
  //#region src/cli/index.ts
1033
- const command = process.argv.slice(2)[0];
1953
+ const args = process.argv.slice(2);
1954
+ const command = args[0];
1955
+ /** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs */
1956
+ function parseFlags(argv) {
1957
+ const flags = {};
1958
+ for (let i = 0; i < argv.length; i++) {
1959
+ const arg = argv[i];
1960
+ if (arg.startsWith("--") && arg.includes("=")) {
1961
+ const [key, value] = arg.slice(2).split("=");
1962
+ flags[key] = value;
1963
+ } else if (arg.startsWith("--") && argv[i + 1] && !argv[i + 1].startsWith("--")) {
1964
+ flags[arg.slice(2)] = argv[i + 1];
1965
+ i++;
1966
+ }
1967
+ }
1968
+ return flags;
1969
+ }
1034
1970
  async function main() {
1035
- if (!command || command === "init") await init();
1971
+ const flags = parseFlags(args);
1972
+ const initOptions = {
1973
+ template: flags.template,
1974
+ name: flags.name,
1975
+ theme: flags.theme,
1976
+ entry: flags.entry
1977
+ };
1978
+ if (!command || command === "init") await init(initOptions);
1036
1979
  else if (command === "--help" || command === "-h") printHelp();
1037
1980
  else if (command === "--version" || command === "-v") printVersion();
1038
1981
  else {
@@ -1053,11 +1996,15 @@ ${pc.dim("Commands:")}
1053
1996
  ${pc.cyan("init")} Scaffold docs in your project (default)
1054
1997
 
1055
1998
  ${pc.dim("Supported frameworks:")}
1056
- Next.js, SvelteKit
1057
-
1058
- ${pc.dim("Options:")}
1059
- ${pc.cyan("-h, --help")} Show this help message
1060
- ${pc.cyan("-v, --version")} Show version
1999
+ Next.js, SvelteKit, Astro, Nuxt
2000
+
2001
+ ${pc.dim("Options for init:")}
2002
+ ${pc.cyan("--template <name>")} Bootstrap a project (${pc.dim("next")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}); use with ${pc.cyan("--name")}
2003
+ ${pc.cyan("--name <project>")} Project folder name when using ${pc.cyan("--template")}; prompt if omitted (e.g. ${pc.dim("my-docs")})
2004
+ ${pc.cyan("--theme <name>")} Skip theme prompt (e.g. ${pc.dim("darksharp")}, ${pc.dim("greentree")})
2005
+ ${pc.cyan("--entry <path>")} Skip entry path prompt (e.g. ${pc.dim("docs")})
2006
+ ${pc.cyan("-h, --help")} Show this help message
2007
+ ${pc.cyan("-v, --version")} Show version
1061
2008
  `);
1062
2009
  }
1063
2010
  function printVersion() {