@farming-labs/docs 0.0.4 → 0.0.5

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.
@@ -1 +1,5 @@
1
- export { };
1
+ //#region src/cli/index.d.ts
2
+ /** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs, --framework astro (exported for tests). */
3
+ declare function parseFlags(argv: string[]): Record<string, string | undefined>;
4
+ //#endregion
5
+ export { parseFlags };
@@ -286,7 +286,7 @@ function nextConfigMergedTemplate(existingContent) {
286
286
  const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("import ") ? i : acc, -1);
287
287
  if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
288
288
  else lines.unshift(importLine, "");
289
- const adjustedExportIdx = exportIdx + (lastImportIdx >= 0 && exportIdx > lastImportIdx ? 1 : 0);
289
+ const adjustedExportIdx = exportIdx + (lastImportIdx >= 0 ? exportIdx > lastImportIdx ? 1 : 0 : 2);
290
290
  const simpleMatch = lines[adjustedExportIdx].match(/^(\s*export\s+default\s+)(.*?)(;?\s*)$/);
291
291
  if (simpleMatch) {
292
292
  const [, prefix, value, suffix] = simpleMatch;
@@ -328,6 +328,32 @@ export default function RootLayout({
328
328
  }
329
329
  `;
330
330
  }
331
+ /**
332
+ * Injects RootProvider (import + wrapper) into an existing root layout without overwriting.
333
+ * Returns the modified content, or null if RootProvider is already present or injection isn't possible.
334
+ */
335
+ function injectRootProviderIntoLayout(content) {
336
+ if (!content || content.includes("RootProvider")) return null;
337
+ let out = content;
338
+ const themeImport = "import { RootProvider } from \"@farming-labs/theme\";";
339
+ if (!out.includes("@farming-labs/theme")) {
340
+ const lines = out.split("\n");
341
+ let lastImportIdx = -1;
342
+ for (let i = 0; i < lines.length; i++) {
343
+ const trimmed = lines[i].trimStart();
344
+ if (trimmed.startsWith("import ") || trimmed.startsWith("import type ")) lastImportIdx = i;
345
+ }
346
+ if (lastImportIdx >= 0) {
347
+ lines.splice(lastImportIdx + 1, 0, themeImport);
348
+ out = lines.join("\n");
349
+ } else out = themeImport + "\n" + out;
350
+ }
351
+ if (!out.includes("<RootProvider>")) {
352
+ const childrenPattern = /\{children\}/;
353
+ if (childrenPattern.test(out)) out = out.replace(childrenPattern, "<RootProvider>{children}</RootProvider>");
354
+ }
355
+ return out === content ? null : out;
356
+ }
331
357
  function globalCssTemplate(theme) {
332
358
  return `\
333
359
  @import "tailwindcss";
@@ -347,9 +373,19 @@ function injectCssImport(existingContent, theme) {
347
373
  function docsLayoutTemplate(cfg) {
348
374
  return `\
349
375
  import docsConfig from "${nextDocsLayoutConfigImport(cfg.useAlias)}";
350
- import { createDocsLayout } from "@farming-labs/theme";
376
+ import { createDocsLayout, createDocsMetadata } from "@farming-labs/theme";
351
377
 
352
- export default createDocsLayout(docsConfig);
378
+ export const metadata = createDocsMetadata(docsConfig);
379
+
380
+ const DocsLayout = createDocsLayout(docsConfig);
381
+
382
+ export default function Layout({ children }: { children: React.ReactNode }) {
383
+ return (
384
+ <>
385
+ <DocsLayout>{children}</DocsLayout>
386
+ </>
387
+ );
388
+ }
353
389
  `;
354
390
  }
355
391
  function postcssConfigTemplate() {
@@ -1820,7 +1856,7 @@ async function init(options = {}) {
1820
1856
  });
1821
1857
  });
1822
1858
  } catch (err) {
1823
- const manualCmd = framework === "sveltekit" ? "npx vite dev" : framework === "astro" ? "npx astro dev" : framework === "nuxt" ? "npx nuxt dev" : "npx next dev --webpack";
1859
+ const manualCmd = framework === "sveltekit" ? "npx vite dev" : framework === "astro" ? "npx astro dev" : framework === "nuxt" ? "npx nuxt dev" : "pnpm dev";
1824
1860
  p.log.error(`Could not start dev server. Try running manually:
1825
1861
  ${pc.cyan(manualCmd)}`);
1826
1862
  p.outro(pc.yellow("Setup complete. Start the server manually."));
@@ -1838,7 +1874,16 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
1838
1874
  written.push(configFile + " (updated)");
1839
1875
  } else skipped.push(configFile + " (already configured)");
1840
1876
  } else write("next.config.ts", nextConfigTemplate());
