@farming-labs/docs 0.2.62 → 0.2.64

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.
Files changed (58) hide show
  1. package/dist/agent-CQTH7NFu.mjs +624 -0
  2. package/dist/agent-DKKptIgy.mjs +4365 -0
  3. package/dist/agent-evals-B7MIxuEX.mjs +2144 -0
  4. package/dist/agent-export-CBgWgPvH.mjs +910 -0
  5. package/dist/agent-scope-C_U--OZ7.mjs +283 -0
  6. package/dist/agent-skills-bundle.d.mts +13 -0
  7. package/dist/agent-skills-bundle.mjs +12 -0
  8. package/dist/agent-skills-server-CPja6Syt.d.mts +14 -0
  9. package/dist/agent-skills-server-DraIb6FV.mjs +415 -0
  10. package/dist/agent-skills-vite.d.mts +31 -0
  11. package/dist/agent-skills-vite.mjs +70 -0
  12. package/dist/agents-XWZBub6f.mjs +221 -0
  13. package/dist/analytics-Bx44lg6d.mjs +177 -0
  14. package/dist/cli/index.d.mts +15 -0
  15. package/dist/cli/index.mjs +452 -0
  16. package/dist/client/react.d.mts +45 -0
  17. package/dist/client/react.mjs +223 -0
  18. package/dist/cloud-analytics-CSyFE6SS.mjs +132 -0
  19. package/dist/cloud-ask-ai-sbpjOR2K.mjs +382 -0
  20. package/dist/cloud-ask-ai-zpwkdwnF.d.mts +23 -0
  21. package/dist/cloud-pdNC-tyj.mjs +1615 -0
  22. package/dist/code-blocks-DnNVNK2M.mjs +871 -0
  23. package/dist/codeblocks-CFuurVIH.mjs +250 -0
  24. package/dist/config-Wcdj-D0a.mjs +369 -0
  25. package/dist/dev-Cmy6DtdF.mjs +1333 -0
  26. package/dist/docs-cloud-server.d.mts +70 -0
  27. package/dist/docs-cloud-server.mjs +310 -0
  28. package/dist/doctor-DtGYZ41i.mjs +2036 -0
  29. package/dist/downgrade-w7e6Se0L.mjs +184 -0
  30. package/dist/errors-DbOhkE1h.mjs +20 -0
  31. package/dist/golden-evaluations-Dj-9Eo3v.mjs +1785 -0
  32. package/dist/i18n-CCaFUnAN.mjs +40 -0
  33. package/dist/index.d.mts +1150 -0
  34. package/dist/index.mjs +10 -0
  35. package/dist/init-CQY0Woe3.mjs +1264 -0
  36. package/dist/mcp-B9dcsivk.mjs +156 -0
  37. package/dist/mcp.d.mts +298 -0
  38. package/dist/mcp.mjs +4430 -0
  39. package/dist/metadata-DWExHQnx.mjs +237 -0
  40. package/dist/package-version-n5AFur8a.mjs +128 -0
  41. package/dist/reading-time-CYZ5VvKU.mjs +742 -0
  42. package/dist/review-CLoHTywU.mjs +673 -0
  43. package/dist/robots-BIpC4j4P.mjs +201 -0
  44. package/dist/robots-CUTahhoY.mjs +179 -0
  45. package/dist/search-B6V6qtiI.mjs +1826 -0
  46. package/dist/search-CaSyi6H6.d.mts +279 -0
  47. package/dist/search-DSjCeOk7.mjs +104 -0
  48. package/dist/server.d.mts +343 -0
  49. package/dist/server.mjs +14 -0
  50. package/dist/sitemap-Cykpe3Tz.mjs +249 -0
  51. package/dist/sitemap-server-C_6Wes83.mjs +1137 -0
  52. package/dist/standards-discovery-C4HUqMd2.d.mts +227 -0
  53. package/dist/standards-discovery-jkykaXq1.mjs +519 -0
  54. package/dist/templates-Bq_P7ctv.mjs +2465 -0
  55. package/dist/types-lMBIdZg0.d.mts +3315 -0
  56. package/dist/upgrade-oz-GChgt.mjs +56 -0
  57. package/dist/utils-DpiIioYb.mjs +225 -0
  58. package/package.json +1 -1
