@fumadocs/cli 1.3.3 → 1.3.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.
Files changed (43) hide show
  1. package/dist/config.d.ts +53 -2
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +9 -6
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.js +87 -157
  6. package/dist/index.js.map +1 -1
  7. package/dist/installer-zD4jTQWp.js +128 -0
  8. package/dist/installer-zD4jTQWp.js.map +1 -0
  9. package/dist/registry/installer.d.ts +13 -0
  10. package/dist/registry/installer.d.ts.map +1 -0
  11. package/dist/registry/installer.js +2 -0
  12. package/package.json +7 -15
  13. package/dist/build/index.d.ts +0 -148
  14. package/dist/build/index.d.ts.map +0 -1
  15. package/dist/build/index.js +0 -289
  16. package/dist/build/index.js.map +0 -1
  17. package/dist/client-C2A4Jf2w.js +0 -102
  18. package/dist/client-C2A4Jf2w.js.map +0 -1
  19. package/dist/config-Bx-m6awG.d.ts +0 -53
  20. package/dist/config-Bx-m6awG.d.ts.map +0 -1
  21. package/dist/fs-C3j4H046.js +0 -18
  22. package/dist/fs-C3j4H046.js.map +0 -1
  23. package/dist/import-CSbSteB3.js +0 -124
  24. package/dist/import-CSbSteB3.js.map +0 -1
  25. package/dist/installer-BWoLnsXE.js +0 -673
  26. package/dist/installer-BWoLnsXE.js.map +0 -1
  27. package/dist/registry/client.d.ts +0 -112
  28. package/dist/registry/client.d.ts.map +0 -1
  29. package/dist/registry/client.js +0 -2
  30. package/dist/registry/installer/index.d.ts +0 -96
  31. package/dist/registry/installer/index.d.ts.map +0 -1
  32. package/dist/registry/installer/index.js +0 -2
  33. package/dist/registry/macros/route-handler.d.ts +0 -21
  34. package/dist/registry/macros/route-handler.d.ts.map +0 -1
  35. package/dist/registry/macros/route-handler.js +0 -11
  36. package/dist/registry/macros/route-handler.js.map +0 -1
  37. package/dist/registry/schema.d.ts +0 -2
  38. package/dist/registry/schema.js +0 -50
  39. package/dist/registry/schema.js.map +0 -1
  40. package/dist/schema-BAaUX4uu.d.ts +0 -77
  41. package/dist/schema-BAaUX4uu.d.ts.map +0 -1
  42. package/dist/types-79PW0lgM.d.ts +0 -6
  43. package/dist/types-79PW0lgM.d.ts.map +0 -1