1841
- write("app/layout.tsx", rootLayoutTemplate(cfg, globalCssRelPath));
1877
+ const rootLayoutPath = path.join(cwd, "app/layout.tsx");
1878
+ const existingRootLayout = readFileSafe(rootLayoutPath);
1879
+ if (!existingRootLayout) write("app/layout.tsx", rootLayoutTemplate(cfg, globalCssRelPath), true);
1880
+ else if (!existingRootLayout.includes("RootProvider")) {
1881
+ const injected = injectRootProviderIntoLayout(existingRootLayout);
1882
+ if (injected) {
1883
+ writeFileSafe(rootLayoutPath, injected, true);
1884
+ written.push("app/layout.tsx (injected RootProvider)");
1885
+ } else skipped.push("app/layout.tsx (could not inject RootProvider)");
1886
+ } else skipped.push("app/layout.tsx (already has RootProvider)");
1842
1887
  const globalCssAbsPath = path.join(cwd, globalCssRelPath);
1843
1888
  const existingGlobalCss = readFileSafe(globalCssAbsPath);
1844
1889
  if (existingGlobalCss) {
@@ -1948,11 +1993,102 @@ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
1948
1993
  write(`${cfg.entry}/quickstart/page.md`, nuxtQuickstartPageTemplate(cfg));
1949
1994
  }
1950
1995
 
1996
+ //#endregion
1997
+ //#region src/cli/upgrade.ts
1998
+ /**
1999
+ * Upgrade @farming-labs/* packages to latest.
2000
+ * Detects framework from package.json by default, or use --framework (next, nuxt, sveltekit, astro).
2001
+ */
2002
+ const PRESETS = [
2003
+ "next",
2004
+ "nuxt",
2005
+ "sveltekit",
2006
+ "astro"
2007
+ ];
2008
+ const PACKAGES_BY_FRAMEWORK = {
2009
+ nextjs: [
2010
+ "@farming-labs/docs",
2011
+ "@farming-labs/theme",
2012
+ "@farming-labs/next"
2013
+ ],
2014
+ nuxt: [
2015
+ "@farming-labs/docs",
2016
+ "@farming-labs/nuxt",
2017
+ "@farming-labs/nuxt-theme"
2018
+ ],
2019
+ sveltekit: [
2020
+ "@farming-labs/docs",
2021
+ "@farming-labs/svelte",
2022
+ "@farming-labs/svelte-theme"
2023
+ ],
2024
+ astro: [
2025
+ "@farming-labs/docs",
2026
+ "@farming-labs/astro",
2027
+ "@farming-labs/astro-theme"
2028
+ ]
2029
+ };
2030
+ function presetFromFramework(fw) {
2031
+ return fw === "nextjs" ? "next" : fw;
2032
+ }
2033
+ function frameworkFromPreset(preset) {
2034
+ return preset === "next" ? "nextjs" : preset;
2035
+ }
2036
+ /** Return package list for a framework (for testing and CLI). */
2037
+ function getPackagesForFramework(framework) {
2038
+ return PACKAGES_BY_FRAMEWORK[framework];
2039
+ }
2040
+ /** Build the install command for upgrade (for testing). */
2041
+ function buildUpgradeCommand(framework, tag, pm) {
2042
+ const packagesWithTag = PACKAGES_BY_FRAMEWORK[framework].map((name) => `${name}@${tag}`);
2043
+ return `${installCommand(pm)} ${packagesWithTag.join(" ")}`;
2044
+ }
2045
+ async function upgrade(options = {}) {
2046
+ const cwd = process.cwd();
2047
+ const tag = options.tag ?? "latest";
2048
+ p.intro(pc.bgCyan(pc.black(" @farming-labs/docs upgrade ")));
2049
+ if (!fileExists(path.join(cwd, "package.json"))) {
2050
+ p.log.error("No package.json found in the current directory. Run this from your project root.");
2051
+ process.exit(1);
2052
+ }
2053
+ let framework = null;
2054
+ let preset;
2055
+ if (options.framework) {
2056
+ const raw = options.framework.toLowerCase().trim();
2057
+ const normalized = raw === "nextjs" ? "next" : raw;
2058
+ if (!PRESETS.includes(normalized)) {
2059
+ p.log.error(`Invalid framework ${pc.cyan(options.framework)}. Use one of: ${PRESETS.map((t) => pc.cyan(t)).join(", ")}`);
2060
+ process.exit(1);
2061
+ }
2062
+ preset = normalized;
2063
+ framework = frameworkFromPreset(preset);
2064
+ } else {
2065
+ framework = detectFramework(cwd);
2066
+ if (!framework) {
2067
+ p.log.error("Could not detect a supported framework (Next.js, Nuxt, SvelteKit, Astro). Use " + pc.cyan("--framework <next|nuxt|sveltekit|astro>") + " to specify.");
2068
+ process.exit(1);
2069
+ }
2070
+ preset = presetFromFramework(framework);
2071
+ }
2072
+ const pm = detectPackageManager(cwd);
2073
+ const cmd = buildUpgradeCommand(framework, tag, pm);
2074
+ const packages = getPackagesForFramework(framework);
2075
+ p.log.step(`Upgrading ${preset} docs packages to ${tag}...`);
2076
+ p.log.message(pc.dim(packages.join(", ")));
2077
+ try {
2078
+ exec(cmd, cwd);
2079
+ p.log.success(`Packages upgraded to ${tag}.`);
2080
+ p.outro(pc.green("Done. Run your dev server to confirm everything works."));
2081
+ } catch {
2082
+ p.log.error("Upgrade failed. Try running manually:\n " + pc.cyan(cmd));
2083
+ process.exit(1);
2084
+ }
2085
+ }
2086
+
1951
2087
  //#endregion
