@farming-labs/docs 0.0.15 → 0.0.17
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 +316 -41
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -214,6 +214,49 @@ const THEME_INFO = {
|
|
|
214
214
|
function getThemeInfo(theme) {
|
|
215
215
|
return THEME_INFO[theme] ?? THEME_INFO.fumadocs;
|
|
216
216
|
}
|
|
217
|
+
function getThemeExportName(themeName) {
|
|
218
|
+
const base = themeName.replace(/\.ts$/i, "").trim();
|
|
219
|
+
if (!base) return "customTheme";
|
|
220
|
+
return base.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toLowerCase());
|
|
221
|
+
}
|
|
222
|
+
function getCustomThemeCssImportPath(globalCssRelPath, themeName) {
|
|
223
|
+
if (globalCssRelPath.startsWith("app/")) return `../themes/${themeName}.css`;
|
|
224
|
+
if (globalCssRelPath.startsWith("src/")) return `../../themes/${themeName}.css`;
|
|
225
|
+
return `../themes/${themeName}.css`;
|
|
226
|
+
}
|
|
227
|
+
/** Content for themes/{name}.ts - createTheme with the given name */
|
|
228
|
+
function customThemeTsTemplate(themeName) {
|
|
229
|
+
return `\
|
|
230
|
+
import { createTheme } from "@farming-labs/docs";
|
|
231
|
+
|
|
232
|
+
export const ${getThemeExportName(themeName)} = createTheme({
|
|
233
|
+
name: "${themeName.replace(/\.ts$/i, "")}",
|
|
234
|
+
ui: {
|
|
235
|
+
colors: {
|
|
236
|
+
primary: "#e11d48",
|
|
237
|
+
background: "#09090b",
|
|
238
|
+
foreground: "#fafafa",
|
|
239
|
+
muted: "#71717a",
|
|
240
|
+
border: "#27272a",
|
|
241
|
+
},
|
|
242
|
+
radius: "0.5rem",
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
`;
|
|
246
|
+
}
|
|
247
|
+
function customThemeCssTemplate(themeName) {
|
|
248
|
+
return `\
|
|
249
|
+
/* Custom theme: ${themeName} - edit variables and selectors as needed */
|
|
250
|
+
@import "@farming-labs/theme/presets/black";
|
|
251
|
+
|
|
252
|
+
.dark {
|
|
253
|
+
--color-fd-primary: #e11d48;
|
|
254
|
+
--color-fd-background: #09090b;
|
|
255
|
+
--color-fd-border: #27272a;
|
|
256
|
+
--radius: 0.5rem;
|
|
257
|
+
}
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
217
260
|
/** Config import for Next.js app/layout.tsx → root docs.config */
|
|
218
261
|
function nextRootLayoutConfigImport(useAlias) {
|
|
219
262
|
return useAlias ? "@/docs.config" : "../docs.config";
|
|
@@ -250,6 +293,27 @@ function astroPageServerImport(useAlias, depth) {
|
|
|
250
293
|
return `${"../".repeat(depth)}lib/docs.server`;
|
|
251
294
|
}
|
|
252
295
|
function docsConfigTemplate(cfg) {
|
|
296
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
297
|
+
const exportName = getThemeExportName(cfg.customThemeName);
|
|
298
|
+
return `\
|
|
299
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
300
|
+
import { ${exportName} } from "${cfg.useAlias ? "@/themes/" + cfg.customThemeName.replace(/\.ts$/i, "") : "./themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
|
|
301
|
+
|
|
302
|
+
export default defineDocs({
|
|
303
|
+
entry: "${cfg.entry}",
|
|
304
|
+
theme: ${exportName}({
|
|
305
|
+
ui: {
|
|
306
|
+
colors: { primary: "#6366f1" },
|
|
307
|
+
},
|
|
308
|
+
}),
|
|
309
|
+
|
|
310
|
+
metadata: {
|
|
311
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
312
|
+
description: "Documentation for ${cfg.projectName}",
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
253
317
|
const t = getThemeInfo(cfg.theme);
|
|
254
318
|
return `\
|
|
255
319
|
import { defineDocs } from "@farming-labs/docs";
|
|
@@ -354,16 +418,21 @@ function injectRootProviderIntoLayout(content) {
|
|
|
354
418
|
}
|
|
355
419
|
return out === content ? null : out;
|
|
356
420
|
}
|
|
357
|
-
function globalCssTemplate(theme) {
|
|
421
|
+
function globalCssTemplate(theme, customThemeName, globalCssRelPath) {
|
|
422
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `\
|
|
423
|
+
@import "tailwindcss";
|
|
424
|
+
@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
|
|
425
|
+
`;
|
|
358
426
|
return `\
|
|
359
427
|
@import "tailwindcss";
|
|
360
428
|
@import "@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css";
|
|
361
429
|
`;
|
|
362
430
|
}
|
|
363
|
-
function injectCssImport(existingContent, theme) {
|
|
364
|
-
const importLine = `@import "@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css";`;
|
|
431
|
+
function injectCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
|
|
432
|
+
const importLine = theme === "custom" && customThemeName && globalCssRelPath ? `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";` : `@import "@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css";`;
|
|
365
433
|
if (existingContent.includes(importLine)) return null;
|
|
366
|
-
if (existingContent.includes("@farming-labs/theme/") && existingContent.includes("/css")) return null;
|
|
434
|
+
if (theme !== "custom" && existingContent.includes("@farming-labs/theme/") && existingContent.includes("/css")) return null;
|
|
435
|
+
if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
|
|
367
436
|
const lines = existingContent.split("\n");
|
|
368
437
|
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
369
438
|
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
@@ -602,6 +671,34 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
|
602
671
|
`;
|
|
603
672
|
}
|
|
604
673
|
function svelteDocsConfigTemplate(cfg) {
|
|
674
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
675
|
+
const exportName = getThemeExportName(cfg.customThemeName);
|
|
676
|
+
return `\
|
|
677
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
678
|
+
import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
|
|
679
|
+
|
|
680
|
+
export default defineDocs({
|
|
681
|
+
entry: "${cfg.entry}",
|
|
682
|
+
theme: ${exportName}({
|
|
683
|
+
ui: {
|
|
684
|
+
colors: { primary: "#6366f1" },
|
|
685
|
+
},
|
|
686
|
+
}),
|
|
687
|
+
|
|
688
|
+
nav: {
|
|
689
|
+
title: "${cfg.projectName}",
|
|
690
|
+
url: "/${cfg.entry}",
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
breadcrumb: { enabled: true },
|
|
694
|
+
|
|
695
|
+
metadata: {
|
|
696
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
697
|
+
description: "Documentation for ${cfg.projectName}",
|
|
698
|
+
},
|
|
699
|
+
});
|
|
700
|
+
`;
|
|
701
|
+
}
|
|
605
702
|
const t = getThemeInfo(cfg.theme);
|
|
606
703
|
return `\
|
|
607
704
|
import { defineDocs } from "@farming-labs/docs";
|
|
@@ -692,17 +789,23 @@ function svelteRootLayoutTemplate(globalCssRelPath) {
|
|
|
692
789
|
{@render children()}
|
|
693
790
|
`;
|
|
694
791
|
}
|
|
695
|
-
function svelteGlobalCssTemplate(theme) {
|
|
792
|
+
function svelteGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
|
|
793
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `\
|
|
794
|
+
@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
|
|
795
|
+
`;
|
|
696
796
|
return `\
|
|
697
797
|
@import "@farming-labs/svelte-theme/${theme}/css";
|
|
698
798
|
`;
|
|
699
799
|
}
|
|
700
|
-
function svelteCssImportLine(theme) {
|
|
800
|
+
function svelteCssImportLine(theme, customThemeName, globalCssRelPath) {
|
|
801
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
|
|
701
802
|
return `@import "@farming-labs/svelte-theme/${theme}/css";`;
|
|
702
803
|
}
|
|
703
|
-
function injectSvelteCssImport(existingContent, theme) {
|
|
704
|
-
const importLine = svelteCssImportLine(theme);
|
|
804
|
+
function injectSvelteCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
|
|
805
|
+
const importLine = svelteCssImportLine(theme, customThemeName, globalCssRelPath);
|
|
705
806
|
if (existingContent.includes(importLine)) return null;
|
|
807
|
+
if (theme !== "custom" && existingContent.includes("@farming-labs/svelte-theme/") && existingContent.includes("/css")) return null;
|
|
808
|
+
if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
|
|
706
809
|
const lines = existingContent.split("\n");
|
|
707
810
|
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
708
811
|
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
@@ -876,6 +979,35 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
|
876
979
|
`;
|
|
877
980
|
}
|
|
878
981
|
function astroDocsConfigTemplate(cfg) {
|
|
982
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
983
|
+
const exportName = getThemeExportName(cfg.customThemeName);
|
|
984
|
+
return `\
|
|
985
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
986
|
+
import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
|
|
987
|
+
|
|
988
|
+
export default defineDocs({
|
|
989
|
+
entry: "${cfg.entry}",
|
|
990
|
+
contentDir: "${cfg.entry}",
|
|
991
|
+
theme: ${exportName}({
|
|
992
|
+
ui: {
|
|
993
|
+
colors: { primary: "#6366f1" },
|
|
994
|
+
},
|
|
995
|
+
}),
|
|
996
|
+
|
|
997
|
+
nav: {
|
|
998
|
+
title: "${cfg.projectName}",
|
|
999
|
+
url: "/${cfg.entry}",
|
|
1000
|
+
},
|
|
1001
|
+
|
|
1002
|
+
breadcrumb: { enabled: true },
|
|
1003
|
+
|
|
1004
|
+
metadata: {
|
|
1005
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
1006
|
+
description: "Documentation for ${cfg.projectName}",
|
|
1007
|
+
},
|
|
1008
|
+
});
|
|
1009
|
+
`;
|
|
1010
|
+
}
|
|
879
1011
|
const t = getThemeInfo(cfg.theme);
|
|
880
1012
|
return `\
|
|
881
1013
|
import { defineDocs } from "@farming-labs/docs";
|
|
@@ -1025,17 +1157,23 @@ export const POST: APIRoute = async ({ request }) => {
|
|
|
1025
1157
|
};
|
|
1026
1158
|
`;
|
|
1027
1159
|
}
|
|
1028
|
-
function astroGlobalCssTemplate(theme) {
|
|
1160
|
+
function astroGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
|
|
1161
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `\
|
|
1162
|
+
@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
|
|
1163
|
+
`;
|
|
1029
1164
|
return `\
|
|
1030
1165
|
@import "@farming-labs/astro-theme/${theme}/css";
|
|
1031
1166
|
`;
|
|
1032
1167
|
}
|
|
1033
|
-
function astroCssImportLine(theme) {
|
|
1168
|
+
function astroCssImportLine(theme, customThemeName, globalCssRelPath) {
|
|
1169
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
|
|
1034
1170
|
return `@import "@farming-labs/astro-theme/${theme}/css";`;
|
|
1035
1171
|
}
|
|
1036
|
-
function injectAstroCssImport(existingContent, theme) {
|
|
1037
|
-
const importLine = astroCssImportLine(theme);
|
|
1172
|
+
function injectAstroCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
|
|
1173
|
+
const importLine = astroCssImportLine(theme, customThemeName, globalCssRelPath);
|
|
1038
1174
|
if (existingContent.includes(importLine)) return null;
|
|
1175
|
+
if (theme !== "custom" && existingContent.includes("@farming-labs/astro-theme/") && existingContent.includes("/css")) return null;
|
|
1176
|
+
if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
|
|
1039
1177
|
const lines = existingContent.split("\n");
|
|
1040
1178
|
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
1041
1179
|
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
@@ -1197,6 +1335,35 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
|
1197
1335
|
`;
|
|
1198
1336
|
}
|
|
1199
1337
|
function nuxtDocsConfigTemplate(cfg) {
|
|
1338
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
1339
|
+
const exportName = getThemeExportName(cfg.customThemeName);
|
|
1340
|
+
return `\
|
|
1341
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
1342
|
+
import { ${exportName} } from "${cfg.useAlias ? "~/themes/" + cfg.customThemeName.replace(/\.ts$/i, "") : "./themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
|
|
1343
|
+
|
|
1344
|
+
export default defineDocs({
|
|
1345
|
+
entry: "${cfg.entry}",
|
|
1346
|
+
contentDir: "${cfg.entry}",
|
|
1347
|
+
theme: ${exportName}({
|
|
1348
|
+
ui: {
|
|
1349
|
+
colors: { primary: "#6366f1" },
|
|
1350
|
+
},
|
|
1351
|
+
}),
|
|
1352
|
+
|
|
1353
|
+
nav: {
|
|
1354
|
+
title: "${cfg.projectName}",
|
|
1355
|
+
url: "/${cfg.entry}",
|
|
1356
|
+
},
|
|
1357
|
+
|
|
1358
|
+
breadcrumb: { enabled: true },
|
|
1359
|
+
|
|
1360
|
+
metadata: {
|
|
1361
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
1362
|
+
description: "Documentation for ${cfg.projectName}",
|
|
1363
|
+
},
|
|
1364
|
+
});
|
|
1365
|
+
`;
|
|
1366
|
+
}
|
|
1200
1367
|
const t = getThemeInfo(cfg.theme);
|
|
1201
1368
|
return `\
|
|
1202
1369
|
import { defineDocs } from "@farming-labs/docs";
|
|
@@ -1496,17 +1663,23 @@ pnpm build
|
|
|
1496
1663
|
Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
1497
1664
|
`;
|
|
1498
1665
|
}
|
|
1499
|
-
function nuxtGlobalCssTemplate(theme) {
|
|
1666
|
+
function nuxtGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
|
|
1667
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `\
|
|
1668
|
+
@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
|
|
1669
|
+
`;
|
|
1500
1670
|
return `\
|
|
1501
1671
|
@import "@farming-labs/nuxt-theme/${theme}/css";
|
|
1502
1672
|
`;
|
|
1503
1673
|
}
|
|
1504
|
-
function nuxtCssImportLine(theme) {
|
|
1674
|
+
function nuxtCssImportLine(theme, customThemeName, globalCssRelPath) {
|
|
1675
|
+
if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
|
|
1505
1676
|
return `@import "@farming-labs/nuxt-theme/${theme}/css";`;
|
|
1506
1677
|
}
|
|
1507
|
-
function injectNuxtCssImport(existingContent, theme) {
|
|
1508
|
-
const importLine = nuxtCssImportLine(theme);
|
|
1678
|
+
function injectNuxtCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
|
|
1679
|
+
const importLine = nuxtCssImportLine(theme, customThemeName, globalCssRelPath);
|
|
1509
1680
|
if (existingContent.includes(importLine)) return null;
|
|
1681
|
+
if (theme !== "custom" && existingContent.includes("@farming-labs/nuxt-theme/") && existingContent.includes("/css")) return null;
|
|
1682
|
+
if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
|
|
1510
1683
|
const lines = existingContent.split("\n");
|
|
1511
1684
|
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
1512
1685
|
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
@@ -1526,11 +1699,65 @@ const VALID_TEMPLATES = [
|
|
|
1526
1699
|
async function init(options = {}) {
|
|
1527
1700
|
const cwd = process.cwd();
|
|
1528
1701
|
p.intro(pc.bgCyan(pc.black(" @farming-labs/docs ")));
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1702
|
+
let projectType = "existing";
|
|
1703
|
+
if (!options.template) {
|
|
1704
|
+
const projectTypeAnswer = await p.select({
|
|
1705
|
+
message: "Are you adding docs to an existing project or starting fresh?",
|
|
1706
|
+
options: [{
|
|
1707
|
+
value: "existing",
|
|
1708
|
+
label: "Existing project",
|
|
1709
|
+
hint: "Add docs to the current app in this directory"
|
|
1710
|
+
}, {
|
|
1711
|
+
value: "fresh",
|
|
1712
|
+
label: "Fresh project",
|
|
1713
|
+
hint: "Bootstrap a new app from a template (Next, Nuxt, SvelteKit, Astro)"
|
|
1714
|
+
}]
|
|
1715
|
+
});
|
|
1716
|
+
if (p.isCancel(projectTypeAnswer)) {
|
|
1717
|
+
p.outro(pc.red("Init cancelled."));
|
|
1718
|
+
process.exit(0);
|
|
1719
|
+
}
|
|
1720
|
+
projectType = projectTypeAnswer;
|
|
1721
|
+
}
|
|
1722
|
+
if (projectType === "fresh" || options.template) {
|
|
1723
|
+
let template;
|
|
1724
|
+
if (options.template) {
|
|
1725
|
+
template = options.template.toLowerCase();
|
|
1726
|
+
if (!VALID_TEMPLATES.includes(template)) {
|
|
1727
|
+
p.log.error(`Invalid ${pc.cyan("--template")}. Use one of: ${VALID_TEMPLATES.map((t) => pc.cyan(t)).join(", ")}`);
|
|
1728
|
+
process.exit(1);
|
|
1729
|
+
}
|
|
1730
|
+
} else {
|
|
1731
|
+
const templateAnswer = await p.select({
|
|
1732
|
+
message: "Which framework would you like to use?",
|
|
1733
|
+
options: [
|
|
1734
|
+
{
|
|
1735
|
+
value: "next",
|
|
1736
|
+
label: "Next.js",
|
|
1737
|
+
hint: "React with App Router"
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
value: "nuxt",
|
|
1741
|
+
label: "Nuxt",
|
|
1742
|
+
hint: "Vue 3 with file-based routing"
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
value: "sveltekit",
|
|
1746
|
+
label: "SvelteKit",
|
|
1747
|
+
hint: "Svelte with file-based routing"
|
|
1748
|
+
},
|
|
1749
|
+
{
|
|
1750
|
+
value: "astro",
|
|
1751
|
+
label: "Astro",
|
|
1752
|
+
hint: "Content-focused with islands"
|
|
1753
|
+
}
|
|
1754
|
+
]
|
|
1755
|
+
});
|
|
1756
|
+
if (p.isCancel(templateAnswer)) {
|
|
1757
|
+
p.outro(pc.red("Init cancelled."));
|
|
1758
|
+
process.exit(0);
|
|
1759
|
+
}
|
|
1760
|
+
template = templateAnswer;
|
|
1534
1761
|
}
|
|
1535
1762
|
let projectName = options.name?.trim();
|
|
1536
1763
|
if (!projectName) {
|
|
@@ -1649,6 +1876,11 @@ async function init(options = {}) {
|
|
|
1649
1876
|
value: "greentree",
|
|
1650
1877
|
label: "GreenTree",
|
|
1651
1878
|
hint: "Emerald green accent, Inter font, Mintlify-inspired"
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
value: "custom",
|
|
1882
|
+
label: "Create your own theme",
|
|
1883
|
+
hint: "Scaffold a new theme file + CSS in themes/ (name asked next)"
|
|
1652
1884
|
}
|
|
1653
1885
|
]
|
|
1654
1886
|
});
|
|
@@ -1656,6 +1888,26 @@ async function init(options = {}) {
|
|
|
1656
1888
|
p.outro(pc.red("Init cancelled."));
|
|
1657
1889
|
process.exit(0);
|
|
1658
1890
|
}
|
|
1891
|
+
let customThemeName;
|
|
1892
|
+
if (theme === "custom") {
|
|
1893
|
+
const nameAnswer = await p.text({
|
|
1894
|
+
message: "Theme name? (we'll create themes/<name>.ts and themes/<name>.css)",
|
|
1895
|
+
placeholder: "my-theme",
|
|
1896
|
+
defaultValue: "my-theme",
|
|
1897
|
+
validate: (value) => {
|
|
1898
|
+
const v = (value ?? "").trim().replace(/\.(ts|css)$/i, "");
|
|
1899
|
+
if (!v) return "Theme name is required";
|
|
1900
|
+
if (v.includes("/") || v.includes("\\")) return "Theme name cannot contain path separators";
|
|
1901
|
+
if (v.includes(" ")) return "Theme name cannot contain spaces";
|
|
1902
|
+
if (!/^[a-z0-9_-]+$/i.test(v)) return "Use only letters, numbers, hyphens, and underscores";
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
if (p.isCancel(nameAnswer)) {
|
|
1906
|
+
p.outro(pc.red("Init cancelled."));
|
|
1907
|
+
process.exit(0);
|
|
1908
|
+
}
|
|
1909
|
+
customThemeName = nameAnswer.trim().replace(/\.(ts|css)$/i, "");
|
|
1910
|
+
}
|
|
1659
1911
|
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)`;
|
|
1660
1912
|
const useAlias = await p.confirm({
|
|
1661
1913
|
message: `Use path aliases for imports? ${pc.dim(aliasHint)}`,
|
|
@@ -1696,21 +1948,22 @@ async function init(options = {}) {
|
|
|
1696
1948
|
}
|
|
1697
1949
|
astroAdapter = adapter;
|
|
1698
1950
|
}
|
|
1951
|
+
const defaultEntry = "docs";
|
|
1699
1952
|
const entry = await p.text({
|
|
1700
1953
|
message: "Where should your docs live?",
|
|
1701
|
-
placeholder:
|
|
1702
|
-
defaultValue:
|
|
1954
|
+
placeholder: defaultEntry,
|
|
1955
|
+
defaultValue: defaultEntry,
|
|
1703
1956
|
validate: (value) => {
|
|
1704
|
-
|
|
1705
|
-
if (
|
|
1706
|
-
if (
|
|
1957
|
+
const v = (value ?? "").trim();
|
|
1958
|
+
if (v.startsWith("/")) return "Use a relative path (no leading /)";
|
|
1959
|
+
if (v.includes(" ")) return "Path cannot contain spaces";
|
|
1707
1960
|
}
|
|
1708
1961
|
});
|
|
1709
1962
|
if (p.isCancel(entry)) {
|
|
1710
1963
|
p.outro(pc.red("Init cancelled."));
|
|
1711
1964
|
process.exit(0);
|
|
1712
1965
|
}
|
|
1713
|
-
const entryPath = entry;
|
|
1966
|
+
const entryPath = entry.trim() || defaultEntry;
|
|
1714
1967
|
const detectedCssFiles = detectGlobalCssFiles(cwd);
|
|
1715
1968
|
let globalCssRelPath;
|
|
1716
1969
|
const defaultCssPath = framework === "sveltekit" ? "src/app.css" : framework === "astro" ? "src/styles/global.css" : framework === "nuxt" ? "assets/css/main.css" : "app/globals.css";
|
|
@@ -1731,27 +1984,29 @@ async function init(options = {}) {
|
|
|
1731
1984
|
}
|
|
1732
1985
|
globalCssRelPath = picked;
|
|
1733
1986
|
} else {
|
|
1734
|
-
const
|
|
1987
|
+
const cssPathAnswer = await p.text({
|
|
1735
1988
|
message: "Where is your global CSS file?",
|
|
1736
1989
|
placeholder: defaultCssPath,
|
|
1737
1990
|
defaultValue: defaultCssPath,
|
|
1738
1991
|
validate: (value) => {
|
|
1739
|
-
|
|
1740
|
-
if (!
|
|
1992
|
+
const v = (value ?? "").trim();
|
|
1993
|
+
if (v && !v.endsWith(".css")) return "Path must end with .css";
|
|
1741
1994
|
}
|
|
1742
1995
|
});
|
|
1743
|
-
if (p.isCancel(
|
|
1996
|
+
if (p.isCancel(cssPathAnswer)) {
|
|
1744
1997
|
p.outro(pc.red("Init cancelled."));
|
|
1745
1998
|
process.exit(0);
|
|
1746
1999
|
}
|
|
1747
|
-
globalCssRelPath =
|
|
2000
|
+
globalCssRelPath = cssPathAnswer.trim() || defaultCssPath;
|
|
1748
2001
|
}
|
|
1749
2002
|
const pkgJsonContent = readFileSafe(path.join(cwd, "package.json"));
|
|
1750
2003
|
const pkgJson = pkgJsonContent ? JSON.parse(pkgJsonContent) : { name: "my-project" };
|
|
2004
|
+
const projectName = pkgJson.name || "My Project";
|
|
1751
2005
|
const cfg = {
|
|
1752
2006
|
entry: entryPath,
|
|
1753
2007
|
theme,
|
|
1754
|
-
|
|
2008
|
+
customThemeName,
|
|
2009
|
+
projectName,
|
|
1755
2010
|
framework,
|
|
1756
2011
|
useAlias,
|
|
1757
2012
|
astroAdapter
|
|
@@ -1864,6 +2119,11 @@ async function init(options = {}) {
|
|
|
1864
2119
|
}
|
|
1865
2120
|
}
|
|
1866
2121
|
function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
2122
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
2123
|
+
const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
|
|
2124
|
+
write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
|
|
2125
|
+
write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
|
|
2126
|
+
}
|
|
1867
2127
|
write("docs.config.ts", docsConfigTemplate(cfg));
|
|
1868
2128
|
const existingNextConfig = readFileSafe(path.join(cwd, "next.config.ts")) ?? readFileSafe(path.join(cwd, "next.config.mjs")) ?? readFileSafe(path.join(cwd, "next.config.js"));
|
|
1869
2129
|
if (existingNextConfig) {
|
|
@@ -1887,12 +2147,12 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
1887
2147
|
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
1888
2148
|
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
1889
2149
|
if (existingGlobalCss) {
|
|
1890
|
-
const injected = injectCssImport(existingGlobalCss, cfg.theme);
|
|
2150
|
+
const injected = injectCssImport(existingGlobalCss, cfg.theme, cfg.customThemeName, globalCssRelPath);
|
|
1891
2151
|
if (injected) {
|
|
1892
2152
|
writeFileSafe(globalCssAbsPath, injected, true);
|
|
1893
2153
|
written.push(globalCssRelPath + " (updated)");
|
|
1894
2154
|
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
1895
|
-
} else write(globalCssRelPath, globalCssTemplate(cfg.theme));
|
|
2155
|
+
} else write(globalCssRelPath, globalCssTemplate(cfg.theme, cfg.customThemeName, globalCssRelPath));
|
|
1896
2156
|
write(`app/${cfg.entry}/layout.tsx`, docsLayoutTemplate(cfg));
|
|
1897
2157
|
write("postcss.config.mjs", postcssConfigTemplate());
|
|
1898
2158
|
if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate(cfg.useAlias));
|
|
@@ -1901,6 +2161,11 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
1901
2161
|
write(`app/${cfg.entry}/quickstart/page.mdx`, quickstartPageTemplate(cfg));
|
|
1902
2162
|
}
|
|
1903
2163
|
function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
2164
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
2165
|
+
const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
|
|
2166
|
+
write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
|
|
2167
|
+
write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
|
|
2168
|
+
}
|
|
1904
2169
|
write("src/lib/docs.config.ts", svelteDocsConfigTemplate(cfg));
|
|
1905
2170
|
write("src/lib/docs.server.ts", svelteDocsServerTemplate(cfg));
|
|
1906
2171
|
write(`src/routes/${cfg.entry}/+layout.svelte`, svelteDocsLayoutTemplate(cfg));
|
|
@@ -1920,17 +2185,22 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
|
|
|
1920
2185
|
default: "fumadocs"
|
|
1921
2186
|
}[cfg.theme] || "fumadocs";
|
|
1922
2187
|
if (existingGlobalCss) {
|
|
1923
|
-
const injected = injectSvelteCssImport(existingGlobalCss, cssTheme);
|
|
2188
|
+
const injected = cfg.theme === "custom" && cfg.customThemeName ? injectSvelteCssImport(existingGlobalCss, "custom", cfg.customThemeName, globalCssRelPath) : injectSvelteCssImport(existingGlobalCss, cssTheme);
|
|
1924
2189
|
if (injected) {
|
|
1925
2190
|
writeFileSafe(globalCssAbsPath, injected, true);
|
|
1926
2191
|
written.push(globalCssRelPath + " (updated)");
|
|
1927
2192
|
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
1928
|
-
} else write(globalCssRelPath, svelteGlobalCssTemplate(cssTheme));
|
|
2193
|
+
} else write(globalCssRelPath, cfg.theme === "custom" && cfg.customThemeName ? svelteGlobalCssTemplate("custom", cfg.customThemeName, globalCssRelPath) : svelteGlobalCssTemplate(cssTheme));
|
|
1929
2194
|
write(`${cfg.entry}/page.md`, svelteWelcomePageTemplate(cfg));
|
|
1930
2195
|
write(`${cfg.entry}/installation/page.md`, svelteInstallationPageTemplate(cfg));
|
|
1931
2196
|
write(`${cfg.entry}/quickstart/page.md`, svelteQuickstartPageTemplate(cfg));
|
|
1932
2197
|
}
|
|
1933
2198
|
function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
2199
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
2200
|
+
const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
|
|
2201
|
+
write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
|
|
2202
|
+
write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
|
|
2203
|
+
}
|
|
1934
2204
|
write("src/lib/docs.config.ts", astroDocsConfigTemplate(cfg));
|
|
1935
2205
|
write("src/lib/docs.server.ts", astroDocsServerTemplate(cfg));
|
|
1936
2206
|
if (!fileExists(path.join(cwd, "astro.config.mjs")) && !fileExists(path.join(cwd, "astro.config.ts"))) write("astro.config.mjs", astroConfigTemplate(cfg.astroAdapter ?? "vercel"));
|
|
@@ -1950,17 +2220,22 @@ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
1950
2220
|
default: "fumadocs"
|
|
1951
2221
|
}[cfg.theme] || "fumadocs";
|
|
1952
2222
|
if (existingGlobalCss) {
|
|
1953
|
-
const injected = injectAstroCssImport(existingGlobalCss, cssTheme);
|
|
2223
|
+
const injected = cfg.theme === "custom" && cfg.customThemeName ? injectAstroCssImport(existingGlobalCss, "custom", cfg.customThemeName, globalCssRelPath) : injectAstroCssImport(existingGlobalCss, cssTheme);
|
|
1954
2224
|
if (injected) {
|
|
1955
2225
|
writeFileSafe(globalCssAbsPath, injected, true);
|
|
1956
2226
|
written.push(globalCssRelPath + " (updated)");
|
|
1957
2227
|
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
1958
|
-
} else write(globalCssRelPath, astroGlobalCssTemplate(cssTheme));
|
|
2228
|
+
} else write(globalCssRelPath, cfg.theme === "custom" && cfg.customThemeName ? astroGlobalCssTemplate("custom", cfg.customThemeName, globalCssRelPath) : astroGlobalCssTemplate(cssTheme));
|
|
1959
2229
|
write(`${cfg.entry}/page.md`, astroWelcomePageTemplate(cfg));
|
|
1960
2230
|
write(`${cfg.entry}/installation/page.md`, astroInstallationPageTemplate(cfg));
|
|
1961
2231
|
write(`${cfg.entry}/quickstart/page.md`, astroQuickstartPageTemplate(cfg));
|
|
1962
2232
|
}
|
|
1963
2233
|
function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
2234
|
+
if (cfg.theme === "custom" && cfg.customThemeName) {
|
|
2235
|
+
const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
|
|
2236
|
+
write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
|
|
2237
|
+
write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
|
|
2238
|
+
}
|
|
1964
2239
|
write("docs.config.ts", nuxtDocsConfigTemplate(cfg));
|
|
1965
2240
|
write("server/utils/docs-server.ts", nuxtDocsServerTemplate(cfg));
|
|
1966
2241
|
write("server/api/docs.get.ts", nuxtServerApiDocsGetTemplate());
|
|
@@ -1982,12 +2257,12 @@ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
|
1982
2257
|
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
1983
2258
|
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
1984
2259
|
if (existingGlobalCss) {
|
|
1985
|
-
const injected = injectNuxtCssImport(existingGlobalCss, cssTheme);
|
|
2260
|
+
const injected = cfg.theme === "custom" && cfg.customThemeName ? injectNuxtCssImport(existingGlobalCss, "custom", cfg.customThemeName, globalCssRelPath) : injectNuxtCssImport(existingGlobalCss, cssTheme);
|
|
1986
2261
|
if (injected) {
|
|
1987
2262
|
writeFileSafe(globalCssAbsPath, injected, true);
|
|
1988
2263
|
written.push(globalCssRelPath + " (updated)");
|
|
1989
2264
|
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
1990
|
-
} else write(globalCssRelPath, nuxtGlobalCssTemplate(cssTheme));
|
|
2265
|
+
} else write(globalCssRelPath, cfg.theme === "custom" && cfg.customThemeName ? nuxtGlobalCssTemplate("custom", cfg.customThemeName, globalCssRelPath) : nuxtGlobalCssTemplate(cssTheme));
|
|
1991
2266
|
write(`${cfg.entry}/page.md`, nuxtWelcomePageTemplate(cfg));
|
|
1992
2267
|
write(`${cfg.entry}/installation/page.md`, nuxtInstallationPageTemplate(cfg));
|
|
1993
2268
|
write(`${cfg.entry}/quickstart/page.md`, nuxtQuickstartPageTemplate(cfg));
|