package/dist/config.d.ts CHANGED
@@ -1,2 +1,53 @@
1
- import { a as createOrLoadConfig, i as createConfigSchema, n as Framework, o as getDefaultConfig, r as LoadedConfig, s as initConfig, t as ConfigInput } from "./config-Bx-m6awG.js";
2
- export { ConfigInput, Framework, LoadedConfig, createConfigSchema, createOrLoadConfig, getDefaultConfig, initConfig };
1
+ import { z } from "zod";
2
+
3
+ //#region src/config.d.ts
4
+ declare const frameworks: readonly ["next", "waku", "react-router", "tanstack-start"];
5
+ type Framework = (typeof frameworks)[number];
6
+ declare function createConfigSchema(cwd?: any): z.ZodObject<{
7
+ $schema: z.ZodOptional<z.ZodDefault<z.ZodString>>;
8
+ aliases: z.ZodDefault<z.ZodObject<{
9
+ uiDir: z.ZodDefault<z.ZodString>;
10
+ componentsDir: z.ZodDefault<z.ZodString>;
11
+ layoutDir: z.ZodDefault<z.ZodString>;
12
+ cssDir: z.ZodDefault<z.ZodString>;
13
+ libDir: z.ZodDefault<z.ZodString>;
14
+ }, z.core.$strip>>;
15
+ baseDir: z.ZodDefault<z.ZodString>;
16
+ uiLibrary: z.ZodDefault<z.ZodEnum<{
17
+ "radix-ui": "radix-ui";
18
+ "base-ui": "base-ui";
19
+ }>>;
20
+ framework: z.ZodDefault<z.ZodLiteral<"next" | "waku" | "react-router" | "tanstack-start">>;
21
+ commands: z.ZodDefault<z.ZodObject<{
22
+ format: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>>;
24
+ }, z.core.$strip>;
25
+ type ConfigSchema = ReturnType<typeof createConfigSchema>;
26
+ type ConfigInput = z.input<ConfigSchema>;
27
+ type LoadedConfig = z.output<ConfigSchema>;
28
+ declare function createOrLoadConfig(file?: string): Promise<LoadedConfig>;
29
+ /**
30
+ * Write new config, skip if a config already exists
31
+ *
32
+ * @returns the created config, `undefined` if not created
33
+ */
34
+ declare function initConfig(file?: string): Promise<LoadedConfig | undefined>;
35
+ declare function getDefaultConfig(cwd?: string): Promise<{
36
+ aliases: {
37
+ uiDir: string;
38
+ componentsDir: string;
39
+ layoutDir: string;
40
+ cssDir: string;
41
+ libDir: string;
42
+ };
43
+ baseDir: string;
44
+ uiLibrary: "radix-ui" | "base-ui";
45
+ framework: "next" | "waku" | "react-router" | "tanstack-start";
46
+ commands: {
47
+ format?: string | undefined;
48
+ };
49
+ $schema?: string | undefined;
50
+ }>;
51
+ //#endregion
52
+ export { ConfigInput, Framework, LoadedConfig, createConfigSchema, createOrLoadConfig, getDefaultConfig, initConfig };
53
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","names":[],"sources":["../src/config.ts"],"mappings":";;;cAKM,UAAA;AAAA,KACM,SAAA,WAAoB,UAAA;AAAA,iBAEhB,kBAAA,CAAmB,GAAA,SAAmB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;KA2DjD,YAAA,GAAe,UAAA,QAAkB,kBAAA;AAAA,KAE1B,WAAA,GAAc,CAAA,CAAE,KAAA,CAAM,YAAA;AAAA,KACtB,YAAA,GAAe,CAAA,CAAE,MAAA,CAAO,YAAA;AAAA,iBAEd,kBAAA,CAAmB,IAAA,YAAsB,OAAA,CAAQ,YAAA;;;;;;iBAejD,UAAA,CAAW,IAAA,YAAsB,OAAA,CAAQ,YAAA;AAAA,iBAezC,gBAAA,CAAiB,GAAA,YAAY,OAAA"}
package/dist/config.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs/promises";
2
2
  import { z } from "zod";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
+ import path from "node:path";
4
5
  //#region src/config.ts
5
6
  const frameworks = [
6
7
  "next",
@@ -8,7 +9,9 @@ const frameworks = [
8
9
  "react-router",
9
10
  "tanstack-start"
10
11
  ];
11
- function createConfigSchema() {
12
+ function createConfigSchema(cwd = process.cwd()) {
13
+ const srcDir = path.resolve(cwd, "src");
14
+ const packageJsonPath = path.resolve(cwd, "package.json");
12
15
  const defaultAliases = {
13
16
  uiDir: "./components/ui",
14
17
  componentsDir: "./components",
@@ -25,15 +28,15 @@ function createConfigSchema() {
25
28
  cssDir: z.string().default(defaultAliases.componentsDir),
26
29
  libDir: z.string().default(defaultAliases.libDir)
27
30
  }).default(defaultAliases),
28
- baseDir: z.string().default(() => existsSync("./src") ? "src" : ""),
31
+ baseDir: z.string().default(() => existsSync(srcDir) ? "src" : ""),
29
32
  uiLibrary: z.enum(["radix-ui", "base-ui"]).default("radix-ui"),
30
33
  framework: z.literal(frameworks).default(() => {
31
- return detectFrameworkFromPackageJson() ?? "next";
34
+ return detectFrameworkFromPackageJson(packageJsonPath) ?? "next";
32
35
  }),
33
36
  commands: z.object({ format: z.string().optional() }).default({})
34
37
  });
35
38
  }
36
- function detectFrameworkFromPackageJson(pkgPath = "./package.json") {
39
+ function detectFrameworkFromPackageJson(pkgPath) {
37
40
  try {
38
41
  const pkgRaw = readFileSync(pkgPath, "utf-8");
39
42
  const pkg = JSON.parse(pkgRaw);
@@ -66,8 +69,8 @@ async function initConfig(file = "./cli.json") {
66
69
  await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));
67
70
  return defaultConfig;
68
71
  }
69
- async function getDefaultConfig() {
70
- return createConfigSchema().parse({});
72
+ async function getDefaultConfig(cwd) {
73
+ return createConfigSchema(cwd).parse({});
71
74
  }
72
75
  //#endregion
73
76
  export { createConfigSchema, createOrLoadConfig, getDefaultConfig, initConfig };
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'node:fs';\n\nconst frameworks = ['next', 'waku', 'react-router', 'tanstack-start'] as const;\nexport type Framework = (typeof frameworks)[number];\n\nexport function createConfigSchema() {\n const defaultAliases = {\n uiDir: './components/ui',\n componentsDir: './components',\n layoutDir: './layouts',\n cssDir: './styles',\n libDir: './lib',\n };\n\n return z.object({\n $schema: z.string().default('node_modules/@fumadocs/cli/dist/schema.json').optional(),\n aliases: z\n .object({\n uiDir: z.string().default(defaultAliases.uiDir),\n componentsDir: z.string().default(defaultAliases.uiDir),\n layoutDir: z.string().default(defaultAliases.layoutDir),\n cssDir: z.string().default(defaultAliases.componentsDir),\n libDir: z.string().default(defaultAliases.libDir),\n })\n .default(defaultAliases),\n\n baseDir: z.string().default(() => (existsSync('./src') ? 'src' : '')),\n uiLibrary: z.enum(['radix-ui', 'base-ui']).default('radix-ui'),\n framework: z.literal(frameworks).default(() => {\n return detectFrameworkFromPackageJson() ?? 'next';\n }),\n\n commands: z\n .object({\n /**\n * command to format output code automatically\n */\n format: z.string().optional(),\n })\n .default({}),\n });\n}\n\nfunction detectFrameworkFromPackageJson(pkgPath = './package.json'): Framework | undefined {\n try {\n const pkgRaw = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(pkgRaw);\n\n const deps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n\n if (deps['next']) return 'next';\n if (deps['waku']) return 'waku';\n if (deps['react-router'] || deps['react-router-dom']) return 'react-router';\n if (deps['@tanstack/react-start']) return 'tanstack-start';\n } catch {\n return;\n }\n}\n\ntype ConfigSchema = ReturnType<typeof createConfigSchema>;\n\nexport type ConfigInput = z.input<ConfigSchema>;\nexport type LoadedConfig = z.output<ConfigSchema>;\n\nexport async function createOrLoadConfig(file = './cli.json'): Promise<LoadedConfig> {\n const inited = await initConfig(file);\n if (inited) return inited;\n\n const content = (await fs.readFile(file)).toString();\n const configSchema = createConfigSchema();\n\n return configSchema.parse(JSON.parse(content));\n}\n\n/**\n * Write new config, skip if a config already exists\n *\n * @returns the created config, `undefined` if not created\n */\nexport async function initConfig(file = './cli.json'): Promise<LoadedConfig | undefined> {\n if (\n await fs\n .stat(file)\n .then(() => true)\n .catch(() => false)\n ) {\n return;\n }\n\n const defaultConfig = await getDefaultConfig();\n await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));\n return defaultConfig;\n}\n\nexport async function getDefaultConfig() {\n return createConfigSchema().parse({} satisfies ConfigInput);\n}\n"],"mappings":";;;;AAIA,MAAM,aAAa;CAAC;CAAQ;CAAQ;CAAgB;CAAiB;AAGrE,SAAgB,qBAAqB;CACnC,MAAM,iBAAiB;EACrB,OAAO;EACP,eAAe;EACf,WAAW;EACX,QAAQ;EACR,QAAQ;EACT;AAED,QAAO,EAAE,OAAO;EACd,SAAS,EAAE,QAAQ,CAAC,QAAQ,8CAA8C,CAAC,UAAU;EACrF,SAAS,EACN,OAAO;GACN,OAAO,EAAE,QAAQ,CAAC,QAAQ,eAAe,MAAM;GAC/C,eAAe,EAAE,QAAQ,CAAC,QAAQ,eAAe,MAAM;GACvD,WAAW,EAAE,QAAQ,CAAC,QAAQ,eAAe,UAAU;GACvD,QAAQ,EAAE,QAAQ,CAAC,QAAQ,eAAe,cAAc;GACxD,QAAQ,EAAE,QAAQ,CAAC,QAAQ,eAAe,OAAO;GAClD,CAAC,CACD,QAAQ,eAAe;EAE1B,SAAS,EAAE,QAAQ,CAAC,cAAe,WAAW,QAAQ,GAAG,QAAQ,GAAI;EACrE,WAAW,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC,CAAC,QAAQ,WAAW;EAC9D,WAAW,EAAE,QAAQ,WAAW,CAAC,cAAc;AAC7C,UAAO,gCAAgC,IAAI;IAC3C;EAEF,UAAU,EACP,OAAO,EAIN,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAC9B,CAAC,CACD,QAAQ,EAAE,CAAC;EACf,CAAC;;AAGJ,SAAS,+BAA+B,UAAU,kBAAyC;AACzF,KAAI;EACF,MAAM,SAAS,aAAa,SAAS,QAAQ;EAC7C,MAAM,MAAM,KAAK,MAAM,OAAO;EAE9B,MAAM,OAAO;GACX,GAAG,IAAI;GACP,GAAG,IAAI;GACR;AAED,MAAI,KAAK,QAAS,QAAO;AACzB,MAAI,KAAK,QAAS,QAAO;AACzB,MAAI,KAAK,mBAAmB,KAAK,oBAAqB,QAAO;AAC7D,MAAI,KAAK,yBAA0B,QAAO;SACpC;AACN;;;AASJ,eAAsB,mBAAmB,OAAO,cAAqC;CACnF,MAAM,SAAS,MAAM,WAAW,KAAK;AACrC,KAAI,OAAQ,QAAO;CAEnB,MAAM,WAAW,MAAM,GAAG,SAAS,KAAK,EAAE,UAAU;AAGpD,QAFqB,oBAAoB,CAErB,MAAM,KAAK,MAAM,QAAQ,CAAC;;;;;;;AAQhD,eAAsB,WAAW,OAAO,cAAiD;AACvF,KACE,MAAM,GACH,KAAK,KAAK,CACV,WAAW,KAAK,CAChB,YAAY,MAAM,CAErB;CAGF,MAAM,gBAAgB,MAAM,kBAAkB;AAC9C,OAAM,GAAG,UAAU,MAAM,KAAK,UAAU,eAAe,MAAM,EAAE,CAAC;AAChE,QAAO;;AAGT,eAAsB,mBAAmB;AACvC,QAAO,oBAAoB,CAAC,MAAM,EAAE,CAAuB"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { z } from 'zod';\nimport { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\n\nconst frameworks = ['next', 'waku', 'react-router', 'tanstack-start'] as const;\nexport type Framework = (typeof frameworks)[number];\n\nexport function createConfigSchema(cwd = process.cwd()) {\n const srcDir = path.resolve(cwd, 'src');\n const packageJsonPath = path.resolve(cwd, 'package.json');\n const defaultAliases = {\n uiDir: './components/ui',\n componentsDir: './components',\n layoutDir: './layouts',\n cssDir: './styles',\n libDir: './lib',\n };\n\n return z.object({\n $schema: z.string().default('node_modules/@fumadocs/cli/dist/schema.json').optional(),\n aliases: z\n .object({\n uiDir: z.string().default(defaultAliases.uiDir),\n componentsDir: z.string().default(defaultAliases.uiDir),\n layoutDir: z.string().default(defaultAliases.layoutDir),\n cssDir: z.string().default(defaultAliases.componentsDir),\n libDir: z.string().default(defaultAliases.libDir),\n })\n .default(defaultAliases),\n\n baseDir: z.string().default(() => (existsSync(srcDir) ? 'src' : '')),\n uiLibrary: z.enum(['radix-ui', 'base-ui']).default('radix-ui'),\n framework: z.literal(frameworks).default(() => {\n return detectFrameworkFromPackageJson(packageJsonPath) ?? 'next';\n }),\n\n commands: z\n .object({\n /**\n * command to format output code automatically\n */\n format: z.string().optional(),\n })\n .default({}),\n });\n}\n\nfunction detectFrameworkFromPackageJson(pkgPath: string): Framework | undefined {\n try {\n const pkgRaw = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(pkgRaw);\n\n const deps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n\n if (deps['next']) return 'next';\n if (deps['waku']) return 'waku';\n if (deps['react-router'] || deps['react-router-dom']) return 'react-router';\n if (deps['@tanstack/react-start']) return 'tanstack-start';\n } catch {\n return;\n }\n}\n\ntype ConfigSchema = ReturnType<typeof createConfigSchema>;\n\nexport type ConfigInput = z.input<ConfigSchema>;\nexport type LoadedConfig = z.output<ConfigSchema>;\n\nexport async function createOrLoadConfig(file = './cli.json'): Promise<LoadedConfig> {\n const inited = await initConfig(file);\n if (inited) return inited;\n\n const content = (await fs.readFile(file)).toString();\n const configSchema = createConfigSchema();\n\n return configSchema.parse(JSON.parse(content));\n}\n\n/**\n * Write new config, skip if a config already exists\n *\n * @returns the created config, `undefined` if not created\n */\nexport async function initConfig(file = './cli.json'): Promise<LoadedConfig | undefined> {\n if (\n await fs\n .stat(file)\n .then(() => true)\n .catch(() => false)\n ) {\n return;\n }\n\n const defaultConfig = await getDefaultConfig();\n await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));\n return defaultConfig;\n}\n\nexport async function getDefaultConfig(cwd?: string) {\n return createConfigSchema(cwd).parse({} satisfies ConfigInput);\n}\n"],"mappings":";;;;;AAKA,MAAM,aAAa;CAAC;CAAQ;CAAQ;CAAgB;CAAiB;AAGrE,SAAgB,mBAAmB,MAAM,QAAQ,KAAK,EAAE;CACtD,MAAM,SAAS,KAAK,QAAQ,KAAK,MAAM;CACvC,MAAM,kBAAkB,KAAK,QAAQ,KAAK,eAAe;CACzD,MAAM,iBAAiB;EACrB,OAAO;EACP,eAAe;EACf,WAAW;EACX,QAAQ;EACR,QAAQ;EACT;AAED,QAAO,EAAE,OAAO;EACd,SAAS,EAAE,QAAQ,CAAC,QAAQ,8CAA8C,CAAC,UAAU;EACrF,SAAS,EACN,OAAO;GACN,OAAO,EAAE,QAAQ,CAAC,QAAQ,eAAe,MAAM;GAC/C,eAAe,EAAE,QAAQ,CAAC,QAAQ,eAAe,MAAM;GACvD,WAAW,EAAE,QAAQ,CAAC,QAAQ,eAAe,UAAU;GACvD,QAAQ,EAAE,QAAQ,CAAC,QAAQ,eAAe,cAAc;GACxD,QAAQ,EAAE,QAAQ,CAAC,QAAQ,eAAe,OAAO;GAClD,CAAC,CACD,QAAQ,eAAe;EAE1B,SAAS,EAAE,QAAQ,CAAC,cAAe,WAAW,OAAO,GAAG,QAAQ,GAAI;EACpE,WAAW,EAAE,KAAK,CAAC,YAAY,UAAU,CAAC,CAAC,QAAQ,WAAW;EAC9D,WAAW,EAAE,QAAQ,WAAW,CAAC,cAAc;AAC7C,UAAO,+BAA+B,gBAAgB,IAAI;IAC1D;EAEF,UAAU,EACP,OAAO,EAIN,QAAQ,EAAE,QAAQ,CAAC,UAAU,EAC9B,CAAC,CACD,QAAQ,EAAE,CAAC;EACf,CAAC;;AAGJ,SAAS,+BAA+B,SAAwC;AAC9E,KAAI;EACF,MAAM,SAAS,aAAa,SAAS,QAAQ;EAC7C,MAAM,MAAM,KAAK,MAAM,OAAO;EAE9B,MAAM,OAAO;GACX,GAAG,IAAI;GACP,GAAG,IAAI;GACR;AAED,MAAI,KAAK,QAAS,QAAO;AACzB,MAAI,KAAK,QAAS,QAAO;AACzB,MAAI,KAAK,mBAAmB,KAAK,oBAAqB,QAAO;AAC7D,MAAI,KAAK,yBAA0B,QAAO;SACpC;AACN;;;AASJ,eAAsB,mBAAmB,OAAO,cAAqC;CACnF,MAAM,SAAS,MAAM,WAAW,KAAK;AACrC,KAAI,OAAQ,QAAO;CAEnB,MAAM,WAAW,MAAM,GAAG,SAAS,KAAK,EAAE,UAAU;AAGpD,QAFqB,oBAAoB,CAErB,MAAM,KAAK,MAAM,QAAQ,CAAC;;;;;;;AAQhD,eAAsB,WAAW,OAAO,cAAiD;AACvF,KACE,MAAM,GACH,KAAK,KAAK,CACV,WAAW,KAAK,CAChB,YAAY,MAAM,CAErB;CAGF,MAAM,gBAAgB,MAAM,kBAAkB;AAC9C,OAAM,GAAG,UAAU,MAAM,KAAK,UAAU,eAAe,MAAM,EAAE,CAAC;AAChE,QAAO;;AAGT,eAAsB,iBAAiB,KAAc;AACnD,QAAO,mBAAmB,IAAI,CAAC,MAAM,EAAE,CAAuB"}
package/dist/index.js CHANGED
@@ -1,17 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { createOrLoadConfig, initConfig } from "./config.js";
3
- import { n as LocalRegistryClient, t as HttpRegistryClient } from "./client-C2A4Jf2w.js";
4
- import { t as ComponentInstaller } from "./installer-BWoLnsXE.js";
5
- import { t as exists } from "./fs-C3j4H046.js";
3
+ import { t as FumadocsComponentInstaller } from "./installer-zD4jTQWp.js";
6
4
  import fs from "node:fs/promises";
