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