1952
2088
  //#region src/cli/index.ts
1953
2089
  const args = process.argv.slice(2);
1954
2090
  const command = args[0];
1955
- /** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs */
2091
+ /** Parse flags like --template next, --name my-docs, --theme darksharp, --entry docs, --framework astro (exported for tests). */
1956
2092
  function parseFlags(argv) {
1957
2093
  const flags = {};
1958
2094
  for (let i = 0; i < argv.length; i++) {
@@ -1976,6 +2112,10 @@ async function main() {
1976
2112
  entry: flags.entry
1977
2113
  };
1978
2114
  if (!command || command === "init") await init(initOptions);
2115
+ else if (command === "upgrade") await upgrade({
2116
+ framework: flags.framework ?? (args[1] && !args[1].startsWith("--") ? args[1] : void 0),
2117
+ tag: args.includes("--beta") ? "beta" : "latest"
2118
+ });
1979
2119
  else if (command === "--help" || command === "-h") printHelp();
1980
2120
  else if (command === "--version" || command === "-v") printVersion();
1981
2121
  else {
@@ -1993,7 +2133,8 @@ ${pc.dim("Usage:")}
1993
2133
  npx @farming-labs/docs@latest ${pc.cyan("<command>")}
1994
2134
 
1995
2135
  ${pc.dim("Commands:")}
1996
- ${pc.cyan("init")} Scaffold docs in your project (default)
2136
+ ${pc.cyan("init")} Scaffold docs in your project (default)
2137
+ ${pc.cyan("upgrade")} Upgrade @farming-labs/* packages to latest (auto-detect or use --framework)
1997
2138
 
1998
2139
  ${pc.dim("Supported frameworks:")}
1999
2140
  Next.js, SvelteKit, Astro, Nuxt
@@ -2003,6 +2144,12 @@ ${pc.dim("Options for init:")}
2003
2144
  ${pc.cyan("--name <project>")} Project folder name when using ${pc.cyan("--template")}; prompt if omitted (e.g. ${pc.dim("my-docs")})
2004
2145
  ${pc.cyan("--theme <name>")} Skip theme prompt (e.g. ${pc.dim("darksharp")}, ${pc.dim("greentree")})
2005
2146
  ${pc.cyan("--entry <path>")} Skip entry path prompt (e.g. ${pc.dim("docs")})
2147
+
2148
+ ${pc.dim("Options for upgrade:")}
2149
+ ${pc.cyan("--framework <name>")} Explicit framework (${pc.dim("next")}, ${pc.dim("nuxt")}, ${pc.dim("sveltekit")}, ${pc.dim("astro")}); omit to auto-detect
2150
+ ${pc.cyan("--latest")} Install latest stable (default)
2151
+ ${pc.cyan("--beta")} Install beta versions
2152
+
2006
2153
  ${pc.cyan("-h, --help")} Show this help message
2007
2154
  ${pc.cyan("-v, --version")} Show version
2008
2155
  `);
@@ -2017,4 +2164,4 @@ main().catch((err) => {
2017
2164
  });
2018
2165
 
2019
2166
  //#endregion
2020
- export { };
2167
+ export { parseFlags };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "keywords": [
6
6
  "docs",
@@ -34,11 +34,14 @@
34
34
  "devDependencies": {
35
35
  "@types/node": "^22.10.0",
36
36
  "tsdown": "^0.20.3",
37
- "typescript": "^5.9.3"
37
+ "typescript": "^5.9.3",
38
+ "vitest": "^3.2.4"
38
39
  },
39
40
  "scripts": {
40
41
  "build": "tsdown",
41
42
  "dev": "tsdown --watch",
42
- "typecheck": "tsc --noEmit"
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest"
43
46
  }
44
47
  }