7
5
  import path from "node:path";
8
6
  import { Command } from "commander";
9
7
  import picocolors from "picocolors";
10
8
  import { x } from "tinyexec";
11
- import { autocompleteMultiselect, box, cancel, confirm, group, intro, isCancel, log, outro, select, spinner } from "@clack/prompts";
12
- import { detect } from "package-manager-detector";
9
+ import { autocompleteMultiselect, cancel, group, intro, isCancel, log, outro, select, spinner } from "@clack/prompts";
13
10
  import { exec } from "node:child_process";
14
11
  import { promisify } from "node:util";
12
+ import { HttpRegistryConnector, LocalRegistryConnector } from "fuma-cli/registry/connector";
15
13
  //#region src/commands/file-tree.ts
16
14
  const scanned = [
17
15
  "file",
@@ -63,7 +61,7 @@ async function runTree(args) {
63
61
  }
64
62
  //#endregion
65
63
  //#region package.json
66
- var version = "1.3.3";
64
+ var version = "1.3.5";
67
65
  //#endregion
68
66
  //#region src/commands/shared.ts
69
67
  const UIRegistries = {
@@ -71,146 +69,13 @@ const UIRegistries = {
71
69
  "radix-ui": "fumadocs/radix-ui"
72
70
  };
73
71
  //#endregion
74
- //#region src/registry/plugins/preserve.ts
75
- /**
76
- * keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them directly.
77
- */
78
- function pluginPreserveLayouts() {
79
- const layoutNames = [
80
- "layouts/home",
81
- "layouts/flux",
82
- "layouts/notebook",
83
- "layouts/docs",
84
- "layouts/shared"
85
- ];
86
- const layoutComps = {
87
- "@/<dir>/home/index.tsx": "layouts/home",
88
- "@/<dir>/shared/index.tsx": "layouts/shared",
89
- "@/<dir>/shared/client.tsx": "layouts/shared",
90
- "@/<dir>/notebook/index.tsx": "layouts/notebook",
91
- "@/<dir>/notebook/client.tsx": "layouts/notebook",
92
- "@/<dir>/notebook/page/index.tsx": "layouts/notebook/page",
93
- "@/<dir>/notebook/page/client.tsx": "layouts/notebook/page",
94
- "@/<dir>/docs/index.tsx": "layouts/docs",
95
- "@/<dir>/docs/client.tsx": "layouts/docs",
96
- "@/<dir>/docs/page/index.tsx": "layouts/docs/page",
97
- "@/<dir>/docs/page/client.tsx": "layouts/docs/page",
98
- "@/<dir>/flux/index.tsx": "layouts/flux",
99
- "@/<dir>/flux/page/index.tsx": "layouts/flux/page",
100
- "@/<dir>/flux/page/client.tsx": "layouts/flux/page"
101
- };
102
- const layoutNameSet = new Set(layoutNames);
103
- return {
104
- beforeInstall(comp, { stack }) {
105
- if (layoutNameSet.has(stack[0].name)) return;
106
- return {
107
- ...comp,
108
- $subComponents: comp.$subComponents.filter((child) => !layoutNameSet.has(child.name))
109
- };
110
- },
111
- transformImport(specifier, { stack }) {
112
- if (layoutNameSet.has(stack[0].name) || !(specifier in layoutComps)) return specifier;
113
- return `fumadocs-ui/${layoutComps[specifier]}`;
114
- }
115
- };
116
- }
117
- //#endregion
118
- //#region src/commands/add.ts
119
- async function add(input, client) {
120
- const config = client.config;
121
- let target;
122
- const installer = new ComponentInstaller(client, { plugins: [pluginPreserveLayouts()] });
123
- const registry = UIRegistries[config.uiLibrary];
124
- if (input.length === 0) {
125
- const spin = spinner();
126
- spin.start("fetching registry");
127
- const info = await client.fetchRegistryInfo();
128
- const options = [];
129
- for (const item of info.indexes) options.push({
130
- label: item.title ?? item.name,
131
- value: item.name,
132
- hint: item.description
133
- });
134
- const { indexes } = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();
135
- for (const item of indexes) options.push({
136
- label: item.title ?? item.name,
137
- value: `${registry}/${item.name}`,
138
- hint: item.description
139
- });
140
- spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
141
- const value = await autocompleteMultiselect({
142
- message: "Select components to install",
143
- options
144
- });
145
- if (isCancel(value)) {
146
- outro("Ended");
147
- return;
148
- }
149
- target = value;
150
- } else target = await Promise.all(input.map(async (item) => await client.hasComponent(item) ? item : `${registry}/${item}`));
151
- await install(target, installer);
152
- }
153
- async function install(target, installer) {
154
- for (const name of target) {
155
- const spin = spinner();
156
- spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));
157
- try {
158
- await installer.install(name, {
159
- onWarn(message) {
160
- spin.message(message);
161
- },
162
- async confirmFileOverride(options) {
163
- spin.clear();
164
- const value = await confirm({
165
- message: `Do you want to override ${options.path}?`,
166
- initialValue: false
167
- });
168
- if (isCancel(value)) {
169
- outro("Installation terminated");
170
- process.exit(0);
171
- }
172
- spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));
173
- return value;
174
- },
175
- onFileDownloaded(options) {
176
- spin.message(options.path);
177
- }
178
- });
179
- spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));
180
- } catch (e) {
181
- spin.error(e instanceof Error ? e.message : String(e));
182
- process.exit(-1);
183
- }
184
- }
185
- const deps = await installer.deps();
186
- if (deps.hasRequired()) {
187
- log.message();
188
- box([...deps.dependencies, ...deps.devDependencies].join("\n"), "New Dependencies");
189
- const pm = (await detect())?.name ?? "npm";
190
- const value = await confirm({ message: `Do you want to install with ${pm}?` });
191
- if (isCancel(value)) {
192
- outro("Installation terminated");
193
- process.exit(0);
194
- }
195
- if (value) {
196
- const spin = spinner({ errorMessage: "Failed to install dependencies" });
197
- spin.start("Installing dependencies");
198
- await deps.installRequired(pm);
199
- spin.stop("Dependencies installed");
200
- } else await deps.writeRequired();
201
- }
202
- await installer.onEnd();
203
- outro(picocolors.bold(picocolors.greenBright("Successful")));
204
- }
205
- //#endregion
206
72
  //#region src/commands/customise.ts