@@ -0,0 +1,2465 @@
1
+ import path from "node:path";
2
+
3
+ //#region src/cli/templates.ts
4
+ const THEME_INFO = {
5
+ fumadocs: {
6
+ factory: "fumadocs",
7
+ nextImport: "@farming-labs/theme",
8
+ svelteImport: "@farming-labs/svelte-theme",
9
+ astroImport: "@farming-labs/astro-theme",
10
+ nuxtImport: "@farming-labs/nuxt-theme",
11
+ nextCssImport: "default"
12
+ },
13
+ darksharp: {
14
+ factory: "darksharp",
15
+ nextImport: "@farming-labs/theme/darksharp",
16
+ svelteImport: "@farming-labs/svelte-theme/darksharp",
17
+ astroImport: "@farming-labs/astro-theme/darksharp",
18
+ nuxtImport: "@farming-labs/nuxt-theme/darksharp",
19
+ nextCssImport: "darksharp"
20
+ },
21
+ "pixel-border": {
22
+ factory: "pixelBorder",
23
+ nextImport: "@farming-labs/theme/pixel-border",
24
+ svelteImport: "@farming-labs/svelte-theme/pixel-border",
25
+ astroImport: "@farming-labs/astro-theme/pixel-border",
26
+ nuxtImport: "@farming-labs/nuxt-theme/pixel-border",
27
+ nextCssImport: "pixel-border"
28
+ },
29
+ colorful: {
30
+ factory: "colorful",
31
+ nextImport: "@farming-labs/theme/colorful",
32
+ svelteImport: "@farming-labs/svelte-theme/colorful",
33
+ astroImport: "@farming-labs/astro-theme/colorful",
34
+ nuxtImport: "@farming-labs/nuxt-theme/colorful",
35
+ nextCssImport: "colorful"
36
+ },
37
+ darkbold: {
38
+ factory: "darkbold",
39
+ nextImport: "@farming-labs/theme/darkbold",
40
+ svelteImport: "@farming-labs/svelte-theme/darkbold",
41
+ astroImport: "@farming-labs/astro-theme/darkbold",
42
+ nuxtImport: "@farming-labs/nuxt-theme/darkbold",
43
+ nextCssImport: "darkbold"
44
+ },
45
+ shiny: {
46
+ factory: "shiny",
47
+ nextImport: "@farming-labs/theme/shiny",
48
+ svelteImport: "@farming-labs/svelte-theme/shiny",
49
+ astroImport: "@farming-labs/astro-theme/shiny",
50
+ nuxtImport: "@farming-labs/nuxt-theme/shiny",
51
+ nextCssImport: "shiny"
52
+ },
53
+ ledger: {
54
+ factory: "ledger",
55
+ nextImport: "@farming-labs/theme/ledger",
56
+ svelteImport: "@farming-labs/svelte-theme/ledger",
57
+ astroImport: "@farming-labs/astro-theme/ledger",
58
+ nuxtImport: "@farming-labs/nuxt-theme/ledger",
59
+ nextCssImport: "ledger"
60
+ },
61
+ greentree: {
62
+ factory: "greentree",
63
+ nextImport: "@farming-labs/theme/greentree",
64
+ svelteImport: "@farming-labs/svelte-theme/greentree",
65
+ astroImport: "@farming-labs/astro-theme/greentree",
66
+ nuxtImport: "@farming-labs/nuxt-theme/greentree",
67
+ nextCssImport: "greentree"
68
+ },
69
+ concrete: {
70
+ factory: "concrete",
71
+ nextImport: "@farming-labs/theme/concrete",
72
+ svelteImport: "@farming-labs/svelte-theme/concrete",
73
+ astroImport: "@farming-labs/astro-theme/concrete",
74
+ nuxtImport: "@farming-labs/nuxt-theme/concrete",
75
+ nextCssImport: "concrete"
76
+ },
77
+ "command-grid": {
78
+ factory: "commandGrid",
79
+ nextImport: "@farming-labs/theme/command-grid",
80
+ svelteImport: "@farming-labs/svelte-theme/command-grid",
81
+ astroImport: "@farming-labs/astro-theme/command-grid",
82
+ nuxtImport: "@farming-labs/nuxt-theme/command-grid",
83
+ nextCssImport: "command-grid"
84
+ },
85
+ hardline: {
86
+ factory: "hardline",
87
+ nextImport: "@farming-labs/theme/hardline",
88
+ svelteImport: "@farming-labs/svelte-theme/hardline",
89
+ astroImport: "@farming-labs/astro-theme/hardline",
90
+ nuxtImport: "@farming-labs/nuxt-theme/hardline",
91
+ nextCssImport: "hardline"
92
+ }
93
+ };
94
+ function getThemeInfo(theme) {
95
+ return THEME_INFO[theme] ?? THEME_INFO.fumadocs;
96
+ }
97
+ function getThemeCssImport(theme) {
98
+ return `@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css`;
99
+ }
100
+ function hasBuiltInThemeCssImport(content) {
101
+ return /["']@farming-labs\/(?:theme|svelte-theme|astro-theme|nuxt-theme)\/[^"'\n]*\/css["']/.test(content);
102
+ }
103
+ function toPosixPath(value) {
104
+ return value.replace(/\\/g, "/");
105
+ }
106
+ function stripScriptExtension(value) {
107
+ return value.replace(/\.(?:[cm]?[jt]sx?)$/i, "");
108
+ }
109
+ function relativeImport(fromFile, toFile) {
110
+ const fromPosix = toPosixPath(fromFile);
111
+ const toPosix = stripScriptExtension(toPosixPath(toFile));
112
+ const rel = path.posix.relative(path.posix.dirname(fromPosix), toPosix);
113
+ return rel.startsWith(".") ? rel : `./${rel}`;
114
+ }
115
+ function relativeAssetPath(fromFile, toFile) {
116
+ const fromPosix = toPosixPath(fromFile);
117
+ const toPosix = toPosixPath(toFile);
118
+ const rel = path.posix.relative(path.posix.dirname(fromPosix), toPosix);
119
+ return rel.startsWith(".") ? rel : `./${rel}`;
120
+ }
121
+ function extractImportSpecifier(importLine) {
122
+ const fromMatch = importLine.match(/\bfrom\s+["']([^"']+)["']/);
123
+ if (fromMatch) return fromMatch[1];
124
+ const bareImportMatch = importLine.match(/^\s*import\s+["']([^"']+)["']/);
125
+ if (bareImportMatch) return bareImportMatch[1];
126
+ return null;
127
+ }
128
+ function addImportLine(content, importLine) {
129
+ if (content.includes(importLine)) return content;
130
+ const specifier = extractImportSpecifier(importLine);
131
+ if (specifier) {
132
+ if (new RegExp(String.raw`\bimport(?:\s+type)?[\s\S]*?\bfrom\s+["']${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']|^\s*import\s+["']${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`, "m").test(content)) return content;
133
+ }
134
+ const lines = content.split("\n");
135
+ const lastImportIdx = lines.reduce((acc, line, index) => {
136
+ const trimmed = line.trimStart();
137
+ return trimmed.startsWith("import ") || trimmed.startsWith("import type ") ? index : acc;
138
+ }, -1);
139
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
140
+ else lines.unshift(importLine, "");
141
+ return lines.join("\n");
142
+ }
143
+ function renderI18nConfig(cfg, indent = " ") {
144
+ const i18n = cfg.i18n;
145
+ if (!i18n || i18n.locales.length === 0) return "";
146
+ return `${indent}i18n: {\n${indent} locales: [${i18n.locales.map((locale) => `"${locale}"`).join(", ")}],\n${indent} defaultLocale: "${i18n.defaultLocale}",\n${indent}},\n`;
147
+ }
148
+ function renderApiReferenceConfig(cfg, indent = " ") {
149
+ const apiReference = cfg.apiReference;
150
+ if (!apiReference) return "";
151
+ const rendererLine = cfg.framework === "nextjs" ? `${indent} renderer: "fumadocs",\n` : "";
152
+ return `${indent}apiReference: {\n${indent} enabled: true,\n${indent} path: "${apiReference.path}",\n${rendererLine}${indent} routeRoot: "${apiReference.routeRoot}",\n${indent}},\n`;
153
+ }
154
+ function toLocaleImportName(locale) {
155
+ return `LocalePage_${locale.replace(/[^a-zA-Z0-9_$]/g, "_")}`;
156
+ }
157
+ function getThemeExportName(themeName) {
158
+ const base = themeName.replace(/\.ts$/i, "").trim();
159
+ if (!base) return "customTheme";
160
+ return base.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toLowerCase());
161
+ }
162
+ function getCustomThemeCssImportPath(globalCssRelPath, themeName) {
163
+ if (globalCssRelPath.startsWith("app/")) return `../themes/${themeName}.css`;
164
+ if (globalCssRelPath.startsWith("src/")) return `../../themes/${themeName}.css`;
165
+ return `../themes/${themeName}.css`;
166
+ }
167
+ /** Content for themes/{name}.ts - createTheme with the given name */
168
+ function customThemeTsTemplate(themeName) {
169
+ return `\
170
+ import { createTheme } from "@farming-labs/docs";
171
+
172
+ export const ${getThemeExportName(themeName)} = createTheme({
173
+ name: "${themeName.replace(/\.ts$/i, "")}",
174
+ ui: {
175
+ colors: {
176
+ primary: "#e11d48",
177
+ background: "#09090b",
178
+ foreground: "#fafafa",
179
+ muted: "#71717a",
180
+ border: "#27272a",
181
+ },
182
+ radius: "0.5rem",
183
+ },
184
+ });
185
+ `;
186
+ }
187
+ function customThemeCssTemplate(themeName) {
188
+ return `\
189
+ /* Custom theme: ${themeName} - edit variables and selectors as needed */
190
+ @import "@farming-labs/theme/presets/black";
191
+
192
+ .dark {
193
+ --color-fd-primary: #e11d48;
194
+ --color-fd-background: #09090b;
195
+ --color-fd-border: #27272a;
196
+ --radius: 0.5rem;
197
+ }
198
+ `;
199
+ }
200
+ /** Config import for Next.js root layout → root docs.config */
201
+ function nextRootLayoutConfigImport(useAlias, nextAppDir = "app") {
202
+ if (useAlias) return "@/docs.config";
203
+ return nextAppDir === "src/app" ? "../../docs.config" : "../docs.config";
204
+ }
205
+ /** Config import for Next.js app/{entry}/layout.tsx → root docs.config */
206
+ function nextDocsLayoutConfigImport(useAlias, nextAppDir = "app") {
207
+ if (useAlias) return "@/docs.config";
208
+ return nextAppDir === "src/app" ? "../../../docs.config" : "../../docs.config";
209
+ }
210
+ function nextApiReferenceConfigImport(useAlias, nextAppDir = "app", filePath) {
211
+ if (useAlias) return "@/docs.config";
212
+ return relativeImport(filePath, "docs.config.ts");
213
+ }
214
+ /** Config import for SvelteKit src/lib/docs.server.ts → src/lib/docs.config */
215
+ function svelteServerConfigImport(useAlias) {
216
+ return useAlias ? "$lib/docs.config" : "./docs.config";
217
+ }
218
+ /** Config import for SvelteKit src/routes/{entry}/+layout.svelte → src/lib/docs.config */
219
+ function svelteLayoutConfigImport(useAlias) {
220
+ return useAlias ? "$lib/docs.config" : "../../lib/docs.config";
221
+ }
222
+ /** Config import for SvelteKit src/routes/{entry}/[...slug]/+page.svelte → src/lib/docs.config */
223
+ function sveltePageConfigImport(useAlias) {
224
+ return useAlias ? "$lib/docs.config" : "../../../lib/docs.config";
225
+ }
226
+ /** Server import for SvelteKit +layout.server.js → src/lib/docs.server */
227
+ function svelteLayoutServerImport(useAlias) {
228
+ return useAlias ? "$lib/docs.server" : "../../lib/docs.server";
229
+ }
230
+ function svelteRouteServerImport(filePath, useAlias) {
231
+ if (useAlias) return "$lib/docs.server";
232
+ return relativeImport(filePath, "src/lib/docs.server.ts");
233
+ }
234
+ function svelteRouteConfigImport(filePath, useAlias) {
235
+ if (useAlias) return "$lib/docs.config";
236
+ return relativeImport(filePath, "src/lib/docs.config.ts");
237
+ }
238
+ function astroServerConfigImport(useAlias) {
239
+ return useAlias ? "@/lib/docs.config" : "./docs.config";
240
+ }
241
+ function astroPageConfigImport(useAlias, depth) {
242
+ if (useAlias) return "@/lib/docs.config";
243
+ return `${"../".repeat(depth)}lib/docs.config`;
244
+ }
245
+ function astroPageServerImport(useAlias, depth) {
246
+ if (useAlias) return "@/lib/docs.server";
247
+ return `${"../".repeat(depth)}lib/docs.server`;
248
+ }
249
+ function astroRouteServerImport(filePath, useAlias) {
250
+ if (useAlias) return "@/lib/docs.server";
251
+ return relativeImport(filePath, "src/lib/docs.server.ts");
252
+ }
253
+ function astroRouteConfigImport(filePath, useAlias) {
254
+ if (useAlias) return "@/lib/docs.config";
255
+ return relativeImport(filePath, "src/lib/docs.config.ts");
256
+ }
257
+ function docsConfigTemplate(cfg) {
258
+ if (cfg.theme === "custom" && cfg.customThemeName) {
259
+ const exportName = getThemeExportName(cfg.customThemeName);
260
+ return `\
261
+ import { defineDocs } from "@farming-labs/docs";
262
+ import { ${exportName} } from "${cfg.useAlias ? "@/themes/" + cfg.customThemeName.replace(/\.ts$/i, "") : "./themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
263
+
264
+ export default defineDocs({
265
+ entry: "${cfg.entry}",
266
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
267
+ ui: {
268
+ colors: { primary: "#6366f1" },
269
+ },
270
+ }),
271
+
272
+ metadata: {
273
+ titleTemplate: "%s – ${cfg.projectName}",
274
+ description: "Documentation for ${cfg.projectName}",
275
+ },
276
+ });
277
+ `;
278
+ }
279
+ const t = getThemeInfo(cfg.theme);
280
+ return `\
281
+ import { defineDocs } from "@farming-labs/docs";
282
+ import { ${t.factory} } from "${t.nextImport}";
283
+
284
+ export default defineDocs({
285
+ entry: "${cfg.entry}",
286
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
287
+ ui: {
288
+ colors: { primary: "#6366f1" },
289
+ },
290
+ }),
291
+
292
+ metadata: {
293
+ titleTemplate: "%s – ${cfg.projectName}",
294
+ description: "Documentation for ${cfg.projectName}",
295
+ },
296
+ });
297
+ `;
298
+ }
299
+ function nextConfigTemplate() {
300
+ return `\
301
+ import { withDocs } from "@farming-labs/next/config";
302
+
303
+ export default withDocs();
304
+ `;
305
+ }
306
+ function nextConfigMergedTemplate(existingContent) {
307
+ if (existingContent.includes("withDocs")) return existingContent;
308
+ const lines = existingContent.split("\n");
309
+ const importLine = "import { withDocs } from \"@farming-labs/next/config\";";
310
+ const exportIdx = lines.findIndex((l) => l.match(/export\s+default/));
311
+ if (exportIdx === -1) return `${importLine}\n\n${existingContent}\n\nexport default withDocs();\n`;
312
+ const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("import ") ? i : acc, -1);
313
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
314
+ else lines.unshift(importLine, "");
315
+ const adjustedExportIdx = exportIdx + (lastImportIdx >= 0 ? exportIdx > lastImportIdx ? 1 : 0 : 2);
316
+ const simpleMatch = lines[adjustedExportIdx].match(/^(\s*export\s+default\s+)(.*?)(;?\s*)$/);
317
+ if (simpleMatch) {
318
+ const [, prefix, value, suffix] = simpleMatch;
319
+ lines[adjustedExportIdx] = `${prefix}withDocs(${value})${suffix}`;
320
+ }
321
+ return lines.join("\n");
322
+ }
323
+ function rootLayoutTemplate(cfg, globalCssRelPath = "app/globals.css") {
324
+ let cssImport;
325
+ if (globalCssRelPath.startsWith("app/")) cssImport = "./" + globalCssRelPath.slice(4);
326
+ else if (globalCssRelPath.startsWith("src/app/")) cssImport = "./" + globalCssRelPath.slice(8);
327
+ else cssImport = "../" + globalCssRelPath;
328
+ const appDir = cfg.nextAppDir ?? "app";
329
+ return `\
330
+ import type { Metadata } from "next";
331
+ import { RootProvider } from "@farming-labs/theme";
332
+ import docsConfig from "${nextRootLayoutConfigImport(cfg.useAlias, appDir)}";
333
+ ${cfg.apiReference ? "import \"@farming-labs/next/api-reference.css\";\n" : ""}import "${cssImport}";
334
+
335
+ export const metadata: Metadata = {
336
+ title: {
337
+ default: "Docs",
338
+ template: docsConfig.metadata?.titleTemplate ?? "%s",
339
+ },
340
+ description: docsConfig.metadata?.description,
341
+ };
342
+
343
+ export default function RootLayout({
344
+ children,
345
+ }: {
346
+ children: React.ReactNode;
347
+ }) {
348
+ return (
349
+ <html lang="en" suppressHydrationWarning>
350
+ <body>
351
+ <RootProvider>{children}</RootProvider>
352
+ </body>
353
+ </html>
354
+ );
355
+ }
356
+ `;
357
+ }
358
+ /**
359
+ * Injects RootProvider (import + wrapper) into an existing root layout without overwriting.
360
+ * Returns the modified content, or null if RootProvider is already present or injection isn't possible.
361
+ */
362
+ function injectRootProviderIntoLayout(content) {
363
+ if (!content || content.includes("RootProvider")) return null;
364
+ let out = content;
365
+ const themeImport = "import { RootProvider } from \"@farming-labs/theme\";";
366
+ if (!out.includes("@farming-labs/theme")) {
367
+ const lines = out.split("\n");
368
+ let lastImportIdx = -1;
369
+ for (let i = 0; i < lines.length; i++) {
370
+ const trimmed = lines[i].trimStart();
371
+ if (trimmed.startsWith("import ") || trimmed.startsWith("import type ")) lastImportIdx = i;
372
+ }
373
+ if (lastImportIdx >= 0) {
374
+ lines.splice(lastImportIdx + 1, 0, themeImport);
375
+ out = lines.join("\n");
376
+ } else out = themeImport + "\n" + out;
377
+ }
378
+ if (!out.includes("<RootProvider>")) {
379
+ const childrenPattern = /\{children\}/;
380
+ if (childrenPattern.test(out)) out = out.replace(childrenPattern, "<RootProvider>{children}</RootProvider>");
381
+ }
382
+ return out === content ? null : out;
383
+ }
384
+ function globalCssTemplate(theme, customThemeName, globalCssRelPath) {
385
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
386
+ @import "tailwindcss";
387
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
388
+ `;
389
+ return `\
390
+ @import "tailwindcss";
391
+ @import "${getThemeCssImport(theme)}";
392
+ `;
393
+ }
394
+ function injectCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
395
+ const importLine = theme === "custom" && customThemeName && globalCssRelPath ? `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";` : `@import "${getThemeCssImport(theme)}";`;
396
+ if (existingContent.includes(importLine)) return null;
397
+ if (theme !== "custom" && hasBuiltInThemeCssImport(existingContent)) return null;
398
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
399
+ const lines = existingContent.split("\n");
400
+ const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
401
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
402
+ else lines.unshift(importLine);
403
+ return lines.join("\n");
404
+ }
405
+ function docsLayoutTemplate(cfg) {
406
+ const appDir = cfg.nextAppDir ?? "app";
407
+ return `\
408
+ import docsConfig from "${nextDocsLayoutConfigImport(cfg.useAlias, appDir)}";
409
+ import { createNextDocsLayout, createNextDocsMetadata } from "@farming-labs/next/layout";
410
+
411
+ export const metadata = createNextDocsMetadata(docsConfig);
412
+
413
+ const DocsLayout = createNextDocsLayout(docsConfig);
414
+
415
+ export default function Layout({ children }: { children: React.ReactNode }) {
416
+ return (
417
+ <>
418
+ <DocsLayout>{children}</DocsLayout>
419
+ </>
420
+ );
421
+ }
422
+ `;
423
+ }
424
+ function nextApiReferencePageTemplate(cfg, filePath) {
425
+ const appDir = cfg.nextAppDir ?? "app";
426
+ return `
427
+ import docsConfig from "${nextApiReferenceConfigImport(cfg.useAlias, appDir, filePath)}";
428
+ import { createNextApiReferencePage } from "@farming-labs/next/api-reference";
429
+
430
+ const ApiReferencePage = createNextApiReferencePage(docsConfig);
431
+
432
+ export const dynamic = "force-dynamic";
433
+ export const revalidate = 0;
434
+
435
+ export default ApiReferencePage;
436
+ `;
437
+ }
438
+ function nextLocaleDocPageTemplate(defaultLocale) {
439
+ return `\
440
+ import type { ComponentType } from "react";
441
+
442
+ type SearchParams = Promise<{ lang?: string | string[] | undefined }> | undefined;
443
+
444
+ function normalizeLang(value: string | string[] | undefined) {
445
+ return Array.isArray(value) ? value[0] : value;
446
+ }
447
+
448
+ export async function resolveLocaleDocPage<T extends ComponentType>(
449
+ searchParams: SearchParams,
450
+ pages: Record<string, T>,
451
+ fallbackLocale = "${defaultLocale}",
452
+ ) {
453
+ const params = (await searchParams) ?? {};
454
+ const locale = normalizeLang(params.lang) ?? fallbackLocale;
455
+
456
+ return pages[locale] ?? pages[fallbackLocale];
457
+ }
458
+ `;
459
+ }
460
+ function nextLocalizedPageTemplate(options) {
461
+ const importLines = options.pageImports.map(({ locale, importPath }) => `import ${toLocaleImportName(locale)} from "${importPath}";`).join("\n");
462
+ const pageMap = options.pageImports.map(({ locale }) => ` ${JSON.stringify(locale)}: ${toLocaleImportName(locale)},`).join("\n");
463
+ return `\
464
+ ${importLines}
465
+ import { resolveLocaleDocPage } from "${options.helperImport}";
466
+
467
+ type PageProps = {
468
+ searchParams?: Promise<{ lang?: string | string[] | undefined }>;
469
+ };
470
+
471
+ export default async function ${options.componentName}({ searchParams }: PageProps) {
472
+ const Page = await resolveLocaleDocPage(searchParams, {
473
+ ${pageMap}
474
+ }, "${options.defaultLocale}");
475
+
476
+ return <Page />;
477
+ }
478
+ `;
479
+ }
480
+ function postcssConfigTemplate() {
481
+ return `\
482
+ const config = {
483
+ plugins: {
484
+ "@tailwindcss/postcss": {},
485
+ },
486
+ };
487
+
488
+ export default config;
489
+ `;
490
+ }
491
+ /** @param useAlias - When false, paths (e.g. @/*) are omitted so no alias is added. */
492
+ function tsconfigTemplate(useAlias = false) {
493
+ return `\
494
+ {
495
+ "compilerOptions": {
496
+ "target": "ES2017",
497
+ "lib": ["dom", "dom.iterable", "esnext"],
498
+ "allowJs": true,
499
+ "skipLibCheck": true,
500
+ "strict": true,
501
+ "noEmit": true,
502
+ "esModuleInterop": true,
503
+ "module": "esnext",
504
+ "moduleResolution": "bundler",
505
+ "resolveJsonModule": true,
506
+ "isolatedModules": true,
507
+ "jsx": "react-jsx",
508
+ "incremental": true,
509
+ "plugins": [{ "name": "next" }]${useAlias ? ",\n \"paths\": { \"@/*\": [\"./*\"] }" : ""}
510
+ },
511
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
512
+ "exclude": ["node_modules"]
513
+ }
514
+ `;
515
+ }
516
+ function welcomePageTemplate(cfg) {
517
+ const appDir = cfg.nextAppDir ?? "app";
518
+ return `\
519
+ ---
520
+ title: "Documentation"
521
+ description: "Welcome to ${cfg.projectName} documentation"
522
+ ---
523
+
524
+ # Welcome to ${cfg.projectName}
525
+
526
+ Get started with our documentation. Browse the pages on the left to learn more.
527
+
528
+ <Callout type="info">
529
+ This documentation was generated by \`@farming-labs/docs\`. Edit the MDX files in \`${appDir}/${cfg.entry}/\` to customize.
530
+ </Callout>
531
+
532
+ ## Overview
533
+
534
+ This is your documentation home page. From here you can navigate to:
535
+
536
+ - [Installation](/${cfg.entry}/installation) — How to install and set up the project
537
+ - [Quickstart](/${cfg.entry}/quickstart) — Get up and running in minutes
538
+
539
+ ## Features
540
+
541
+ - **MDX Support** — Write docs with Markdown and React components
542
+ - **Syntax Highlighting** — Code blocks with automatic highlighting
543
+ - **Dark Mode** — Built-in theme switching
544
+ - **Search** — Full-text search across all pages
545
+ - **Responsive** — Works on any screen size
546
+
547
+ ---
548
+
549
+ ## Next Steps
550
+
551
+ Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
552
+ `;
553
+ }
554
+ function installationPageTemplate(cfg) {
555
+ const t = getThemeInfo(cfg.theme);
556
+ return `\
557
+ ---
558
+ title: "Installation"
559
+ description: "How to install and set up ${cfg.projectName}"
560
+ ---
561
+
562
+ # Installation
563
+
564
+ Follow these steps to install and configure ${cfg.projectName}.
565
+
566
+ <Callout type="info">
567
+ Prerequisites: Node.js 18+ and a package manager (pnpm, npm, or yarn).
568
+ </Callout>
569
+
570
+ ## Install Dependencies
571
+
572
+ \`\`\`bash
573
+ pnpm add @farming-labs/docs @farming-labs/next @farming-labs/theme
574
+ \`\`\`
575
+
576
+ ## Configuration
577
+
578
+ Your project includes a \`docs.config.ts\` at the root:
579
+
580
+ \`\`\`ts
581
+ import { defineDocs } from "@farming-labs/docs";
582
+ import { ${t.factory} } from "${t.nextImport}";
583
+
584
+ export default defineDocs({
585
+ entry: "${cfg.entry}",
586
+ theme: ${t.factory}({
587
+ ui: { colors: { primary: "#6366f1" } },
588
+ }),
589
+ });
590
+ \`\`\`
591
+
592
+ ## Project Structure
593
+
594
+ \`\`\`
595
+ ${cfg.nextAppDir ?? "app"}/
596
+ ${cfg.entry}/
597
+ layout.tsx # Docs layout
598
+ page.mdx # /${cfg.entry}
599
+ installation/
600
+ page.mdx # /${cfg.entry}/installation
601
+ quickstart/
602
+ page.mdx # /${cfg.entry}/quickstart
603
+ docs.config.ts # Docs configuration
604
+ next.config.ts # Next.js config with withDocs()
605
+ \`\`\`
606
+
607
+ ## What's Next?
608
+
609
+ Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
610
+ `;
611
+ }
612
+ function quickstartPageTemplate(cfg) {
613
+ const t = getThemeInfo(cfg.theme);
614
+ return `\
615
+ ---
616
+ title: "Quickstart"
617
+ description: "Get up and running in minutes"
618
+ ---
619
+
620
+ # Quickstart
621
+
622
+ This guide walks you through creating your first documentation page.
623
+
624
+ ## Creating a Page
625
+
626
+ Create a new folder under \`${cfg.nextAppDir ?? "app"}/${cfg.entry}/\` with a \`page.mdx\` file:
627
+
628
+ \`\`\`bash
629
+ mkdir -p ${cfg.nextAppDir ?? "app"}/${cfg.entry}/my-page
630
+ \`\`\`
631
+
632
+ Then create \`${cfg.nextAppDir ?? "app"}/${cfg.entry}/my-page/page.mdx\`:
633
+
634
+ \`\`\`mdx
635
+ ---
636
+ title: "My Page"
637
+ description: "A custom documentation page"
638
+ ---
639
+
640
+ # My Page
641
+
642
+ Write your content here using **Markdown** and JSX components.
643
+ \`\`\`
644
+
645
+ Your page is now available at \`/${cfg.entry}/my-page\`.
646
+
647
+ ## Using Components
648
+
649
+ ### Callouts
650
+
651
+ <Callout type="info">
652
+ This is an informational callout. Use it for tips and notes.
653
+ </Callout>
654
+
655
+ <Callout type="warn">
656
+ This is a warning callout. Use it for important caveats.
657
+ </Callout>
658
+
659
+ ### Code Blocks
660
+
661
+ Code blocks are automatically syntax-highlighted:
662
+
663
+ \`\`\`typescript
664
+ function greet(name: string): string {
665
+ return \\\`Hello, \\\${name}!\\\`;
666
+ }
667
+
668
+ console.log(greet("World"));
669
+ \`\`\`
670
+
671
+ ## Customizing the Theme
672
+
673
+ Edit \`docs.config.ts\` to change colors, typography, and component defaults:
674
+
675
+ \`\`\`ts
676
+ theme: ${t.factory}({
677
+ ui: {
678
+ colors: { primary: "#22c55e" },
679
+ },
680
+ }),
681
+ \`\`\`
682
+
683
+ ## Deploying
684
+
685
+ Build your docs for production:
686
+
687
+ \`\`\`bash
688
+ pnpm build
689
+ \`\`\`
690
+
691
+ Deploy to Vercel, Netlify, or any Node.js hosting platform.
692
+ `;
693
+ }
694
+ function tanstackDocsConfigTemplate(cfg) {
695
+ if (cfg.theme === "custom" && cfg.customThemeName) {
696
+ const exportName = getThemeExportName(cfg.customThemeName);
697
+ return `\
698
+ import { defineDocs } from "@farming-labs/docs";
699
+ import { ${exportName} } from "./themes/${cfg.customThemeName.replace(/\.ts$/i, "")}";
700
+
701
+ export default defineDocs({
702
+ entry: "${cfg.entry}",
703
+ contentDir: "${cfg.entry}",
704
+ ${renderApiReferenceConfig(cfg)} theme: ${exportName}({
705
+ ui: {
706
+ colors: { primary: "#6366f1" },
707
+ },
708
+ }),
709
+ nav: {
710
+ title: "${cfg.projectName} Docs",
711
+ url: "/${cfg.entry}",
712
+ },
713
+ themeToggle: {
714
+ enabled: true,
715
+ default: "dark",
716
+ },
717
+ metadata: {
718
+ titleTemplate: "%s – ${cfg.projectName}",
719
+ description: "Documentation for ${cfg.projectName}",
720
+ },
721
+ });
722
+ `;
723
+ }
724
+ const t = getThemeInfo(cfg.theme);
725
+ return `\
726
+ import { defineDocs } from "@farming-labs/docs";
727
+ import { ${t.factory} } from "${t.nextImport}";
728
+
729
+ export default defineDocs({
730
+ entry: "${cfg.entry}",
731
+ contentDir: "${cfg.entry}",
732
+ ${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
733
+ ui: {
734
+ colors: { primary: "#6366f1" },
735
+ },
736
+ }),
737
+ nav: {
738
+ title: "${cfg.projectName} Docs",
739
+ url: "/${cfg.entry}",
740
+ },
741
+ themeToggle: {
742
+ enabled: true,
743
+ default: "dark",
744
+ },
745
+ metadata: {
746
+ titleTemplate: "%s – ${cfg.projectName}",
747
+ description: "Documentation for ${cfg.projectName}",
748
+ },
749
+ });
750
+ `;
751
+ }
752
+ function tanstackDocsServerTemplate() {
753
+ return `\
754
+ import { createDocsServer } from "@farming-labs/tanstack-start/server";
755
+ import { bundledAgentSkills } from "@farming-labs/docs/agent-skills-bundle";
756
+ import docsConfig from "../../docs.config";
757
+
758
+ export const docsServer = createDocsServer({
759
+ ...docsConfig,
760
+ rootDir: process.cwd(),
761
+ _preloadedAgentSkills: bundledAgentSkills,
762
+ });
763
+ `;
764
+ }
765
+ function tanstackDocsFunctionsTemplate() {
766
+ return `\
767
+ import { createServerFn } from "@tanstack/react-start";
768
+ import { docsServer } from "./docs.server";
769
+
770
+ export const loadDocPage = createServerFn({ method: "GET" })
771
+ .inputValidator((data: { pathname: string; locale?: string }) => data)
772
+ .handler(async ({ data }) => docsServer.load(data));
773
+ `;
774
+ }
775
+ function tanstackDocsFunctionsImport(opts) {
776
+ if (opts.useAlias) return "@/lib/docs.functions";
777
+ return relativeImport(opts.filePath, "src/lib/docs.functions.ts");
778
+ }
779
+ function tanstackDocsConfigImport(filePath) {
780
+ return relativeImport(filePath, "docs.config.ts");
781
+ }
782
+ function tanstackDocsIndexRouteTemplate(opts) {
783
+ const entryUrl = `/${opts.entry.replace(/^\/+|\/+$/g, "")}`;
784
+ return `\
785
+ import { createFileRoute } from "@tanstack/react-router";
786
+ import { toDocsMarkdownUrl } from "@farming-labs/docs";
787
+ import { TanstackDocsPage } from "@farming-labs/tanstack-start/react";
788
+ import { loadDocPage } from "${tanstackDocsFunctionsImport(opts)}";
789
+ import docsConfig from "${tanstackDocsConfigImport(opts.filePath)}";
790
+
791
+ export const Route = createFileRoute("${entryUrl}/")({
792
+ loader: () => loadDocPage({ data: { pathname: "${entryUrl}" } }),
793
+ head: ({ loaderData }) => ({
794
+ links: loaderData && !docsConfig.staticExport
795
+ ? [{ rel: "alternate", type: "text/markdown", href: toDocsMarkdownUrl(loaderData.url, { locale: loaderData.locale }) }]
796
+ : [],
797
+ meta: [
798
+ { title: loaderData ? \`\${loaderData.title} – ${opts.projectName}\` : "${opts.projectName}" },
799
+ ...(loaderData?.description
800
+ ? [{ name: "description", content: loaderData.description }]
801
+ : []),
802
+ ],
803
+ }),
804
+ component: DocsIndexPage,
805
+ });
806
+
807
+ function DocsIndexPage() {
808
+ const data = Route.useLoaderData();
809
+ return <TanstackDocsPage config={docsConfig} data={data} />;
810
+ }
811
+ `;
812
+ }
813
+ function tanstackDocsCatchAllRouteTemplate(opts) {
814
+ const entryUrl = `/${opts.entry.replace(/^\/+|\/+$/g, "")}`;
815
+ const serverImport = opts.useAlias ? "@/lib/docs.server" : relativeImport(opts.filePath, "src/lib/docs.server.ts");
816
+ return `\
817
+ import { createFileRoute, notFound } from "@tanstack/react-router";
818
+ import { isDocsPublicGetRequest, toDocsMarkdownUrl } from "@farming-labs/docs";
819
+ import { TanstackDocsPage } from "@farming-labs/tanstack-start/react";
820
+ import { loadDocPage } from "${tanstackDocsFunctionsImport(opts)}";
821
+ import { docsServer } from "${serverImport}";
822
+ import docsConfig from "${tanstackDocsConfigImport(opts.filePath)}";
823
+
824
+ export const Route = createFileRoute("${entryUrl}/$")({
825
+ server: {
826
+ handlers: {
827
+ GET: async ({ request }) => {
828
+ const url = new URL(request.url);
829
+ if (isDocsPublicGetRequest(${JSON.stringify(opts.entry)}, url, request, { sitemap: docsConfig.sitemap, llms: docsConfig.llmsTxt, robots: docsConfig.robots })) {
830
+ return docsServer.GET({ request });
831
+ }
832
+ return undefined;
833
+ },
834
+ },
835
+ },
836
+ loader: async ({ location }) => {
837
+ try {
838
+ return await loadDocPage({ data: { pathname: location.pathname } });
839
+ } catch (error) {
840
+ if (
841
+ error &&
842
+ typeof error === "object" &&
843
+ "status" in error &&
844
+ (error as { status?: unknown }).status === 404
845
+ ) {
846
+ throw notFound();
847
+ }
848
+ throw error;
849
+ }
850
+ },
851
+ head: ({ loaderData }) => ({
852
+ links: loaderData && !docsConfig.staticExport
853
+ ? [{ rel: "alternate", type: "text/markdown", href: toDocsMarkdownUrl(loaderData.url, { locale: loaderData.locale }) }]
854
+ : [],
855
+ meta: [
856
+ { title: loaderData ? \`\${loaderData.title} – ${opts.projectName}\` : "${opts.projectName}" },
857
+ ...(loaderData?.description
858
+ ? [{ name: "description", content: loaderData.description }]
859
+ : []),
860
+ ],
861
+ }),
862
+ component: DocsCatchAllPage,
863
+ });
864
+
865
+ function DocsCatchAllPage() {
866
+ const data = Route.useLoaderData();
867
+ return <TanstackDocsPage config={docsConfig} data={data} />;
868
+ }
869
+ `;
870
+ }
871
+ function tanstackApiDocsRouteTemplate(useAlias, filePath) {
872
+ return `\
873
+ import { createFileRoute } from "@tanstack/react-router";
874
+ import { docsServer } from "${useAlias ? "@/lib/docs.server" : relativeImport(filePath, "src/lib/docs.server.ts")}";
875
+
876
+ export const Route = createFileRoute("/api/docs")({
877
+ server: {
878
+ handlers: {
879
+ GET: async ({ request }) => docsServer.GET({ request }),
880
+ POST: async ({ request }) => docsServer.POST({ request }),
881
+ },
882
+ },
883
+ });
884
+ `;
885
+ }
886
+ function tanstackDocsPublicRouteTemplate(useAlias, filePath, entry) {
887
+ return `\
888
+ import { createFileRoute } from "@tanstack/react-router";
889
+ import { isDocsMcpRequest, isDocsPublicGetRequest } from "@farming-labs/docs";
890
+ import { docsServer } from "${useAlias ? "@/lib/docs.server" : relativeImport(filePath, "src/lib/docs.server.ts")}";
891
+ import docsConfig from "${tanstackDocsConfigImport(filePath)}";
892
+
893
+ const docsEntry = ${JSON.stringify(entry)};
894
+
895
+ async function handlePublicDocsRequest(request: Request) {
896
+ const url = new URL(request.url);
897
+ const method = request.method.toUpperCase();
898
+
899
+ if (isDocsMcpRequest(url, docsConfig.mcp)) {
900
+ if (method === "OPTIONS") return docsServer.MCP.OPTIONS({ request });
901
+ if (method === "POST") return docsServer.MCP.POST({ request });
902
+ if (method === "DELETE") return docsServer.MCP.DELETE({ request });
903
+ if (method === "GET" || method === "HEAD") return docsServer.MCP.GET({ request });
904
+ return new Response("Method Not Allowed", {
905
+ status: 405,
906
+ headers: { Allow: "GET, HEAD, POST, DELETE, OPTIONS" },
907
+ });
908
+ }
909
+
910
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, url, request, { sitemap: docsConfig.sitemap, llms: docsConfig.llmsTxt, robots: docsConfig.robots })) {
911
+ return docsServer.GET({ request });
912
+ }
913
+
914
+ return new Response("Not Found", { status: 404 });
915
+ }
916
+
917
+ export const Route = createFileRoute("/$")({
918
+ server: {
919
+ handlers: {
920
+ GET: async ({ request }) => handlePublicDocsRequest(request),
921
+ POST: async ({ request }) => handlePublicDocsRequest(request),
922
+ DELETE: async ({ request }) => handlePublicDocsRequest(request),
923
+ OPTIONS: async ({ request }) => handlePublicDocsRequest(request),
924
+ },
925
+ },
926
+ });
927
+ `;
928
+ }
929
+ function tanstackApiReferenceRouteTemplate(opts) {
930
+ const routePath = `/${opts.apiReferencePath}${opts.catchAll ? "/$" : "/"}`;
931
+ return `\
932
+ import { createFileRoute } from "@tanstack/react-router";
933
+ import { createTanstackApiReference } from "@farming-labs/tanstack-start/api-reference";
934
+ import docsConfig from "${tanstackDocsConfigImport(opts.filePath)}";
935
+
936
+ const handler = createTanstackApiReference(docsConfig);
937
+
938
+ export const Route = createFileRoute("${routePath}")({
939
+ server: {
940
+ handlers: {
941
+ GET: handler,
942
+ },
943
+ },
944
+ });
945
+ `;
946
+ }
947
+ function tanstackRootRouteTemplate(globalCssRelPath) {
948
+ return `\
949
+ import appCss from "${relativeAssetPath("src/routes/__root.tsx", globalCssRelPath)}?url";
950
+ import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-router";
951
+ import { RootProvider } from "@farming-labs/theme/tanstack";
952
+
953
+ export const Route = createRootRoute({
954
+ head: () => ({
955
+ links: [{ rel: "stylesheet", href: appCss }],
956
+ meta: [
957
+ { charSet: "utf-8" },
958
+ { name: "viewport", content: "width=device-width, initial-scale=1" },
959
+ { title: "Docs" },
960
+ ],
961
+ }),
962
+ component: RootComponent,
963
+ });
964
+
965
+ function RootComponent() {
966
+ return (
967
+ <html lang="en" suppressHydrationWarning>
968
+ <head>
969
+ <HeadContent />
970
+ </head>
971
+ <body>
972
+ <RootProvider>
973
+ <Outlet />
974
+ </RootProvider>
975
+ <Scripts />
976
+ </body>
977
+ </html>
978
+ );
979
+ }
980
+ `;
981
+ }
982
+ function injectTanstackRootProviderIntoRoute(content) {
983
+ if (!content || content.includes("RootProvider")) return null;
984
+ let out = addImportLine(content, "import { RootProvider } from \"@farming-labs/theme/tanstack\";");
985
+ if (out.includes("<Outlet />")) out = out.replace("<Outlet />", "<RootProvider><Outlet /></RootProvider>");
986
+ else if (out.includes("<Outlet></Outlet>")) out = out.replace("<Outlet></Outlet>", "<RootProvider><Outlet /></RootProvider>");
987
+ else return null;
988
+ return out === content ? null : out;
989
+ }
990
+ function tanstackViteConfigTemplate(useAlias) {
991
+ return `\
992
+ import { defineConfig } from "vite";
993
+ import tailwindcss from "@tailwindcss/vite";
994
+ ${useAlias ? "import tsconfigPaths from \"vite-tsconfig-paths\";\n" : ""}import { tanstackStart } from "@tanstack/react-start/plugin/vite";
995
+ import { docsAgentSkills } from "@farming-labs/docs/vite";
996
+ import { docsMdx } from "@farming-labs/tanstack-start/vite";
997
+ import docsConfig from "./docs.config";
998
+
999
+ export default defineConfig({
1000
+ plugins: [tailwindcss(), docsMdx(), docsAgentSkills(docsConfig), ${useAlias ? "tsconfigPaths({ ignoreConfigErrors: true }), " : ""}tanstackStart()],
1001
+ });
1002
+ `;
1003
+ }
1004
+ function svelteViteConfigTemplate() {
1005
+ return `\
1006
+ import { sveltekit } from "@sveltejs/kit/vite";
1007
+ import { defineConfig } from "vite";
1008
+ import { docsAgentSkills } from "@farming-labs/docs/vite";
1009
+ import docsConfig from "./src/lib/docs.config";
1010
+
1011
+ export default defineConfig({
1012
+ plugins: [sveltekit(), docsAgentSkills(docsConfig)],
1013
+ });
1014
+ `;
1015
+ }
1016
+ /** Add the Agent Skills snapshot plugin to an existing Vite config. */
1017
+ function injectDocsAgentSkillsVitePlugin(content, configImportPath) {
1018
+ if (!content) return null;
1019
+ let out = addImportLine(content, "import { docsAgentSkills } from \"@farming-labs/docs/vite\";");
1020
+ out = addImportLine(out, `import docsConfig from ${JSON.stringify(configImportPath)};`);
1021
+ if (out.includes("docsAgentSkills(docsConfig)")) return out === content ? null : out;
1022
+ const pluginsMatch = out.match(/plugins\s*:\s*\[([\s\S]*?)\]/m);
1023
+ if (pluginsMatch) {
1024
+ const current = pluginsMatch[1].trim();
1025
+ const existing = current ? `${current}${current.endsWith(",") ? "" : ","}` : "";
1026
+ return out.replace(pluginsMatch[0], `plugins: [${existing}${existing ? " " : ""}docsAgentSkills(docsConfig)]`);
1027
+ }
1028
+ const configMatch = out.match(/defineConfig\(\s*\{/);
1029
+ if (!configMatch) return null;
1030
+ return out.replace(configMatch[0], "defineConfig({\n plugins: [docsAgentSkills(docsConfig)],");
1031
+ }
1032
+ function injectTanstackVitePlugins(content, useAlias) {
1033
+ if (!content) return null;
1034
+ let out = content;
1035
+ out = addImportLine(out, "import tailwindcss from \"@tailwindcss/vite\";");
1036
+ if (useAlias) out = addImportLine(out, "import tsconfigPaths from \"vite-tsconfig-paths\";");
1037
+ out = addImportLine(out, "import { docsMdx } from \"@farming-labs/tanstack-start/vite\";");
1038
+ out = addImportLine(out, "import { docsAgentSkills } from \"@farming-labs/docs/vite\";");
1039
+ out = addImportLine(out, "import docsConfig from \"./docs.config\";");
1040
+ const additions = [];
1041
+ if (!out.includes("tailwindcss()")) additions.push("tailwindcss()");
1042
+ if (!out.includes("docsMdx()")) additions.push("docsMdx()");
1043
+ if (!out.includes("docsAgentSkills(")) additions.push("docsAgentSkills(docsConfig)");
1044
+ if (useAlias && !out.includes("tsconfigPaths(")) additions.push("tsconfigPaths({ ignoreConfigErrors: true })");
1045
+ if (additions.length === 0) return out === content ? null : out;
1046
+ const pluginsMatch = out.match(/plugins\s*:\s*\[([\s\S]*?)\]/m);
1047
+ if (pluginsMatch) {
1048
+ const current = pluginsMatch[1].trim();
1049
+ const existing = current ? `${current}${current.endsWith(",") ? "" : ","}` : "";
1050
+ const replacement = `plugins: [\n ${existing}${existing ? "\n " : ""}${additions.join(",\n ")}\n ]`;
1051
+ return out.replace(pluginsMatch[0], replacement);
1052
+ }
1053
+ const configMatch = out.match(/defineConfig\(\s*\{/);
1054
+ if (configMatch) {
1055
+ const insertion = `defineConfig({\n plugins: [${additions.join(", ")}],`;
1056
+ return out.replace(configMatch[0], insertion);
1057
+ }
1058
+ return out === content ? null : out;
1059
+ }
1060
+ function tanstackWelcomePageTemplate(cfg) {
1061
+ return `\
1062
+ ---
1063
+ title: "Documentation"
1064
+ description: "Welcome to ${cfg.projectName} documentation"
1065
+ ---
1066
+
1067
+ # Welcome to ${cfg.projectName}
1068
+
1069
+ This docs site is powered by \`@farming-labs/docs\` and TanStack Start.
1070
+
1071
+ ## Overview
1072
+
1073
+ - Content lives in \`${cfg.entry}/\`
1074
+ - Routes live in \`src/routes/${cfg.entry}/\`
1075
+ - Search is served from \`/api/docs\`
1076
+
1077
+ ## Next Steps
1078
+
1079
+ Read the [Installation](/${cfg.entry}/installation) guide, then continue to [Quickstart](/${cfg.entry}/quickstart).
1080
+ `;
1081
+ }
1082
+ function tanstackInstallationPageTemplate(cfg) {
1083
+ if (cfg.theme === "custom" && cfg.customThemeName) {
1084
+ const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
1085
+ const exportName = getThemeExportName(baseName);
1086
+ const cssImportPath = getCustomThemeCssImportPath("src/styles/app.css", baseName);
1087
+ return `\
1088
+ ---
1089
+ title: "Installation"
1090
+ description: "How to install and set up ${cfg.projectName}"
1091
+ ---
1092
+
1093
+ # Installation
1094
+
1095
+ Add the docs packages to your TanStack Start app:
1096
+
1097
+ \`\`\`bash
1098
+ pnpm add @farming-labs/docs @farming-labs/theme @farming-labs/tanstack-start
1099
+ \`\`\`
1100
+
1101
+ The scaffold also configures MDX through \`docsMdx()\` in \`vite.config.ts\`.
1102
+
1103
+ ## Theme CSS
1104
+
1105
+ Keep your config theme and global CSS import aligned:
1106
+
1107
+ \`\`\`ts title="docs.config.ts"
1108
+ import { defineDocs } from "@farming-labs/docs";
1109
+ import { ${exportName} } from "./themes/${baseName}";
1110
+
1111
+ export default defineDocs({
1112
+ entry: "${cfg.entry}",
1113
+ contentDir: "${cfg.entry}",
1114
+ theme: ${exportName}(),
1115
+ });
1116
+ \`\`\`
1117
+
1118
+ \`\`\`css title="src/styles/app.css"
1119
+ @import "tailwindcss";
1120
+ @import "${cssImportPath}";
1121
+ \`\`\`
1122
+
1123
+ ## Generated Files
1124
+
1125
+ \`\`\`
1126
+ docs.config.ts
1127
+ themes/${baseName}.ts
1128
+ themes/${baseName}.css
1129
+ ${cfg.entry}/
1130
+ src/lib/docs.server.ts
1131
+ src/lib/docs.functions.ts
1132
+ src/routes/${cfg.entry}/index.tsx
1133
+ src/routes/${cfg.entry}/$.tsx
1134
+ src/routes/api/docs.ts
1135
+ src/routes/$.ts
1136
+ \`\`\`
1137
+ `;
1138
+ }
1139
+ const t = getThemeInfo(cfg.theme);
1140
+ return `\
1141
+ ---
1142
+ title: "Installation"
1143
+ description: "How to install and set up ${cfg.projectName}"
1144
+ ---
1145
+
1146
+ # Installation
1147
+
1148
+ Add the docs packages to your TanStack Start app:
1149
+
1150
+ \`\`\`bash
1151
+ pnpm add @farming-labs/docs @farming-labs/theme @farming-labs/tanstack-start
1152
+ \`\`\`
1153
+
1154
+ The scaffold also configures MDX through \`docsMdx()\` in \`vite.config.ts\`.
1155
+
1156
+ ## Theme CSS
1157
+
1158
+ Keep your config theme and global CSS import aligned:
1159
+
1160
+ \`\`\`ts title="docs.config.ts"
1161
+ import { defineDocs } from "@farming-labs/docs";
1162
+ import { ${t.factory} } from "${t.nextImport}";
1163
+
1164
+ export default defineDocs({
1165
+ entry: "${cfg.entry}",
1166
+ contentDir: "${cfg.entry}",
1167
+ theme: ${t.factory}(),
1168
+ });
1169
+ \`\`\`
1170
+
1171
+ \`\`\`css title="src/styles/app.css"
1172
+ @import "tailwindcss";
1173
+ @import "@farming-labs/theme/${t.nextCssImport}/css";
1174
+ \`\`\`
1175
+
1176
+ ## Generated Files
1177
+
1178
+ \`\`\`
1179
+ docs.config.ts
1180
+ ${cfg.entry}/
1181
+ src/lib/docs.server.ts
1182
+ src/lib/docs.functions.ts
1183
+ src/routes/${cfg.entry}/index.tsx
1184
+ src/routes/${cfg.entry}/$.tsx
1185
+ src/routes/api/docs.ts
1186
+ \`\`\`
1187
+ `;
1188
+ }
1189
+ function tanstackQuickstartPageTemplate(cfg) {
1190
+ return `\
1191
+ ---
1192
+ title: "Quickstart"
1193
+ description: "Get up and running in minutes"
1194
+ ---
1195
+
1196
+ # Quickstart
1197
+
1198
+ Create a new page under \`${cfg.entry}/\`:
1199
+
1200
+ \`\`\`bash
1201
+ mkdir -p ${cfg.entry}/my-page
1202
+ \`\`\`
1203
+
1204
+ Then add \`${cfg.entry}/my-page/page.mdx\`:
1205
+
1206
+ \`\`\`mdx
1207
+ ---
1208
+ title: "My Page"
1209
+ description: "A custom documentation page"
1210
+ ---
1211
+
1212
+ # My Page
1213
+
1214
+ Write your content here using **MDX**.
1215
+ \`\`\`
1216
+
1217
+ Visit [/${cfg.entry}/my-page](/${cfg.entry}/my-page) after starting the dev server.
1218
+ `;
1219
+ }
1220
+ function svelteDocsConfigTemplate(cfg) {
1221
+ if (cfg.theme === "custom" && cfg.customThemeName) {
1222
+ const exportName = getThemeExportName(cfg.customThemeName);
1223
+ return `\
1224
+ import { defineDocs } from "@farming-labs/docs";
1225
+ import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
1226
+
1227
+ export default defineDocs({
1228
+ entry: "${cfg.entry}",
1229
+ contentDir: "${cfg.entry}",
1230
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
1231
+ ui: {
1232
+ colors: { primary: "#6366f1" },
1233
+ },
1234
+ }),
1235
+
1236
+ nav: {
1237
+ title: "${cfg.projectName}",
1238
+ url: "/${cfg.entry}",
1239
+ },
1240
+
1241
+ breadcrumb: { enabled: true },
1242
+
1243
+ metadata: {
1244
+ titleTemplate: "%s – ${cfg.projectName}",
1245
+ description: "Documentation for ${cfg.projectName}",
1246
+ },
1247
+ });
1248
+ `;
1249
+ }
1250
+ const t = getThemeInfo(cfg.theme);
1251
+ return `\
1252
+ import { defineDocs } from "@farming-labs/docs";
1253
+ import { ${t.factory} } from "${t.svelteImport}";
1254
+
1255
+ export default defineDocs({
1256
+ entry: "${cfg.entry}",
1257
+ contentDir: "${cfg.entry}",
1258
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
1259
+ ui: {
1260
+ colors: { primary: "#6366f1" },
1261
+ },
1262
+ }),
1263
+
1264
+ nav: {
1265
+ title: "${cfg.projectName}",
1266
+ url: "/${cfg.entry}",
1267
+ },
1268
+
1269
+ breadcrumb: { enabled: true },
1270
+
1271
+ metadata: {
1272
+ titleTemplate: "%s – ${cfg.projectName}",
1273
+ description: "Documentation for ${cfg.projectName}",
1274
+ },
1275
+ });
1276
+ `;
1277
+ }
1278
+ function svelteDocsServerTemplate(cfg) {
1279
+ return `\
1280
+ import { createDocsServer } from "@farming-labs/svelte/server";
1281
+ import { bundledAgentSkills } from "@farming-labs/docs/agent-skills-bundle";
1282
+ import config from "${svelteServerConfigImport(cfg.useAlias)}";
1283
+
1284
+ // preload for production
1285
+ const contentFiles = import.meta.glob(["/${cfg.entry ?? "docs"}/**/*.{md,mdx,svx}", "/AGENTS.md", "/AGENT.md", "/skill.md", "/.farming-labs/sitemap-manifest.json"], {
1286
+ query: "?raw",
1287
+ import: "default",
1288
+ eager: true,
1289
+ }) as Record<string, string>;
1290
+
1291
+ export const { load, GET, POST, MCP } = createDocsServer({
1292
+ ...config,
1293
+ _preloadedContent: contentFiles,
1294
+ _preloadedAgentSkills: bundledAgentSkills,
1295
+ });
1296
+ `;
1297
+ }
1298
+ function svelteDocsLayoutTemplate(cfg) {
1299
+ return `\
1300
+ <script>
1301
+ import { DocsLayout } from "@farming-labs/svelte-theme";
1302
+ import config from "${svelteLayoutConfigImport(cfg.useAlias)}";
1303
+
1304
+ let { data, children } = $props();
1305
+ <\/script>
1306
+
1307
+ <DocsLayout tree={data.tree} {config}>
1308
+ {@render children()}
1309
+ </DocsLayout>
1310
+ `;
1311
+ }
1312
+ function svelteDocsLayoutServerTemplate(cfg) {
1313
+ return `\
1314
+ export { load } from "${svelteLayoutServerImport(cfg.useAlias)}";
1315
+ `;
1316
+ }
1317
+ function svelteDocsPageTemplate(cfg) {
1318
+ return `\
1319
+ <script>
1320
+ import { DocsContent } from "@farming-labs/svelte-theme";
1321
+ import config from "${sveltePageConfigImport(cfg.useAlias)}";
1322
+
1323
+ let { data } = $props();
1324
+ <\/script>
1325
+
1326
+ <DocsContent {data} {config} />
1327
+ `;
1328
+ }
1329
+ function svelteApiReferenceRouteTemplate(filePath, useAlias) {
1330
+ return `\
1331
+ import { createSvelteApiReference } from "@farming-labs/svelte/api-reference";
1332
+ import config from "${useAlias ? "$lib/docs.config" : relativeImport(filePath, "src/lib/docs.config.ts")}";
1333
+
1334
+ export const GET = createSvelteApiReference(config);
1335
+ `;
1336
+ }
1337
+ function svelteDocsApiRouteTemplate(filePath, useAlias) {
1338
+ return `\
1339
+ export { GET, POST } from "${svelteRouteServerImport(filePath, useAlias)}";
1340
+ `;
1341
+ }
1342
+ function svelteDocsPublicHookTemplate(filePath, useAlias) {
1343
+ const serverImport = svelteRouteServerImport(filePath, useAlias);
1344
+ return `\
1345
+ import { isDocsLlmsTxtPublicRequest, isDocsMcpRequest, isDocsPublicGetRequest } from "@farming-labs/docs";
1346
+ import type { Handle } from "@sveltejs/kit";
1347
+ import config from "${svelteRouteConfigImport(filePath, useAlias)}";
1348
+ import { GET, MCP } from "${serverImport}";
1349
+
1350
+ const docsEntry = config.entry ?? "docs";
1351
+
1352
+ export const handle: Handle = async ({ event, resolve }) => {
1353
+ const method = event.request.method.toUpperCase();
1354
+
1355
+ if (isDocsMcpRequest(event.url, config.mcp)) {
1356
+ if (method === "OPTIONS") return MCP.OPTIONS({ request: event.request });
1357
+ if (method === "POST") return MCP.POST({ request: event.request });
1358
+ if (method === "DELETE") return MCP.DELETE({ request: event.request });
1359
+ if (method === "GET" || method === "HEAD") return MCP.GET({ request: event.request });
1360
+ return new Response("Method Not Allowed", {
1361
+ status: 405,
1362
+ headers: { Allow: "GET, HEAD, POST, DELETE, OPTIONS" },
1363
+ });
1364
+ }
1365
+
1366
+ if ((method === "GET" || method === "HEAD") && isDocsLlmsTxtPublicRequest(event.url, config.llmsTxt, docsEntry)) {
1367
+ const nativeResponse = await resolve(event);
1368
+ if (nativeResponse.status !== 404) return nativeResponse;
1369
+ }
1370
+
1371
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, event.url, event.request, { sitemap: config.sitemap, llms: config.llmsTxt, robots: config.robots })) {
1372
+ return GET({ url: event.url, request: event.request });
1373
+ }
1374
+
1375
+ return resolve(event);
1376
+ };
1377
+ `;
1378
+ }
1379
+ function injectSvelteDocsPublicHook(content, filePath, useAlias) {
1380
+ if (content.includes("isDocsPublicGetRequest") || content.includes("docsPublicHandle")) return null;
1381
+ if (/export\s*{\s*handle\b/.test(content)) return null;
1382
+ const serverImport = svelteRouteServerImport(filePath, useAlias);
1383
+ const configImport = svelteRouteConfigImport(filePath, useAlias);
1384
+ let next = content.trimEnd();
1385
+ let hasExistingHandle = false;
1386
+ for (const [pattern, replacement] of [
1387
+ [/\bexport\s+const\s+handle(\s*:\s*[^=]+)?\s*=/, "const existingHandle$1 ="],
1388
+ [/\bexport\s+async\s+function\s+handle\b/, "async function existingHandle"],
1389
+ [/\bexport\s+function\s+handle\b/, "function existingHandle"]
1390
+ ]) if (pattern.test(next)) {
1391
+ next = next.replace(pattern, replacement);
1392
+ next = next.replace(/const existingHandle(:[^=\n]*?)\s+=/, "const existingHandle$1 =");
1393
+ hasExistingHandle = true;
1394
+ break;
1395
+ }
1396
+ const imports = [
1397
+ "import { isDocsLlmsTxtPublicRequest, isDocsMcpRequest, isDocsPublicGetRequest } from \"@farming-labs/docs\";",
1398
+ ...next.includes("Handle") ? [] : ["import type { Handle } from \"@sveltejs/kit\";"],
1399
+ ...hasExistingHandle && !next.includes("sequence") ? ["import { sequence } from \"@sveltejs/kit/hooks\";"] : [],
1400
+ `import docsConfig from "${configImport}";`,
1401
+ `import { GET as docsGET, MCP as docsMCP } from "${serverImport}";`
1402
+ ];
1403
+ const docsHandle = `\
1404
+ const docsEntry = docsConfig.entry ?? "docs";
1405
+
1406
+ const docsPublicHandle: Handle = async ({ event, resolve }) => {
1407
+ const method = event.request.method.toUpperCase();
1408
+
1409
+ if (isDocsMcpRequest(event.url, docsConfig.mcp)) {
1410
+ if (method === "OPTIONS") return docsMCP.OPTIONS({ request: event.request });
1411
+ if (method === "POST") return docsMCP.POST({ request: event.request });
1412
+ if (method === "DELETE") return docsMCP.DELETE({ request: event.request });
1413
+ if (method === "GET" || method === "HEAD") return docsMCP.GET({ request: event.request });
1414
+ return new Response("Method Not Allowed", {
1415
+ status: 405,
1416
+ headers: { Allow: "GET, HEAD, POST, DELETE, OPTIONS" },
1417
+ });
1418
+ }
1419
+
1420
+ if ((method === "GET" || method === "HEAD") && isDocsLlmsTxtPublicRequest(event.url, docsConfig.llmsTxt, docsEntry)) {
1421
+ const nativeResponse = await resolve(event);
1422
+ if (nativeResponse.status !== 404) return nativeResponse;
1423
+ }
1424
+
1425
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, event.url, event.request, { sitemap: docsConfig.sitemap, llms: docsConfig.llmsTxt, robots: docsConfig.robots })) {
1426
+ return docsGET({ url: event.url, request: event.request });
1427
+ }
1428
+
1429
+ return resolve(event);
1430
+ };`;
1431
+ const exportLine = hasExistingHandle ? "export const handle = sequence(docsPublicHandle, existingHandle);" : "export const handle = docsPublicHandle;";
1432
+ return `${imports.join("\n")}\n${next}\n\n${docsHandle}\n\n${exportLine}\n`;
1433
+ }
1434
+ function svelteRootLayoutTemplate(globalCssRelPath) {
1435
+ let cssImport;
1436
+ if (globalCssRelPath.startsWith("src/")) cssImport = "./" + globalCssRelPath.slice(4);
1437
+ else cssImport = "../" + globalCssRelPath;
1438
+ return `\
1439
+ <script>
1440
+ import "${cssImport}";
1441
+
1442
+ let { children } = $props();
1443
+ <\/script>
1444
+
1445
+ {@render children()}
1446
+ `;
1447
+ }
1448
+ function svelteGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
1449
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
1450
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
1451
+ `;
1452
+ return `\
1453
+ @import "${getThemeCssImport(theme)}";
1454
+ `;
1455
+ }
1456
+ function svelteCssImportLine(theme, customThemeName, globalCssRelPath) {
1457
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
1458
+ return `@import "${getThemeCssImport(theme)}";`;
1459
+ }
1460
+ function injectSvelteCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
1461
+ const importLine = svelteCssImportLine(theme, customThemeName, globalCssRelPath);
1462
+ if (existingContent.includes(importLine)) return null;
1463
+ if (theme !== "custom" && hasBuiltInThemeCssImport(existingContent)) return null;
1464
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
1465
+ const lines = existingContent.split("\n");
1466
+ const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
1467
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
1468
+ else lines.unshift(importLine);
1469
+ return lines.join("\n");
1470
+ }
1471
+ function svelteWelcomePageTemplate(cfg) {
1472
+ return `\
1473
+ ---
1474
+ title: "Documentation"
1475
+ description: "Welcome to ${cfg.projectName} documentation"
1476
+ ---
1477
+
1478
+ # Welcome to ${cfg.projectName}
1479
+
1480
+ Get started with our documentation. Browse the pages on the left to learn more.
1481
+
1482
+ ## Overview
1483
+
1484
+ This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
1485
+
1486
+ ## Features
1487
+
1488
+ - **Markdown Support** — Write docs with standard Markdown
1489
+ - **Syntax Highlighting** — Code blocks with automatic highlighting
1490
+ - **Dark Mode** — Built-in theme switching
1491
+ - **Search** — Full-text search across all pages
1492
+ - **Responsive** — Works on any screen size
1493
+
1494
+ ---
1495
+
1496
+ ## Next Steps
1497
+
1498
+ Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
1499
+ `;
1500
+ }
1501
+ function svelteInstallationPageTemplate(cfg) {
1502
+ const t = getThemeInfo(cfg.theme);
1503
+ return `\
1504
+ ---
1505
+ title: "Installation"
1506
+ description: "How to install and set up ${cfg.projectName}"
1507
+ ---
1508
+
1509
+ # Installation
1510
+
1511
+ Follow these steps to install and configure ${cfg.projectName}.
1512
+
1513
+ ## Prerequisites
1514
+
1515
+ - Node.js 18+
1516
+ - A package manager (pnpm, npm, or yarn)
1517
+
1518
+ ## Install Dependencies
1519
+
1520
+ \`\`\`bash
1521
+ pnpm add @farming-labs/docs @farming-labs/svelte @farming-labs/svelte-theme @farming-labs/theme
1522
+ \`\`\`
1523
+
1524
+ ## Configuration
1525
+
1526
+ Your project includes a \`docs.config.ts\` in \`src/lib/\`:
1527
+
1528
+ \`\`\`ts title="src/lib/docs.config.ts"
1529
+ import { defineDocs } from "@farming-labs/docs";
1530
+ import { ${t.factory} } from "${t.svelteImport}";
1531
+
1532
+ export default defineDocs({
1533
+ entry: "${cfg.entry}",
1534
+ contentDir: "${cfg.entry}",
1535
+ theme: ${t.factory}({
1536
+ ui: { colors: { primary: "#6366f1" } },
1537
+ }),
1538
+ });
1539
+ \`\`\`
1540
+
1541
+ ## Project Structure
1542
+
1543
+ \`\`\`
1544
+ ${cfg.entry}/ # Markdown content
1545
+ page.md # /${cfg.entry}
1546
+ installation/
1547
+ page.md # /${cfg.entry}/installation
1548
+ quickstart/
1549
+ page.md # /${cfg.entry}/quickstart
1550
+ src/
1551
+ hooks.server.ts # Public docs aliases
1552
+ lib/
1553
+ docs.config.ts # Docs configuration
1554
+ docs.server.ts # Server-side docs loader
1555
+ routes/
1556
+ api/docs/+server.ts # Search/AI API route
1557
+ ${cfg.entry}/
1558
+ +layout.svelte # Docs layout
1559
+ +layout.server.js # Layout data loader
1560
+ [...slug]/
1561
+ +page.svelte # Dynamic doc page
1562
+ \`\`\`
1563
+
1564
+ ## What's Next?
1565
+
1566
+ Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
1567
+ `;
1568
+ }
1569
+ function svelteQuickstartPageTemplate(cfg) {
1570
+ const t = getThemeInfo(cfg.theme);
1571
+ return `\
1572
+ ---
1573
+ title: "Quickstart"
1574
+ description: "Get up and running in minutes"
1575
+ ---
1576
+
1577
+ # Quickstart
1578
+
1579
+ This guide walks you through creating your first documentation page.
1580
+
1581
+ ## Creating a Page
1582
+
1583
+ Create a new folder under \`${cfg.entry}/\` with a \`page.md\` file:
1584
+
1585
+ \`\`\`bash
1586
+ mkdir -p ${cfg.entry}/my-page
1587
+ \`\`\`
1588
+
1589
+ Then create \`${cfg.entry}/my-page/page.md\`:
1590
+
1591
+ \`\`\`md
1592
+ ---
1593
+ title: "My Page"
1594
+ description: "A custom documentation page"
1595
+ ---
1596
+
1597
+ # My Page
1598
+
1599
+ Write your content here using **Markdown**.
1600
+ \`\`\`
1601
+
1602
+ Your page is now available at \`/${cfg.entry}/my-page\`.
1603
+
1604
+ ## Code Blocks
1605
+
1606
+ Code blocks are automatically syntax-highlighted:
1607
+
1608
+ \`\`\`typescript
1609
+ function greet(name: string): string {
1610
+ return \\\`Hello, \\\${name}!\\\`;
1611
+ }
1612
+
1613
+ console.log(greet("World"));
1614
+ \`\`\`
1615
+
1616
+ ## Customizing the Theme
1617
+
1618
+ Edit \`src/lib/docs.config.ts\` to change colors, typography, and component defaults:
1619
+
1620
+ \`\`\`ts title="src/lib/docs.config.ts"
1621
+ theme: ${t.factory}({
1622
+ ui: {
1623
+ colors: { primary: "#22c55e" },
1624
+ },
1625
+ }),
1626
+ \`\`\`
1627
+
1628
+ ## Deploying
1629
+
1630
+ Build your docs for production:
1631
+
1632
+ \`\`\`bash
1633
+ pnpm build
1634
+ \`\`\`
1635
+
1636
+ Deploy to Vercel, Netlify, or any Node.js hosting platform.
1637
+ `;
1638
+ }
1639
+ function astroDocsConfigTemplate(cfg) {
1640
+ if (cfg.theme === "custom" && cfg.customThemeName) {
1641
+ const exportName = getThemeExportName(cfg.customThemeName);
1642
+ return `\
1643
+ import { defineDocs } from "@farming-labs/docs";
1644
+ import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
1645
+
1646
+ export default defineDocs({
1647
+ entry: "${cfg.entry}",
1648
+ contentDir: "${cfg.entry}",
1649
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
1650
+ ui: {
1651
+ colors: { primary: "#6366f1" },
1652
+ },
1653
+ }),
1654
+
1655
+ nav: {
1656
+ title: "${cfg.projectName}",
1657
+ url: "/${cfg.entry}",
1658
+ },
1659
+
1660
+ breadcrumb: { enabled: true },
1661
+
1662
+ metadata: {
1663
+ titleTemplate: "%s – ${cfg.projectName}",
1664
+ description: "Documentation for ${cfg.projectName}",
1665
+ },
1666
+ });
1667
+ `;
1668
+ }
1669
+ const t = getThemeInfo(cfg.theme);
1670
+ return `\
1671
+ import { defineDocs } from "@farming-labs/docs";
1672
+ import { ${t.factory} } from "${t.astroImport}";
1673
+
1674
+ export default defineDocs({
1675
+ entry: "${cfg.entry}",
1676
+ contentDir: "${cfg.entry}",
1677
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
1678
+ ui: {
1679
+ colors: { primary: "#6366f1" },
1680
+ },
1681
+ }),
1682
+
1683
+ nav: {
1684
+ title: "${cfg.projectName}",
1685
+ url: "/${cfg.entry}",
1686
+ },
1687
+
1688
+ breadcrumb: { enabled: true },
1689
+
1690
+ metadata: {
1691
+ titleTemplate: "%s – ${cfg.projectName}",
1692
+ description: "Documentation for ${cfg.projectName}",
1693
+ },
1694
+ });
1695
+ `;
1696
+ }
1697
+ function astroDocsServerTemplate(cfg) {
1698
+ return `\
1699
+ import { createDocsServer } from "@farming-labs/astro/server";
1700
+ import { bundledAgentSkills } from "@farming-labs/docs/agent-skills-bundle";
1701
+ import config from "${astroServerConfigImport(cfg.useAlias)}";
1702
+
1703
+ const contentFiles = import.meta.glob(["/${cfg.entry ?? "docs"}/**/*.{md,mdx}", "/AGENTS.md", "/AGENT.md", "/skill.md", "/.farming-labs/sitemap-manifest.json"], {
1704
+ query: "?raw",
1705
+ import: "default",
1706
+ eager: true,
1707
+ }) as Record<string, string>;
1708
+
1709
+ export const { load, GET, POST, MCP } = createDocsServer({
1710
+ ...config,
1711
+ _preloadedContent: contentFiles,
1712
+ _preloadedAgentSkills: bundledAgentSkills,
1713
+ });
1714
+ `;
1715
+ }
1716
+ const ASTRO_ADAPTER_INFO = {
1717
+ vercel: {
1718
+ pkg: "@astrojs/vercel",
1719
+ import: "@astrojs/vercel"
1720
+ },
1721
+ netlify: {
1722
+ pkg: "@astrojs/netlify",
1723
+ import: "@astrojs/netlify"
1724
+ },
1725
+ node: {
1726
+ pkg: "@astrojs/node",
1727
+ import: "@astrojs/node"
1728
+ },
1729
+ cloudflare: {
1730
+ pkg: "@astrojs/cloudflare",
1731
+ import: "@astrojs/cloudflare"
1732
+ }
1733
+ };
1734
+ function getAstroAdapterPkg(adapter) {
1735
+ return ASTRO_ADAPTER_INFO[adapter]?.pkg ?? ASTRO_ADAPTER_INFO.vercel.pkg;
1736
+ }
1737
+ function astroConfigTemplate(adapter = "vercel") {
1738
+ const info = ASTRO_ADAPTER_INFO[adapter] ?? ASTRO_ADAPTER_INFO.vercel;
1739
+ const adapterCall = adapter === "node" ? `${adapter}({ mode: "standalone" })` : `${adapter}()`;
1740
+ return `\
1741
+ import { defineConfig } from "astro/config";
1742
+ import ${adapter} from "${info.import}";
1743
+ import { docsAgentSkills } from "@farming-labs/docs/vite";
1744
+ import docsConfig from "./src/lib/docs.config";
1745
+
1746
+ export default defineConfig({
1747
+ output: "server",
1748
+ adapter: ${adapterCall},
1749
+ vite: { plugins: [docsAgentSkills(docsConfig)] },
1750
+ });
1751
+ `;
1752
+ }
1753
+ /** Add the Agent Skills snapshot plugin to an existing Astro config. */
1754
+ function injectAstroAgentSkillsPlugin(content) {
1755
+ if (!content) return null;
1756
+ let out = addImportLine(content, "import { docsAgentSkills } from \"@farming-labs/docs/vite\";");
1757
+ out = addImportLine(out, "import docsConfig from \"./src/lib/docs.config\";");
1758
+ if (out.includes("docsAgentSkills(docsConfig)")) return out === content ? null : out;
1759
+ const vitePlugins = out.match(/(vite\s*:\s*\{[\s\S]*?plugins\s*:\s*\[)([\s\S]*?)(\])/m);
1760
+ if (vitePlugins) {
1761
+ const current = vitePlugins[2].trim();
1762
+ const next = `${current}${current && !current.endsWith(",") ? "," : ""}${current ? " " : ""}docsAgentSkills(docsConfig)`;
1763
+ return out.replace(vitePlugins[0], `${vitePlugins[1]}${next}${vitePlugins[3]}`);
1764
+ }
1765
+ const viteBlock = out.match(/vite\s*:\s*\{/);
1766
+ if (viteBlock) return out.replace(viteBlock[0], `${viteBlock[0]}\n plugins: [docsAgentSkills(docsConfig)],`);
1767
+ const configMatch = out.match(/defineConfig\(\s*\{/);
1768
+ if (!configMatch) return null;
1769
+ return out.replace(configMatch[0], "defineConfig({\n vite: { plugins: [docsAgentSkills(docsConfig)] },");
1770
+ }
1771
+ function astroDocsPageTemplate(cfg) {
1772
+ return `\
1773
+ ---
1774
+ import DocsLayout from "@farming-labs/astro-theme/src/components/DocsLayout.astro";
1775
+ import DocsContent from "@farming-labs/astro-theme/src/components/DocsContent.astro";
1776
+ import SearchDialog from "@farming-labs/astro-theme/src/components/SearchDialog.astro";
1777
+ import config from "${astroPageConfigImport(cfg.useAlias, 2)}";
1778
+ import { load } from "${astroPageServerImport(cfg.useAlias, 2)}";
1779
+ import "${getThemeCssImport(cfg.theme)}";
1780
+
1781
+ const data = await load(Astro.url.pathname);
1782
+ ---
1783
+
1784
+ <html lang="en">
1785
+ <head>
1786
+ <meta charset="utf-8" />
1787
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1788
+ <title>{data.title} – Docs</title>
1789
+ </head>
1790
+ <body>
1791
+ <DocsLayout tree={data.tree} config={config}>
1792
+ <DocsContent data={data} config={config} />
1793
+ </DocsLayout>
1794
+ <SearchDialog config={config} />
1795
+ </body>
1796
+ </html>
1797
+ `;
1798
+ }
1799
+ function astroDocsIndexTemplate(cfg) {
1800
+ return `\
1801
+ ---
1802
+ import DocsLayout from "@farming-labs/astro-theme/src/components/DocsLayout.astro";
1803
+ import DocsContent from "@farming-labs/astro-theme/src/components/DocsContent.astro";
1804
+ import SearchDialog from "@farming-labs/astro-theme/src/components/SearchDialog.astro";
1805
+ import config from "${astroPageConfigImport(cfg.useAlias, 2)}";
1806
+ import { load } from "${astroPageServerImport(cfg.useAlias, 2)}";
1807
+ import "${getThemeCssImport(cfg.theme)}";
1808
+
1809
+ const data = await load(Astro.url.pathname);
1810
+ ---
1811
+
1812
+ <html lang="en">
1813
+ <head>
1814
+ <meta charset="utf-8" />
1815
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
1816
+ <title>{data.title} – Docs</title>
1817
+ </head>
1818
+ <body>
1819
+ <DocsLayout tree={data.tree} config={config}>
1820
+ <DocsContent data={data} config={config} />
1821
+ </DocsLayout>
1822
+ <SearchDialog config={config} />
1823
+ </body>
1824
+ </html>
1825
+ `;
1826
+ }
1827
+ function astroApiRouteTemplate(cfg) {
1828
+ return `\
1829
+ import type { APIRoute } from "astro";
1830
+ import { GET as docsGET, POST as docsPOST } from "${astroPageServerImport(cfg.useAlias, 2)}";
1831
+
1832
+ export const GET: APIRoute = async ({ request }) => {
1833
+ return docsGET({ request });
1834
+ };
1835
+
1836
+ export const POST: APIRoute = async ({ request }) => {
1837
+ return docsPOST({ request });
1838
+ };
1839
+ `;
1840
+ }
1841
+ function astroDocsMiddlewareTemplate(filePath, useAlias) {
1842
+ const serverImport = astroRouteServerImport(filePath, useAlias);
1843
+ return `\
1844
+ import { isDocsLlmsTxtPublicRequest, isDocsMcpRequest, isDocsPublicGetRequest } from "@farming-labs/docs";
1845
+ import type { MiddlewareHandler } from "astro";
1846
+ import config from "${astroRouteConfigImport(filePath, useAlias)}";
1847
+ import { GET, MCP } from "${serverImport}";
1848
+
1849
+ const docsEntry = config.entry ?? "docs";
1850
+
1851
+ export const onRequest: MiddlewareHandler = async (context, next) => {
1852
+ const method = context.request.method.toUpperCase();
1853
+
1854
+ if (isDocsMcpRequest(context.url, config.mcp)) {
1855
+ if (method === "OPTIONS") return MCP.OPTIONS({ request: context.request });
1856
+ if (method === "POST") return MCP.POST({ request: context.request });
1857
+ if (method === "DELETE") return MCP.DELETE({ request: context.request });
1858
+ if (method === "GET" || method === "HEAD") return MCP.GET({ request: context.request });
1859
+ return new Response("Method Not Allowed", {
1860
+ status: 405,
1861
+ headers: { Allow: "GET, HEAD, POST, DELETE, OPTIONS" },
1862
+ });
1863
+ }
1864
+
1865
+ if ((method === "GET" || method === "HEAD") && isDocsLlmsTxtPublicRequest(context.url, config.llmsTxt, docsEntry)) {
1866
+ const nativeResponse = await next();
1867
+ if (nativeResponse.status !== 404) return nativeResponse;
1868
+ }
1869
+
1870
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, context.url, context.request, { sitemap: config.sitemap, llms: config.llmsTxt, robots: config.robots })) {
1871
+ return GET({ request: context.request });
1872
+ }
1873
+
1874
+ return next();
1875
+ };
1876
+ `;
1877
+ }
1878
+ function injectAstroDocsMiddleware(content, filePath, useAlias) {
1879
+ if (content.includes("isDocsPublicGetRequest") || content.includes("docsPublicMiddleware")) return null;
1880
+ if (/export\s*{\s*onRequest\b/.test(content)) return null;
1881
+ const serverImport = astroRouteServerImport(filePath, useAlias);
1882
+ const configImport = astroRouteConfigImport(filePath, useAlias);
1883
+ let next = content.trimEnd();
1884
+ let hasExistingMiddleware = false;
1885
+ for (const [pattern, replacement] of [
1886
+ [/\bexport\s+const\s+onRequest(\s*:\s*[^=]+)?\s*=/, "const existingOnRequest$1 ="],
1887
+ [/\bexport\s+async\s+function\s+onRequest\b/, "async function existingOnRequest"],
1888
+ [/\bexport\s+function\s+onRequest\b/, "function existingOnRequest"]
1889
+ ]) if (pattern.test(next)) {
1890
+ next = next.replace(pattern, replacement);
1891
+ next = next.replace(/const existingOnRequest(:[^=\n]*?)\s+=/, "const existingOnRequest$1 =");
1892
+ hasExistingMiddleware = true;
1893
+ break;
1894
+ }
1895
+ const imports = [
1896
+ "import { isDocsLlmsTxtPublicRequest, isDocsMcpRequest, isDocsPublicGetRequest } from \"@farming-labs/docs\";",
1897
+ ...next.includes("MiddlewareHandler") ? [] : ["import type { MiddlewareHandler } from \"astro\";"],
1898
+ ...hasExistingMiddleware && !next.includes("sequence") ? ["import { sequence } from \"astro:middleware\";"] : [],
1899
+ `import docsConfig from "${configImport}";`,
1900
+ `import { GET as docsGET, MCP as docsMCP } from "${serverImport}";`
1901
+ ];
1902
+ const docsMiddleware = `\
1903
+ const docsEntry = docsConfig.entry ?? "docs";
1904
+
1905
+ const docsPublicMiddleware: MiddlewareHandler = async (context, next) => {
1906
+ const method = context.request.method.toUpperCase();
1907
+
1908
+ if (isDocsMcpRequest(context.url, docsConfig.mcp)) {
1909
+ if (method === "OPTIONS") return docsMCP.OPTIONS({ request: context.request });
1910
+ if (method === "POST") return docsMCP.POST({ request: context.request });
1911
+ if (method === "DELETE") return docsMCP.DELETE({ request: context.request });
1912
+ if (method === "GET" || method === "HEAD") return docsMCP.GET({ request: context.request });
1913
+ return new Response("Method Not Allowed", {
1914
+ status: 405,
1915
+ headers: { Allow: "GET, HEAD, POST, DELETE, OPTIONS" },
1916
+ });
1917
+ }
1918
+
1919
+ if ((method === "GET" || method === "HEAD") && isDocsLlmsTxtPublicRequest(context.url, docsConfig.llmsTxt, docsEntry)) {
1920
+ const nativeResponse = await next();
1921
+ if (nativeResponse.status !== 404) return nativeResponse;
1922
+ }
1923
+
1924
+ if ((method === "GET" || method === "HEAD") && isDocsPublicGetRequest(docsEntry, context.url, context.request, { sitemap: docsConfig.sitemap, llms: docsConfig.llmsTxt, robots: docsConfig.robots })) {
1925
+ return docsGET({ request: context.request });
1926
+ }
1927
+
1928
+ return next();
1929
+ };`;
1930
+ const exportLine = hasExistingMiddleware ? "export const onRequest = sequence(docsPublicMiddleware, existingOnRequest);" : "export const onRequest = docsPublicMiddleware;";
1931
+ return `${imports.join("\n")}\n${next}\n\n${docsMiddleware}\n\n${exportLine}\n`;
1932
+ }
1933
+ function astroApiReferenceRouteTemplate(filePath) {
1934
+ return `\
1935
+ import { createAstroApiReference } from "@farming-labs/astro/api-reference";
1936
+ import config from "${relativeImport(filePath, "src/lib/docs.config.ts")}";
1937
+
1938
+ export const GET = createAstroApiReference(config);
1939
+ `;
1940
+ }
1941
+ function astroGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
1942
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
1943
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
1944
+ `;
1945
+ return `\
1946
+ @import "${getThemeCssImport(theme)}";
1947
+ `;
1948
+ }
1949
+ function astroCssImportLine(theme, customThemeName, globalCssRelPath) {
1950
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
1951
+ return `@import "${getThemeCssImport(theme)}";`;
1952
+ }
1953
+ function injectAstroCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
1954
+ const importLine = astroCssImportLine(theme, customThemeName, globalCssRelPath);
1955
+ if (existingContent.includes(importLine)) return null;
1956
+ if (theme !== "custom" && hasBuiltInThemeCssImport(existingContent)) return null;
1957
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
1958
+ const lines = existingContent.split("\n");
1959
+ const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
1960
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
1961
+ else lines.unshift(importLine);
1962
+ return lines.join("\n");
1963
+ }
1964
+ function astroWelcomePageTemplate(cfg) {
1965
+ return `\
1966
+ ---
1967
+ title: "Documentation"
1968
+ description: "Welcome to ${cfg.projectName} documentation"
1969
+ ---
1970
+
1971
+ # Welcome to ${cfg.projectName}
1972
+
1973
+ Get started with our documentation. Browse the pages on the left to learn more.
1974
+
1975
+ ## Overview
1976
+
1977
+ This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
1978
+
1979
+ ## Features
1980
+
1981
+ - **Markdown Support** — Write docs with standard Markdown
1982
+ - **Syntax Highlighting** — Code blocks with automatic highlighting
1983
+ - **Dark Mode** — Built-in theme switching
1984
+ - **Search** — Full-text search across all pages
1985
+ - **Responsive** — Works on any screen size
1986
+
1987
+ ---
1988
+
1989
+ ## Next Steps
1990
+
1991
+ Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
1992
+ `;
1993
+ }
1994
+ function astroInstallationPageTemplate(cfg) {
1995
+ const t = getThemeInfo(cfg.theme);
1996
+ return `\
1997
+ ---
1998
+ title: "Installation"
1999
+ description: "How to install and set up ${cfg.projectName}"
2000
+ ---
2001
+
2002
+ # Installation
2003
+
2004
+ Follow these steps to install and configure ${cfg.projectName}.
2005
+
2006
+ ## Prerequisites
2007
+
2008
+ - Node.js 18+
2009
+ - A package manager (pnpm, npm, or yarn)
2010
+
2011
+ ## Install Dependencies
2012
+
2013
+ \\\`\\\`\\\`bash
2014
+ pnpm add @farming-labs/docs @farming-labs/astro @farming-labs/astro-theme @farming-labs/theme
2015
+ \\\`\\\`\\\`
2016
+
2017
+ ## Configuration
2018
+
2019
+ Your project includes a \\\`docs.config.ts\\\` in \\\`src/lib/\\\`:
2020
+
2021
+ \\\`\\\`\\\`ts title="src/lib/docs.config.ts"
2022
+ import { defineDocs } from "@farming-labs/docs";
2023
+ import { ${t.factory} } from "${t.astroImport}";
2024
+
2025
+ export default defineDocs({
2026
+ entry: "${cfg.entry}",
2027
+ contentDir: "${cfg.entry}",
2028
+ theme: ${t.factory}({
2029
+ ui: { colors: { primary: "#6366f1" } },
2030
+ }),
2031
+ });
2032
+ \\\`\\\`\\\`
2033
+
2034
+ ## Project Structure
2035
+
2036
+ \\\`\\\`\\\`
2037
+ ${cfg.entry}/ # Markdown content
2038
+ page.md # /${cfg.entry}
2039
+ installation/
2040
+ page.md # /${cfg.entry}/installation
2041
+ quickstart/
2042
+ page.md # /${cfg.entry}/quickstart
2043
+ src/
2044
+ middleware.ts # Public docs aliases
2045
+ lib/
2046
+ docs.config.ts # Docs configuration
2047
+ docs.server.ts # Server-side docs loader
2048
+ pages/
2049
+ ${cfg.entry}/
2050
+ index.astro # Docs index page
2051
+ [...slug].astro # Dynamic doc page
2052
+ api/
2053
+ ${cfg.entry}.ts # Search/AI API route
2054
+ \\\`\\\`\\\`
2055
+
2056
+ ## What's Next?
2057
+
2058
+ Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
2059
+ `;
2060
+ }
2061
+ function astroQuickstartPageTemplate(cfg) {
2062
+ const t = getThemeInfo(cfg.theme);
2063
+ return `\
2064
+ ---
2065
+ title: "Quickstart"
2066
+ description: "Get up and running in minutes"
2067
+ ---
2068
+
2069
+ # Quickstart
2070
+
2071
+ This guide walks you through creating your first documentation page.
2072
+
2073
+ ## Creating a Page
2074
+
2075
+ Create a new folder under \\\`${cfg.entry}/\\\` with a \\\`page.md\\\` file:
2076
+
2077
+ \\\`\\\`\\\`bash
2078
+ mkdir -p ${cfg.entry}/my-page
2079
+ \\\`\\\`\\\`
2080
+
2081
+ Then create \\\`${cfg.entry}/my-page/page.md\\\`:
2082
+
2083
+ \\\`\\\`\\\`md
2084
+ ---
2085
+ title: "My Page"
2086
+ description: "A custom documentation page"
2087
+ ---
2088
+
2089
+ # My Page
2090
+
2091
+ Write your content here using **Markdown**.
2092
+ \\\`\\\`\\\`
2093
+
2094
+ Your page is now available at \\\`/${cfg.entry}/my-page\\\`.
2095
+
2096
+ ## Customizing the Theme
2097
+
2098
+ Edit \\\`src/lib/docs.config.ts\\\` to change colors, typography, and component defaults:
2099
+
2100
+ \\\`\\\`\\\`ts title="src/lib/docs.config.ts"
2101
+ theme: ${t.factory}({
2102
+ ui: {
2103
+ colors: { primary: "#22c55e" },
2104
+ },
2105
+ }),
2106
+ \\\`\\\`\\\`
2107
+
2108
+ ## Deploying
2109
+
2110
+ Build your docs for production:
2111
+
2112
+ \\\`\\\`\\\`bash
2113
+ pnpm build
2114
+ \\\`\\\`\\\`
2115
+
2116
+ Deploy to Vercel, Netlify, or any Node.js hosting platform.
2117
+ `;
2118
+ }
2119
+ function nuxtDocsConfigTemplate(cfg) {
2120
+ if (cfg.theme === "custom" && cfg.customThemeName) {
2121
+ const exportName = getThemeExportName(cfg.customThemeName);
2122
+ return `\
2123
+ import { defineDocs } from "@farming-labs/docs";
2124
+ import { ${exportName} } from "${cfg.useAlias ? "~/themes/" + cfg.customThemeName.replace(/\.ts$/i, "") : "./themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
2125
+
2126
+ export default defineDocs({
2127
+ entry: "${cfg.entry}",
2128
+ contentDir: "${cfg.entry}",
2129
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${exportName}({
2130
+ ui: {
2131
+ colors: { primary: "#6366f1" },
2132
+ },
2133
+ }),
2134
+
2135
+ nav: {
2136
+ title: "${cfg.projectName}",
2137
+ url: "/${cfg.entry}",
2138
+ },
2139
+
2140
+ breadcrumb: { enabled: true },
2141
+
2142
+ metadata: {
2143
+ titleTemplate: "%s – ${cfg.projectName}",
2144
+ description: "Documentation for ${cfg.projectName}",
2145
+ },
2146
+ });
2147
+ `;
2148
+ }
2149
+ const t = getThemeInfo(cfg.theme);
2150
+ return `\
2151
+ import { defineDocs } from "@farming-labs/docs";
2152
+ import { ${t.factory} } from "${t.nuxtImport}";
2153
+
2154
+ export default defineDocs({
2155
+ entry: "${cfg.entry}",
2156
+ contentDir: "${cfg.entry}",
2157
+ ${renderI18nConfig(cfg)}${renderApiReferenceConfig(cfg)} theme: ${t.factory}({
2158
+ ui: {
2159
+ colors: { primary: "#6366f1" },
2160
+ },
2161
+ }),
2162
+
2163
+ nav: {
2164
+ title: "${cfg.projectName}",
2165
+ url: "/${cfg.entry}",
2166
+ },
2167
+
2168
+ breadcrumb: { enabled: true },
2169
+
2170
+ metadata: {
2171
+ titleTemplate: "%s – ${cfg.projectName}",
2172
+ description: "Documentation for ${cfg.projectName}",
2173
+ },
2174
+ });
2175
+ `;
2176
+ }
2177
+ function nuxtServerApiDocsRouteTemplate(cfg) {
2178
+ return `\
2179
+ import { defineDocsHandler } from "@farming-labs/nuxt/server";
2180
+ import { bundledAgentSkills } from "@farming-labs/docs/agent-skills-bundle";
2181
+ import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
2182
+
2183
+ export default defineDocsHandler(
2184
+ { ...config, _preloadedAgentSkills: bundledAgentSkills },
2185
+ useStorage,
2186
+ );
2187
+ `;
2188
+ }
2189
+ function nuxtServerDocsPublicMiddlewareTemplate(cfg) {
2190
+ return `\
2191
+ import { defineDocsPublicHandler } from "@farming-labs/nuxt/server";
2192
+ import { bundledAgentSkills } from "@farming-labs/docs/agent-skills-bundle";
2193
+ import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
2194
+
2195
+ export default defineDocsPublicHandler(
2196
+ { ...config, _preloadedAgentSkills: bundledAgentSkills },
2197
+ useStorage,
2198
+ );
2199
+ `;
2200
+ }
2201
+ function nuxtServerApiReferenceRouteTemplate(filePath, useAlias) {
2202
+ return `\
2203
+ import { defineApiReferenceHandler } from "@farming-labs/nuxt/api-reference";
2204
+ import config from "${useAlias ? "~/docs.config" : relativeImport(filePath, "docs.config.ts")}";
2205
+
2206
+ export default defineApiReferenceHandler(config);
2207
+ `;
2208
+ }
2209
+ function nuxtDocsPageTemplate(cfg) {
2210
+ return `\
2211
+ <script setup lang="ts">
2212
+ import { DocsLayout, DocsContent } from "@farming-labs/nuxt-theme";
2213
+ import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
2214
+
2215
+ const route = useRoute();
2216
+ const pathname = computed(() => route.path);
2217
+
2218
+ const { data, error } = await useAsyncData(\`docs-\${pathname.value}\`, () =>
2219
+ $fetch("/api/docs", {
2220
+ query: { pathname: pathname.value },
2221
+ })
2222
+ );
2223
+
2224
+ if (error.value) {
2225
+ throw createError({
2226
+ statusCode: 404,
2227
+ statusMessage: "Page not found",
2228
+ });
2229
+ }
2230
+ <\/script>
2231
+
2232
+ <template>
2233
+ <div v-if="data" class="fd-docs-wrapper">
2234
+ <DocsLayout :tree="data.tree" :config="config">
2235
+ <DocsContent :data="data" :config="config" />
2236
+ </DocsLayout>
2237
+ </div>
2238
+ </template>
2239
+ `;
2240
+ }
2241
+ function nuxtConfigTemplate(cfg) {
2242
+ return `\
2243
+ import { docsAgentSkills } from "@farming-labs/docs/vite";
2244
+ import docsConfig from "./docs.config";
2245
+
2246
+ export default defineNuxtConfig({
2247
+ compatibilityDate: "2024-11-01",
2248
+
2249
+ css: ["${getThemeCssImport(cfg.theme)}"],
2250
+
2251
+ vite: {
2252
+ optimizeDeps: {
2253
+ include: ["@farming-labs/docs", "@farming-labs/nuxt", "@farming-labs/nuxt-theme", "@farming-labs/theme"],
2254
+ },
2255
+ },
2256
+
2257
+ nitro: {
2258
+ moduleSideEffects: ["@farming-labs/nuxt/server"],
2259
+ serverAssets: [{ baseName: "${cfg.entry}", dir: "${cfg.entry}" }],
2260
+ rollupConfig: { plugins: [docsAgentSkills(docsConfig)] },
2261
+ },
2262
+ });
2263
+ `;
2264
+ }
2265
+ /** Add the Agent Skills snapshot plugin to Nitro's server Rollup build. */
2266
+ function injectNuxtAgentSkillsPlugin(content) {
2267
+ if (!content) return null;
2268
+ let out = addImportLine(content, "import { docsAgentSkills } from \"@farming-labs/docs/vite\";");
2269
+ out = addImportLine(out, "import docsConfig from \"./docs.config\";");
2270
+ if (out.includes("docsAgentSkills(docsConfig)")) return out === content ? null : out;
2271
+ const rollupPlugins = out.match(/(rollupConfig\s*:\s*\{[\s\S]*?plugins\s*:\s*\[)([\s\S]*?)(\])/m);
2272
+ if (rollupPlugins) {
2273
+ const current = rollupPlugins[2].trim();
2274
+ const next = `${current}${current && !current.endsWith(",") ? "," : ""}${current ? " " : ""}docsAgentSkills(docsConfig)`;
2275
+ return out.replace(rollupPlugins[0], `${rollupPlugins[1]}${next}${rollupPlugins[3]}`);
2276
+ }
2277
+ const rollupBlock = out.match(/rollupConfig\s*:\s*\{/);
2278
+ if (rollupBlock) return out.replace(rollupBlock[0], `${rollupBlock[0]}\n plugins: [docsAgentSkills(docsConfig)],`);
2279
+ const nitroBlock = out.match(/nitro\s*:\s*\{/);
2280
+ if (nitroBlock) return out.replace(nitroBlock[0], `${nitroBlock[0]}\n rollupConfig: { plugins: [docsAgentSkills(docsConfig)] },`);
2281
+ const configMatch = out.match(/defineNuxtConfig\(\s*\{/);
2282
+ if (!configMatch) return null;
2283
+ return out.replace(configMatch[0], "defineNuxtConfig({\n nitro: { rollupConfig: { plugins: [docsAgentSkills(docsConfig)] } },");
2284
+ }
2285
+ function nuxtWelcomePageTemplate(cfg) {
2286
+ return `\
2287
+ ---
2288
+ order: 1
2289
+ title: Documentation
2290
+ description: Welcome to ${cfg.projectName} documentation
2291
+ icon: book
2292
+ ---
2293
+
2294
+ # Welcome to ${cfg.projectName}
2295
+
2296
+ Get started with our documentation. Browse the pages on the left to learn more.
2297
+
2298
+ ## Overview
2299
+
2300
+ This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
2301
+
2302
+ ## Features
2303
+
2304
+ - **Markdown Support** — Write docs with standard Markdown
2305
+ - **Syntax Highlighting** — Code blocks with automatic highlighting
2306
+ - **Dark Mode** — Built-in theme switching
2307
+ - **Search** — Full-text search across all pages (⌘ K)
2308
+ - **Responsive** — Works on any screen size
2309
+
2310
+ ---
2311
+
2312
+ ## Next Steps
2313
+
2314
+ Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
2315
+ `;
2316
+ }
2317
+ function nuxtInstallationPageTemplate(cfg) {
2318
+ const t = getThemeInfo(cfg.theme);
2319
+ return `\
2320
+ ---
2321
+ order: 3
2322
+ title: Installation
2323
+ description: How to install and set up ${cfg.projectName}
2324
+ icon: terminal
2325
+ ---
2326
+
2327
+ # Installation
2328
+
2329
+ Follow these steps to install and configure ${cfg.projectName}.
2330
+
2331
+ ## Prerequisites
2332
+
2333
+ - Node.js 18+
2334
+ - A package manager (pnpm, npm, or yarn)
2335
+
2336
+ ## Install Dependencies
2337
+
2338
+ \`\`\`bash
2339
+ pnpm add @farming-labs/docs @farming-labs/nuxt @farming-labs/nuxt-theme @farming-labs/theme
2340
+ \`\`\`
2341
+
2342
+ ## Configuration
2343
+
2344
+ Your project includes a \`docs.config.ts\` at the root:
2345
+
2346
+ \`\`\`ts title="docs.config.ts"
2347
+ import { defineDocs } from "@farming-labs/docs";
2348
+ import { ${t.factory} } from "${t.nuxtImport}";
2349
+
2350
+ export default defineDocs({
2351
+ entry: "${cfg.entry}",
2352
+ contentDir: "${cfg.entry}",
2353
+ theme: ${t.factory}({
2354
+ ui: { colors: { primary: "#6366f1" } },
2355
+ }),
2356
+ });
2357
+ \`\`\`
2358
+
2359
+ ## Project Structure
2360
+
2361
+ \`\`\`
2362
+ ${cfg.entry}/ # Markdown content
2363
+ page.md
2364
+ installation/page.md
2365
+ quickstart/page.md
2366
+ server/
2367
+ api/docs.ts # Page data, search, and AI chat API
2368
+ middleware/docs-public.ts # llms.txt, sitemap, .well-known, .md, and MCP aliases
2369
+ pages/
2370
+ ${cfg.entry}/[[...slug]].vue # Docs catch-all page
2371
+ docs.config.ts
2372
+ nuxt.config.ts
2373
+ \`\`\`
2374
+
2375
+ ## What's Next?
2376
+
2377
+ Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
2378
+ `;
2379
+ }
2380
+ function nuxtQuickstartPageTemplate(cfg) {
2381
+ const t = getThemeInfo(cfg.theme);
2382
+ return `\
2383
+ ---
2384
+ order: 2
2385
+ title: Quickstart
2386
+ description: Get up and running in minutes
2387
+ icon: rocket
2388
+ ---
2389
+
2390
+ # Quickstart
2391
+
2392
+ This guide walks you through creating your first documentation page.
2393
+
2394
+ ## Creating a Page
2395
+
2396
+ Create a new folder under \`${cfg.entry}/\` with a \`page.md\` file:
2397
+
2398
+ \`\`\`bash
2399
+ mkdir -p ${cfg.entry}/my-page
2400
+ \`\`\`
2401
+
2402
+ Then create \`${cfg.entry}/my-page/page.md\`:
2403
+
2404
+ \`\`\`md
2405
+ ---
2406
+ title: "My Page"
2407
+ description: "A custom documentation page"
2408
+ ---
2409
+
2410
+ # My Page
2411
+
2412
+ Write your content here using **Markdown**.
2413
+ \`\`\`
2414
+
2415
+ Your page is now available at \`/${cfg.entry}/my-page\`.
2416
+
2417
+ ## Customizing the Theme
2418
+
2419
+ Edit \`docs.config.ts\` to change colors and typography:
2420
+
2421
+ \`\`\`ts
2422
+ theme: ${t.factory}({
2423
+ ui: {
2424
+ colors: { primary: "#22c55e" },
2425
+ },
2426
+ }),
2427
+ \`\`\`
2428
+
2429
+ ## Deploying
2430
+
2431
+ Build your docs for production:
2432
+
2433
+ \`\`\`bash
2434
+ pnpm build
2435
+ \`\`\`
2436
+
2437
+ Deploy to Vercel, Netlify, or any Node.js hosting platform.
2438
+ `;
2439
+ }
2440
+ function nuxtGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
2441
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
2442
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
2443
+ `;
2444
+ return `\
2445
+ @import "${getThemeCssImport(theme)}";
2446
+ `;
2447
+ }
2448
+ function nuxtCssImportLine(theme, customThemeName, globalCssRelPath) {
2449
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
2450
+ return `@import "${getThemeCssImport(theme)}";`;
2451
+ }
2452
+ function injectNuxtCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
2453
+ const importLine = nuxtCssImportLine(theme, customThemeName, globalCssRelPath);
2454
+ if (existingContent.includes(importLine)) return null;
2455
+ if (theme !== "custom" && hasBuiltInThemeCssImport(existingContent)) return null;
2456
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
2457
+ const lines = existingContent.split("\n");
2458
+ const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
2459
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
2460
+ else lines.unshift(importLine);
2461
+ return lines.join("\n");
2462
+ }
2463
+
2464
+ //#endregion
2465
+ export { svelteDocsLayoutServerTemplate as $, injectTanstackVitePlugins as A, nuxtGlobalCssTemplate as B, injectDocsAgentSkillsVitePlugin as C, tsconfigTemplate as Ct, injectSvelteCssImport as D, injectRootProviderIntoLayout as E, nextLocaleDocPageTemplate as F, nuxtServerDocsPublicMiddlewareTemplate as G, nuxtQuickstartPageTemplate as H, nextLocalizedPageTemplate as I, quickstartPageTemplate as J, nuxtWelcomePageTemplate as K, nuxtConfigTemplate as L, nextApiReferencePageTemplate as M, nextConfigMergedTemplate as N, injectSvelteDocsPublicHook as O, nextConfigTemplate as P, svelteDocsConfigTemplate as Q, nuxtDocsConfigTemplate as R, injectCssImport as S, tanstackWelcomePageTemplate as St, injectNuxtCssImport as T, nuxtServerApiDocsRouteTemplate as U, nuxtInstallationPageTemplate as V, nuxtServerApiReferenceRouteTemplate as W, svelteApiReferenceRouteTemplate as X, rootLayoutTemplate as Y, svelteDocsApiRouteTemplate as Z, getAstroAdapterPkg as _, tanstackDocsServerTemplate as _t, astroDocsIndexTemplate as a, svelteInstallationPageTemplate as at, injectAstroCssImport as b, tanstackRootRouteTemplate as bt, astroDocsServerTemplate as c, svelteViteConfigTemplate as ct, astroQuickstartPageTemplate as d, tanstackApiReferenceRouteTemplate as dt, svelteDocsLayoutTemplate as et, astroWelcomePageTemplate as f, tanstackDocsCatchAllRouteTemplate as ft, docsLayoutTemplate as g, tanstackDocsPublicRouteTemplate as gt, docsConfigTemplate as h, tanstackDocsIndexRouteTemplate as ht, astroDocsConfigTemplate as i, svelteGlobalCssTemplate as it, installationPageTemplate as j, injectTanstackRootProviderIntoRoute as k, astroGlobalCssTemplate as l, svelteWelcomePageTemplate as lt, customThemeTsTemplate as m, tanstackDocsFunctionsTemplate as mt, astroApiRouteTemplate as n, svelteDocsPublicHookTemplate as nt, astroDocsMiddlewareTemplate as o, svelteQuickstartPageTemplate as ot, customThemeCssTemplate as p, tanstackDocsConfigTemplate as pt, postcssConfigTemplate as q, astroConfigTemplate as r, svelteDocsServerTemplate as rt, astroDocsPageTemplate as s, svelteRootLayoutTemplate as st, astroApiReferenceRouteTemplate as t, svelteDocsPageTemplate as tt, astroInstallationPageTemplate as u, tanstackApiDocsRouteTemplate as ut, globalCssTemplate as v, tanstackInstallationPageTemplate as vt, injectNuxtAgentSkillsPlugin as w, welcomePageTemplate as wt, injectAstroDocsMiddleware as x, tanstackViteConfigTemplate as xt, injectAstroAgentSkillsPlugin as y, tanstackQuickstartPageTemplate as yt, nuxtDocsPageTemplate as z };