@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.
- package/dist/agent-CQTH7NFu.mjs +624 -0
- package/dist/agent-DKKptIgy.mjs +4365 -0
- package/dist/agent-evals-B7MIxuEX.mjs +2144 -0
- package/dist/agent-export-CBgWgPvH.mjs +910 -0
- package/dist/agent-scope-C_U--OZ7.mjs +283 -0
- package/dist/agent-skills-bundle.d.mts +13 -0
- package/dist/agent-skills-bundle.mjs +12 -0
- package/dist/agent-skills-server-CPja6Syt.d.mts +14 -0
- package/dist/agent-skills-server-DraIb6FV.mjs +415 -0
- package/dist/agent-skills-vite.d.mts +31 -0
- package/dist/agent-skills-vite.mjs +70 -0
- package/dist/agents-XWZBub6f.mjs +221 -0
- package/dist/analytics-Bx44lg6d.mjs +177 -0
- package/dist/cli/index.d.mts +15 -0
- package/dist/cli/index.mjs +452 -0
- package/dist/client/react.d.mts +45 -0
- package/dist/client/react.mjs +223 -0
- package/dist/cloud-analytics-CSyFE6SS.mjs +132 -0
- package/dist/cloud-ask-ai-sbpjOR2K.mjs +382 -0
- package/dist/cloud-ask-ai-zpwkdwnF.d.mts +23 -0
- package/dist/cloud-pdNC-tyj.mjs +1615 -0
- package/dist/code-blocks-DnNVNK2M.mjs +871 -0
- package/dist/codeblocks-CFuurVIH.mjs +250 -0
- package/dist/config-Wcdj-D0a.mjs +369 -0
- package/dist/dev-Cmy6DtdF.mjs +1333 -0
- package/dist/docs-cloud-server.d.mts +70 -0
- package/dist/docs-cloud-server.mjs +310 -0
- package/dist/doctor-DtGYZ41i.mjs +2036 -0
- package/dist/downgrade-w7e6Se0L.mjs +184 -0
- package/dist/errors-DbOhkE1h.mjs +20 -0
- package/dist/golden-evaluations-Dj-9Eo3v.mjs +1785 -0
- package/dist/i18n-CCaFUnAN.mjs +40 -0
- package/dist/index.d.mts +1150 -0
- package/dist/index.mjs +10 -0
- package/dist/init-CQY0Woe3.mjs +1264 -0
- package/dist/mcp-B9dcsivk.mjs +156 -0
- package/dist/mcp.d.mts +298 -0
- package/dist/mcp.mjs +4430 -0
- package/dist/metadata-DWExHQnx.mjs +237 -0
- package/dist/package-version-n5AFur8a.mjs +128 -0
- package/dist/reading-time-CYZ5VvKU.mjs +742 -0
- package/dist/review-CLoHTywU.mjs +673 -0
- package/dist/robots-BIpC4j4P.mjs +201 -0
- package/dist/robots-CUTahhoY.mjs +179 -0
- package/dist/search-B6V6qtiI.mjs +1826 -0
- package/dist/search-CaSyi6H6.d.mts +279 -0
- package/dist/search-DSjCeOk7.mjs +104 -0
- package/dist/server.d.mts +343 -0
- package/dist/server.mjs +14 -0
- package/dist/sitemap-Cykpe3Tz.mjs +249 -0
- package/dist/sitemap-server-C_6Wes83.mjs +1137 -0
- package/dist/standards-discovery-C4HUqMd2.d.mts +227 -0
- package/dist/standards-discovery-jkykaXq1.mjs +519 -0
- package/dist/templates-Bq_P7ctv.mjs +2465 -0
- package/dist/types-lMBIdZg0.d.mts +3315 -0
- package/dist/upgrade-oz-GChgt.mjs +56 -0
- package/dist/utils-DpiIioYb.mjs +225 -0
- 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 };
|