207
- async function customise(client) {
73
+ async function customise(config, connector) {
208
74
  intro(picocolors.bgBlack(picocolors.whiteBright("Customise Fumadocs UI")));
209
- const config = client.config;
210
- const installer = new ComponentInstaller(client, { plugins: [pluginPreserveLayouts()] });
211
- const registry = UIRegistries[config.uiLibrary];
212
- const info = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();
213
- const target = (await group({
75
+ const installer = new FumadocsComponentInstaller(connector, config);
76
+ const subRegistry = UIRegistries[config.uiLibrary];
77
+ const info = await connector.fetchRegistryInfo(subRegistry);
78
+ const targetInfo = (await group({
214
79
  layout: () => select({
215
80
  message: "What do you want to customise?",
216
81
  options: [
@@ -218,7 +83,10 @@ async function customise(client) {
218
83
  label: "Docs Layout",
219
84
  value: {
220
85
  id: "docs",
221
- target: [`${registry}/layouts/docs`],
86
+ targets: [{
87
+ subRegistry,
88
+ name: "layouts/docs"
89
+ }],
222
90
  print() {
223
91
  printLayout(["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"]);
224
92
  }
@@ -229,7 +97,10 @@ async function customise(client) {
229
97
  label: "Notebook Layout",
230
98
  value: {
231
99
  id: "notebook",
232
- target: [`${registry}/layouts/notebook`],
100
+ targets: [{
101
+ subRegistry,
102
+ name: "layouts/notebook"
103
+ }],
233
104
  print() {
234
105
  printLayout(["fumadocs-ui/layouts/notebook", "@/layouts/notebook"], ["fumadocs-ui/layouts/notebook/page", "@/layouts/notebook/page"]);
235
106
  }
@@ -240,7 +111,10 @@ async function customise(client) {
240
111
  label: "Flux Layout",
241
112
  value: {
242
113
  id: "flux",
243
- target: [`${registry}/layouts/flux`],
114
+ targets: [{
115
+ subRegistry,
116
+ name: "layouts/flux"
117
+ }],
244
118
  print() {
245
119
  printLayout(["fumadocs-ui/layouts/flux", "@/layouts/flux"], ["fumadocs-ui/layouts/flux/page", "@/layouts/flux/page"]);
246
120
  }
@@ -251,7 +125,10 @@ async function customise(client) {
251
125
  label: "Home Layout",
252
126
  value: {
253
127
  id: "home",
254
- target: [`${registry}/layouts/home`],
128
+ targets: [{
129
+ subRegistry,
130
+ name: "layouts/home"
131
+ }],
255
132
  print() {
256
133
  printLayout(["fumadocs-ui/layouts/home", `@/layouts/home`]);
257
134
  }
@@ -276,7 +153,7 @@ async function customise(client) {
276
153
  hint: "for those who want to build their own UI from ground up",
277
154
  value: {
278
155
  id: "docs-min",
279
- target: ["layouts/docs-min"],
156
+ targets: [{ name: "layouts/docs-min" }],
280
157
  print() {
281
158
  printLayout(["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"]);
282
159
  }
@@ -293,7 +170,10 @@ async function customise(client) {
293
170
  hint: "only replace a part of layout's page, useful for adjusting details",
294
171
  value: {
295
172
  id: index.name,
296
- target: [`${registry}/${index.name}`],
173
+ targets: [{
174
+ subRegistry,
175
+ name: index.name
176
+ }],
297
177
  print() {
298
178
  printSlot({
299
179
  at: `@/layouts/${selected.id}/page/slots/${name}`,
@@ -310,7 +190,10 @@ async function customise(client) {
310
190
  hint: "only replace a part of layout, useful for adjusting details",
311
191
  value: {
312
192
  id: index.name,
313
- target: [`${registry}/${index.name}`],
193
+ targets: [{
194
+ subRegistry,
195
+ name: index.name
196
+ }],
314
197
  print() {
315
198
  printSlot({
316
199
  at: `@/layouts/${selected.id}/slots/${name}`,
@@ -329,8 +212,8 @@ async function customise(client) {
329
212
  cancel("Installation Stopped.");
330
213
  process.exit(0);
331
214
  } })).target;
332
- await install(target.target, installer);
333
- target.print?.();
215
+ for (const target of targetInfo.targets) await installer.installInteractive(target.name, target.subRegistry);
216
+ targetInfo.print?.();
334
217
  outro(picocolors.bold("Have fun!"));
335
218
  }
336
219
  function printLayout(...maps) {
@@ -499,6 +382,52 @@ return (
499
382
  }
500
383
  }
501
384
  //#endregion
385
+ //#region src/commands/add.ts
386
+ async function add(input, connector, config) {
387
+ let targets;
388
+ const installer = new FumadocsComponentInstaller(connector, config);
389
+ const subRegistry = UIRegistries[config.uiLibrary];
390
+ if (input.length === 0) {
391
+ const spin = spinner();
392
+ spin.start("fetching registry");
393
+ async function scan(subRegistry) {
394
+ return (await connector.fetchRegistryInfo(subRegistry)).indexes.map((item) => ({
395
+ label: item.title ?? item.name,
396
+ value: {
397
+ name: item.name,
398
+ subRegistry
399
+ },
400
+ hint: item.description
401
+ }));
402
+ }
403
+ spin.stop(picocolors.bold(picocolors.greenBright("registry fetched")));
404
+ const value = await autocompleteMultiselect({
405
+ message: "Select components to install",
406
+ options: [...await scan(), ...await scan(subRegistry)]
407
+ });
408
+ if (isCancel(value)) {
409
+ outro("Ended");
410
+ return;
411
+ }
412
+ targets = value;
413
+ } else targets = await Promise.all(input.map(async (item) => await connector.hasComponent(item) ? { name: item } : {
414
+ subRegistry,
415
+ name: item
416
+ }));
417
+ for (const target of targets) await installer.installInteractive(target.name, target.subRegistry);
418
+ outro(picocolors.bold(picocolors.greenBright("Successful")));
419
+ }
420
+ //#endregion
421
+ //#region src/utils/fs.ts
422
+ async function exists(pathLike) {
423
+ try {
424
+ await fs.access(pathLike);
425
+ return true;
426
+ } catch {
427
+ return false;
428
+ }
429
+ }
430
+ //#endregion
502
431
  //#region src/commands/export-epub.ts
503
432
  const execAsync = promisify(exec);
504
433
  async function readPackageJson(cwd) {
@@ -692,14 +621,15 @@ program.name("fumadocs").description("CLI to setup Fumadocs, init a config").ver
692
621
  else console.log(picocolors.redBright("A config file already exists."));
693
622
  });
694
623
  program.command("customise").alias("customize").description("simple way to customise layouts with Fumadocs UI").option("--dir <string>", "the root url or directory to resolve registry").action(async (options) => {
695
- await customise(createClientFromDir(options.dir, await createOrLoadConfig(options.config)));
624
+ await customise(await createOrLoadConfig(options.config), createClientFromDir(options.dir));
696
625
  });
697
626
  const dirShortcuts = {
698
627
  ":preview": "https://preview.fumadocs.dev/registry",
699
628
  ":dev": "http://localhost:3000/registry"
700
629
  };
701
630
  program.command("add").description("add a new component to your docs").argument("[components...]", "components to download").option("--dir <string>", "the root url or directory to resolve registry").action(async (input, options) => {
702
- await add(input, createClientFromDir(options.dir, await createOrLoadConfig(options.config)));
631
+ const config = await createOrLoadConfig(options.config);
632
+ await add(input, createClientFromDir(options.dir), config);
703
633
  });
704
634
  program.command("export").description("export documentation to various formats").command("epub").description("export documentation to EPUB format (run after production build)").requiredOption("--framework <name>", "React framework: next, tanstack-start, react-router, waku").option("--output <path>", "output file path", "docs.epub").option("--scaffold-only", "only scaffold the EPUB route, do not copy").action(async (options) => {
705
635
  await exportEpub({
@@ -727,9 +657,9 @@ program.command("tree").argument("[json_or_args]", "JSON output of `tree` comman
727
657
  await fs.writeFile(output, out);
728
658
  } else console.log(out);
729
659
  });
730
- function createClientFromDir(dir = "https://fumadocs.dev/registry", config) {
660
+ function createClientFromDir(dir = "https://fumadocs.dev/registry") {
731
661
  if (dir in dirShortcuts) dir = dirShortcuts[dir];
732
- return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryClient(dir, config) : new LocalRegistryClient(dir, config);
662
+ return dir.startsWith("http://") || dir.startsWith("https://") ? new HttpRegistryConnector(dir) : new LocalRegistryConnector(dir);
733
663
  }
734
664
  program.parse();
735
665
  //#endregion