@fumadocs/cli 1.6.2 → 1.7.0

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,2 +1,2 @@
1
- import { t as runFeature } from "../features-CXFCgOWD.js";
1
+ import { t as runFeature } from "../features-Do6x9Tsz.js";
2
2
  export { runFeature };
@@ -1,6 +1,6 @@
1
1
  import { g as createSourceFile } from "./codemod-auol-p4R.js";
2
2
  import { t as exists } from "./fs-D6J9dOEx.js";
3
- import { t as FumadocsComponentInstaller } from "./installer-CzhVfs7i.js";
3
+ import { t as FumadocsComponentInstaller } from "./installer-C4662Woc.js";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { x } from "tinyexec";
@@ -15,9 +15,8 @@ async function runFeature(feature, options, { project, connector, io, versions }
15
15
  const env = [];
16
16
  const notes = [];
17
17
  const installer = new FumadocsComponentInstaller(connector, project.config, cwd, {
18
- onWarn: io.log,
19
- confirmFileOverride: ({ path: file }) => io.confirmOverwrite(path.relative(cwd, file)),
20
- onFileDownloaded: ({ path: file }) => io.log(`installed ${path.relative(cwd, file)}`)
18
+ confirmFileOverride: (file) => io.confirmOverwrite(path.relative(cwd, file.output)),
19
+ onFileWritten: (file) => io.log(`installed ${path.relative(cwd, file.output)}`)
21
20
  });
22
21
  const ctx = {
23
22
  project,
@@ -72,13 +71,8 @@ async function runFeature(feature, options, { project, connector, io, versions }
72
71
  },
73
72
  async install(name, subRegistry) {
74
73
  const manager = await (await installer.install(name, subRegistry)).deps();
75
- const decode = (target, dep) => {
76
- const at = dep.indexOf("@", 1);
77
- if (at === -1) target[dep] = null;
78
- else target[dep.slice(0, at)] = dep.slice(at + 1);
79
- };
80
- for (const dep of manager.dependencies) decode(deps, dep);
81
- for (const dep of manager.devDependencies) decode(devDeps, dep);
74
+ Object.assign(deps, manager.required);
75
+ Object.assign(devDeps, manager.requiredDev);
82
76
  }
83
77
  };
84
78
  await feature.apply(ctx, options);
@@ -129,4 +123,4 @@ async function installDependencies(project, deps, devDeps, io, versions = {}) {
129
123
  //#endregion
130
124
  export { runFeature as t };
131
125
 
132
- //# sourceMappingURL=features-CXFCgOWD.js.map
126
+ //# sourceMappingURL=features-Do6x9Tsz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features-Do6x9Tsz.js","names":[],"sources":["../src/features/index.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { x } from 'tinyexec';\nimport type { RegistryConnector } from 'fuma-cli/registry/connector';\nimport type { Project } from '@/project';\nimport { createSourceFile, type SourceFile } from '@/codemod';\nimport { FumadocsComponentInstaller } from '@/registry/installer';\nimport { exists } from '@/utils/fs';\n\nexport interface SelectOption<T> {\n message: string;\n choices: { value: T; label: string; hint?: string }[];\n}\n\nexport interface ConfirmOption {\n message: string;\n initialValue?: boolean;\n}\n\nexport type FeatureOption<T> = [T] extends [boolean] ? ConfirmOption : SelectOption<T>;\n\nexport interface Feature<Options extends object = Record<never, never>> {\n id: string;\n title: string;\n description: string;\n options?: { [K in keyof Options]: FeatureOption<Options[K]> };\n /** features that must be applied first */\n requires?: AnyFeature[];\n /** whether the feature is already applied to the project */\n detect?: (project: Project) => Promise<boolean>;\n /** @returns `true` if supported, otherwise the reason */\n supports?: (project: Project) => true | string;\n apply(ctx: FeatureContext, options: Options): Promise<void>;\n}\n\nexport type AnyFeature = Omit<Feature<Record<string, unknown>>, 'options'> & {\n options?: Record<string, SelectOption<unknown> | ConfirmOption>;\n};\n\nexport interface PackageJson {\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport interface FeatureContext {\n project: Project;\n /** write a file (relative to project root), asks before overwriting */\n write: (file: string, content: string) => Promise<void>;\n /** edit a file with codemods, `false` if the file doesn't exist */\n source: (file: string, edit: (file: SourceFile) => void) => Promise<boolean>;\n /** append lines to a text file, skip lines already present */\n append: (file: string, lines: string[]) => Promise<void>;\n remove: (file: string) => Promise<void>;\n packageJson: (edit: (data: PackageJson) => void) => Promise<void>;\n /** install a registry component */\n install: (name: string, subRegistry?: string) => Promise<void>;\n /** `null` to install the latest version */\n addDependencies: (deps: Record<string, string | null>, dev?: boolean) => void;\n env: (key: string, value: string) => void;\n /** instructions to show after the feature is applied */\n note: (text: string) => void;\n log: (message: string) => void;\n}\n\nexport interface FeatureIO {\n log: (message: string) => void;\n confirmOverwrite: (file: string) => Promise<boolean>;\n /** run package manager to install dependencies, otherwise only write them into `package.json` */\n installDependencies: boolean;\n}\n\nexport interface RunOptions {\n project: Project;\n connector: RegistryConnector;\n io: FeatureIO;\n /** preferred versions of dependencies, when a feature doesn't specify one */\n versions?: Record<string, string>;\n}\n\nexport async function runFeature<O extends object>(\n feature: Omit<Feature<O>, 'options'>,\n options: O,\n { project, connector, io, versions }: RunOptions,\n): Promise<{ notes: string[] }> {\n for (const dep of feature.requires ?? []) {\n if (dep.detect && !(await dep.detect(project)))\n throw new Error(\n `\"${feature.id}\" requires \"${dep.id}\", run \\`fumadocs feature ${dep.id}\\` first.`,\n );\n }\n const supported = feature.supports?.(project) ?? true;\n if (supported !== true) throw new Error(`\"${feature.id}\" is unavailable: ${supported}`);\n\n const { cwd } = project;\n const deps: Record<string, string | null> = {};\n const devDeps: Record<string, string | null> = {};\n const env: string[] = [];\n const notes: string[] = [];\n const installer = new FumadocsComponentInstaller(connector, project.config, cwd, {\n confirmFileOverride: (file) => io.confirmOverwrite(path.relative(cwd, file.output)),\n onFileWritten: (file) => io.log(`installed ${path.relative(cwd, file.output)}`),\n });\n\n const ctx: FeatureContext = {\n project,\n log: io.log,\n note: (text) => notes.push(text),\n env: (key, value) => env.push(`${key}=${value}`),\n addDependencies(input, dev) {\n Object.assign(dev ? devDeps : deps, input);\n },\n async write(file, content) {\n const target = path.join(cwd, file);\n const current = await fs.readFile(target, 'utf-8').catch(() => null);\n if (current !== null) {\n if (current.trim() === content.trim()) return;\n if (!(await io.confirmOverwrite(file))) return;\n }\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, content);\n io.log(`${current === null ? 'created' : 'updated'} ${file}`);\n },\n async source(file, edit) {\n const target = path.join(cwd, file);\n if (!(await exists(target))) return false;\n const source = await createSourceFile(target);\n edit(source);\n if (source.s.hasChanged()) {\n await source.save();\n io.log(`updated ${file}`);\n }\n return true;\n },\n async append(file, lines) {\n const target = path.join(cwd, file);\n const current = await fs.readFile(target, 'utf-8').catch(() => '');\n const missing = lines.filter((line) => !current.includes(line));\n if (missing.length === 0) return;\n const separator = current.length === 0 || current.endsWith('\\n') ? '' : '\\n';\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, `${current}${separator}${missing.join('\\n')}\\n`);\n io.log(`updated ${file}`);\n },\n async remove(file) {\n await fs.rm(path.join(cwd, file), { force: true });\n io.log(`removed ${file}`);\n },\n async packageJson(edit) {\n const target = path.join(cwd, 'package.json');\n const data = JSON.parse(await fs.readFile(target, 'utf-8'));\n edit(data);\n await fs.writeFile(target, `${JSON.stringify(data, null, 2)}\\n`);\n io.log('updated package.json');\n },\n async install(name, subRegistry) {\n const result = await installer.install(name, subRegistry);\n const manager = await result.deps();\n Object.assign(deps, manager.required);\n Object.assign(devDeps, manager.requiredDev);\n },\n };\n\n await feature.apply(ctx, options);\n\n if (env.length > 0) await ctx.append('.env.local', env);\n await installDependencies(project, deps, devDeps, io, versions);\n return { notes };\n}\n\nasync function installDependencies(\n project: Project,\n deps: Record<string, string | null>,\n devDeps: Record<string, string | null>,\n io: FeatureIO,\n versions: Record<string, string> = {},\n) {\n const { dependencies = {}, devDependencies = {} } = project.packageJson;\n const filter = (input: Record<string, string | null>) =>\n Object.entries(input).filter(([name]) => !(name in dependencies) && !(name in devDependencies));\n const missing = filter(deps);\n const missingDev = filter(devDeps);\n if (missing.length === 0 && missingDev.length === 0) return;\n\n if (io.installDependencies) {\n const pm = project.packageManager;\n const run = async (flags: string[], entries: [string, string | null][]) => {\n if (entries.length === 0) return;\n const args = [\n 'add',\n ...flags,\n ...entries.map(([name, version]) => (version ? `${name}@${version}` : name)),\n ];\n io.log(`${pm} ${args.join(' ')}`);\n await x(pm, args, {\n nodeOptions: { cwd: project.cwd, stdio: 'inherit' },\n throwOnError: true,\n });\n };\n await run([], missing);\n await run(['-D'], missingDev);\n return;\n }\n\n const file = path.join(project.cwd, 'package.json');\n const json = JSON.parse(await fs.readFile(file, 'utf-8'));\n const write = (field: string, entries: [string, string | null][]) => {\n if (entries.length === 0) return;\n const target = (json[field] ??= {});\n for (const [name, version] of entries) target[name] = version ?? versions[name] ?? 'latest';\n };\n write('dependencies', missing);\n write('devDependencies', missingDev);\n await fs.writeFile(file, `${JSON.stringify(json, null, 2)}\\n`);\n io.log('updated package.json');\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3,15 +3,15 @@ import { i as initConfig, n as createOrLoadConfig } from "./config-Bw0kmF01.js";
3
3
  import { T as parseSourceFile } from "./codemod-auol-p4R.js";
4
4
  import { t as exists } from "./fs-D6J9dOEx.js";
5
5
  import { i as loadProject, n as frameworks } from "./project-DwRVTm4p.js";
6
- import { n as isCancel$1, r as UIRegistries, t as FumadocsComponentInstaller } from "./installer-CzhVfs7i.js";
7
- import { t as runFeature } from "./features-CXFCgOWD.js";
6
+ import { n as UIRegistries, t as FumadocsComponentInstaller } from "./installer-C4662Woc.js";
7
+ import { t as runFeature } from "./features-Do6x9Tsz.js";
8
8
  import { t as features } from "./all-BBf7I8hG.js";
9
9
  import fs from "node:fs/promises";
10
10
  import path from "node:path";
11
11
  import { cac } from "cac";
12
12
  import picocolors from "picocolors";
13
13
  import { x } from "tinyexec";
14
- import { autocompleteMultiselect, cancel, confirm, group, intro, log, note, outro, select, spinner } from "@clack/prompts";
14
+ import { autocompleteMultiselect, cancel, confirm, group, intro, isCancel, log, note, outro, select, spinner } from "@clack/prompts";
15
15
  import { exec } from "node:child_process";
16
16
  import { promisify } from "node:util";
17
17
  import { HttpRegistryConnector, LocalRegistryConnector } from "fuma-cli/registry/connector";
@@ -66,14 +66,14 @@ async function runTree(args) {
66
66
  }
67
67
  //#endregion
68
68
  //#region package.json
69
- var version = "1.6.2";
69
+ var version = "1.7.0";
70
70
  //#endregion
71
71
  //#region src/commands/customise.ts
72
72
  async function customise(config, connector) {
73
73
  intro(picocolors.bgBlack(picocolors.whiteBright("Customize Fumadocs UI")));
74
74
  const installer = new FumadocsComponentInstaller(connector, config);
75
75
  const subRegistry = UIRegistries[config.uiLibrary];
76
- const info = await connector.fetchRegistryInfo(subRegistry);
76
+ const manifest = await installer.fetchManifest(subRegistry);
77
77
  const targetInfo = (await group({
78
78
  layout: () => select({
79
79
  message: "What do you want to customize?",
@@ -143,6 +143,33 @@ async function customise(config, connector) {
143
143
  target: (v) => {
144
144
  const selected = v.results.layout;
145
145
  if (selected.id === "home") return Promise.resolve(selected);
146
+ const slots = [];
147
+ const prefix = `layouts/${selected.id}/`;
148
+ for (const { name: id } of manifest.components) {
149
+ const [dir, name] = id.startsWith(prefix) ? id.slice(prefix.length).split("slots/") : [];
150
+ if (name === void 0) continue;
151
+ const isPage = dir === "page/";
152
+ slots.push({
153
+ label: `${isPage ? "Page" : "Layout"}: ${name}`,
154
+ hint: `only replace a part of layout${isPage ? "'s page" : ""}, useful for adjusting details`,
155
+ value: {
156
+ id,
157
+ targets: [{
158
+ subRegistry,
159
+ name: id
160
+ }],
161
+ print() {
162
+ printSlot({
163
+ at: `@/${id}`,
164
+ layoutId: selected.id,
165
+ name,
166
+ isPage,
167
+ uiLibrary: config.uiLibrary
168
+ });
169
+ }
170
+ }
171
+ });
172
+ }
146
173
  return select({
147
174
  message: "Which part do you want to customize?",
148
175
  options: [
@@ -160,54 +187,7 @@ async function customise(config, connector) {
160
187
  print: () => printLayout(config, ["fumadocs-ui/layouts/docs", "@/layouts/docs"], ["fumadocs-ui/layouts/docs/page", "@/layouts/docs/page"])
161
188
  }
162
189
  },
163
- ...info.unlistedIndexes.flatMap((index) => {
164
- const prefix = `slots/${selected.id}`;
165
- if (!index.name.startsWith(prefix)) return [];
166
- let name = index.name.slice(prefix.length + 1);
167
- if (name.startsWith("page/")) {
168
- name = name.slice(5);
169
- return {
170
- label: `Page: ${name}`,
171
- hint: "only replace a part of layout's page, useful for adjusting details",
172
- value: {
173
- id: index.name,
174
- targets: [{
175
- subRegistry,
176
- name: index.name
177
- }],
178
- print() {
179
- printSlot({
180
- at: `@/layouts/${selected.id}/page/slots/${name}`,
181
- layoutId: selected.id,
182
- name,
183
- isPage: true,
184
- uiLibrary: config.uiLibrary
185
- });
186
- }
187
- }
188
- };
189
- }
190
- return {
191
- label: `Layout: ${name}`,
192
- hint: "only replace a part of layout, useful for adjusting details",
193
- value: {
194
- id: index.name,
195
- targets: [{
196
- subRegistry,
197
- name: index.name
198
- }],
199
- print() {
200
- printSlot({
201
- at: `@/layouts/${selected.id}/slots/${name}`,
202
- layoutId: selected.id,
203
- name,
204
- isPage: false,
205
- uiLibrary: config.uiLibrary
206
- });
207
- }
208
- }
209
- };
210
- })
190
+ ...slots
211
191
  ]
212
192
  });
213
193
  }
@@ -455,6 +435,15 @@ return (
455
435
  }
456
436
  }
457
437
  //#endregion
438
+ //#region src/utils/prompt.ts
439
+ /**
440
+ * `@clack/core` narrows to its own `unique symbol`, which can't exclude the wider `symbol` that
441
+ * `@clack/prompts` returns, so a prompt result never narrows without this.
442
+ */
443
+ function isCancel$1(value) {
444
+ return isCancel(value);
445
+ }
446
+ //#endregion
458
447
  //#region src/commands/add.ts
459
448
  async function add(input, connector, config) {
460
449
  let targets;
@@ -464,14 +453,20 @@ async function add(input, connector, config) {
464
453
  const spin = spinner();
465
454
  spin.start("fetching registry");
466
455
  async function scan(subRegistry, prefix) {
467
- return (await connector.fetchRegistryInfo(subRegistry)).indexes.map((item) => ({
468
- label: `${prefix ? `${picocolors.bold(prefix)} - ` : ""}${item.title ?? item.name}`,
469
- value: {
470
- name: item.name,
471
- subRegistry
472
- },
473
- hint: item.description
474
- }));
456
+ const manifest = await installer.fetchManifest(subRegistry);
457
+ const options = [];
458
+ for (const item of manifest.components) {
459
+ if (item.unlisted) continue;
460
+ options.push({
461
+ label: `${prefix ? `${picocolors.bold(prefix)} - ` : ""}${item.title ?? item.name}`,
462
+ value: {
463
+ name: item.name,
464
+ subRegistry
465
+ },
466
+ hint: item.description
467
+ });
468
+ }
469
+ return options;
475
470
  }
476
471
  const groups = await Promise.all([
477
472
  scan(void 0, "common"),
@@ -490,10 +485,14 @@ async function add(input, connector, config) {
490
485
  return;
491
486
  }
492
487
  targets = value;
493
- } else targets = await Promise.all(input.map(async (item) => await connector.hasComponent(item) ? { name: item } : {
494
- subRegistry,
495
- name: item
496
- }));
488
+ } else {
489
+ const root = /* @__PURE__ */ new Set();
490
+ for (const item of (await installer.fetchManifest()).components) root.add(item.name);
491
+ targets = input.map((name) => root.has(name) ? { name } : {
492
+ subRegistry,
493
+ name
494
+ });
495
+ }
497
496
  for (const target of targets) await installer.installInteractive(target.name, target.subRegistry);
498
497
  outro(picocolors.bold(picocolors.greenBright("Successful")));
499
498
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["isCancel","isCancel","packageJson.version"],"sources":["../src/commands/file-tree.ts","../src/utils/file-tree/run-tree.ts","../package.json","../src/commands/customise.ts","../src/commands/add.ts","../src/commands/export-epub.ts","../src/commands/feature.ts","../src/index.ts"],"sourcesContent":["export type JsonTreeNode =\n | {\n type: 'file';\n name: string;\n }\n | {\n type: 'directory';\n name: string;\n contents: JsonTreeNode[];\n }\n | {\n type: 'report';\n }\n | {\n type: 'link';\n name: string;\n };\n\nconst scanned = ['file', 'directory', 'link'];\n\nexport function treeToMdx(input: JsonTreeNode[], noRoot = false): string {\n function toNode(item: JsonTreeNode): string {\n if (item.type === 'file' || item.type === 'link') {\n return `<File name=${JSON.stringify(item.name)} />`;\n }\n\n if (item.type === 'directory') {\n if (item.contents.length === 1 && 'name' in item.contents[0]) {\n const child = item.contents[0];\n\n return toNode({\n ...child,\n name: `${item.name}/${child.name}`,\n });\n }\n\n return `<Folder name=${JSON.stringify(item.name)}>\n${item.contents.map(toNode).filter(Boolean).join('\\n')}\n</Folder>`;\n }\n\n return '';\n }\n\n let children = input.filter((v) => scanned.includes(v.type));\n\n if (noRoot && children.length === 1 && input[0].type === 'directory') {\n children = input[0].contents;\n }\n\n return `<Files>\n${children.map(toNode).filter(Boolean).join('\\n')}\n</Files>`;\n}\n\nexport function treeToJavaScript(\n input: JsonTreeNode[],\n noRoot?: boolean,\n importName = 'fumadocs-ui/components/files',\n): string {\n return `import { File, Files, Folder } from ${JSON.stringify(importName)}\n\nexport default (${treeToMdx(input, noRoot)})`;\n}\n","import { x } from 'tinyexec';\nimport type { JsonTreeNode } from '@/commands/file-tree';\n\nexport async function runTree(args: string): Promise<JsonTreeNode[]> {\n const out = await x('tree', [args, '--gitignore', '--prune', '-J']);\n\n try {\n return JSON.parse(out.stdout) as JsonTreeNode[];\n } catch (e) {\n throw new Error('failed to run `tree` command', {\n cause: e,\n });\n }\n}\n","","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { cancel, group, intro, log, outro, select } from '@clack/prompts';\nimport picocolors from 'picocolors';\nimport { parseSourceFile } from '@/codemod';\nimport { frameworks } from '@/project';\nimport type { Target } from '@/commands/add';\nimport { UIRegistries } from '@/commands/shared';\nimport { LoadedConfig } from '@/config';\nimport { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { FumadocsComponentInstaller } from '@/registry/installer';\n\ninterface TargetInfo {\n targets: Target[];\n id: string;\n print?: () => void | Promise<void>;\n}\n\ninterface SlotPrintInfo {\n at: string;\n layoutId: string;\n name: string;\n isPage: boolean;\n uiLibrary: LoadedConfig['uiLibrary'];\n}\n\nexport async function customise(config: LoadedConfig, connector: RegistryConnector) {\n intro(picocolors.bgBlack(picocolors.whiteBright('Customize Fumadocs UI')));\n\n const installer = new FumadocsComponentInstaller(connector, config);\n const subRegistry = UIRegistries[config.uiLibrary];\n const info = await connector.fetchRegistryInfo(subRegistry);\n\n const result = await group(\n {\n layout: (): Promise<TargetInfo | symbol> =>\n select({\n message: 'What do you want to customize?',\n options: [\n {\n label: 'Docs Layout',\n value: {\n id: 'docs',\n targets: [{ subRegistry, name: 'layouts/docs' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n ),\n },\n hint: 'the default docs layout',\n },\n {\n label: 'Notebook Layout',\n value: {\n id: 'notebook',\n targets: [{ subRegistry, name: 'layouts/notebook' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/notebook', '@/layouts/notebook'],\n ['fumadocs-ui/layouts/notebook/page', '@/layouts/notebook/page'],\n ),\n },\n hint: 'a more compact version of docs layout',\n },\n {\n label: 'Flux Layout',\n value: {\n id: 'flux',\n targets: [{ subRegistry, name: 'layouts/flux' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/flux', '@/layouts/flux'],\n ['fumadocs-ui/layouts/flux/page', '@/layouts/flux/page'],\n ),\n },\n hint: 'the experimental variant of docs layout',\n },\n {\n label: 'Glass Layout',\n value: {\n id: 'glass',\n targets: [{ subRegistry, name: 'layouts/glass' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/glass', '@/layouts/glass'],\n ['fumadocs-ui/layouts/glass/page', '@/layouts/glass/page'],\n ),\n },\n hint: 'a docs layout with floating, translucent panels',\n },\n {\n label: 'Home Layout',\n value: {\n id: 'home',\n targets: [{ subRegistry, name: 'layouts/home' }],\n print: () => printLayout(config, ['fumadocs-ui/layouts/home', '@/layouts/home']),\n },\n hint: 'the layout for other non-docs pages',\n },\n ],\n }),\n target: (v): Promise<TargetInfo | symbol> => {\n const selected = v.results.layout!;\n if (selected.id === 'home') return Promise.resolve(selected);\n\n return select<TargetInfo>({\n message: 'Which part do you want to customize?',\n options: [\n {\n label: 'All',\n hint: 'install the entire layout',\n value: selected,\n },\n {\n label: 'Replace & rewrite from minimal styles',\n hint: 'for those who want to build their own UI from ground up',\n value: {\n id: 'docs-min',\n targets: [{ name: 'layouts/docs-min' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n ),\n },\n },\n ...info.unlistedIndexes.flatMap((index) => {\n const prefix = `slots/${selected.id}`;\n if (!index.name.startsWith(prefix)) return [];\n let name = index.name.slice(prefix.length + 1);\n\n if (name.startsWith('page/')) {\n name = name.slice('page/'.length);\n\n return {\n label: `Page: ${name}`,\n hint: \"only replace a part of layout's page, useful for adjusting details\",\n value: {\n id: index.name,\n targets: [{ subRegistry, name: index.name }],\n print() {\n printSlot({\n at: `@/layouts/${selected.id}/page/slots/${name}`,\n layoutId: selected.id,\n name,\n isPage: true,\n uiLibrary: config.uiLibrary,\n });\n },\n } as TargetInfo,\n };\n }\n\n return {\n label: `Layout: ${name}`,\n hint: 'only replace a part of layout, useful for adjusting details',\n value: {\n id: index.name,\n targets: [{ subRegistry, name: index.name }],\n print() {\n printSlot({\n at: `@/layouts/${selected.id}/slots/${name}`,\n layoutId: selected.id,\n name,\n isPage: false,\n uiLibrary: config.uiLibrary,\n });\n },\n } as TargetInfo,\n };\n }),\n ],\n });\n },\n },\n {\n onCancel: () => {\n cancel('Installation Stopped.');\n process.exit(0);\n },\n },\n );\n\n const targetInfo = result.target as TargetInfo;\n for (const target of targetInfo.targets) {\n await installer.installInteractive(target.name, target.subRegistry);\n }\n\n await targetInfo.print?.();\n\n outro(picocolors.bold('Have fun!'));\n}\n\n/** point the imports of route files at the installed layouts */\nexport async function rewriteLayoutImports(config: LoadedConfig, map: Map<string, string>) {\n const dir = path.join(config.baseDir, frameworks[config.framework].routesDir);\n const entries = await fs.readdir(dir, { recursive: true, withFileTypes: true }).catch(() => []);\n const updated: string[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.tsx')) continue;\n const file = path.join(entry.parentPath, entry.name);\n const source = parseSourceFile(file, await fs.readFile(file, 'utf-8'));\n for (const node of source.program.body) {\n if (node.type !== 'ImportDeclaration') continue;\n const to = map.get(node.source.value);\n if (to) source.s.overwrite(node.source.start + 1, node.source.end - 1, to);\n }\n if (!source.s.hasChanged()) continue;\n await source.save();\n updated.push(file);\n }\n\n return updated;\n}\n\nasync function printLayout(config: LoadedConfig, ...maps: [from: string, to: string][]) {\n intro(picocolors.bold('What is Next?'));\n const updated = await rewriteLayoutImports(config, new Map(maps));\n\n log.info(\n [\n 'You can check the installed layouts in `layouts` folder.',\n picocolors.dim('---'),\n updated.length > 0\n ? `Updated the imports of ${updated.join(', ')}.`\n : 'Open your `layout.tsx` files, replace the imports of components:',\n ...maps.map(([from, to]) => picocolors.greenBright(`\"${from}\" -> \"${to}\"`)),\n ].join('\\n'),\n );\n}\n\nfunction printSlot({ at, layoutId, name, isPage, uiLibrary }: SlotPrintInfo) {\n intro(picocolors.bold('What is Next?'));\n\n log.info(`You can check the installed layout slot in \"${at}\".`);\n\n const code = getSlotCode({ at, layoutId, name, isPage, uiLibrary });\n\n if (code) {\n const layoutComponent = layoutId === 'glass' ? '<GlassLayout />' : '<DocsLayout />';\n\n if (isPage) {\n log.info(\n `${picocolors.bold('At your <DocsPage /> component, update your \"slots\" prop:')}\\n\\n${code}`,\n );\n } else {\n log.info(\n `${picocolors.bold(`At your ${layoutComponent} component, update your \"slots\" prop:`)}\\n\\n${code}`,\n );\n }\n }\n}\n\nfunction getSlotCode({ at, layoutId, name, isPage, uiLibrary }: SlotPrintInfo): string | undefined {\n if (layoutId === 'glass') {\n // Glass layout wires its page slots directly, only layout-level slots are swappable.\n if (isPage) return;\n\n switch (name) {\n case 'header':\n return `import { Header } from '${at}';\n\nreturn (\n <GlassLayout\n slots={{\n header: Header,\n }}\n >\n ...\n </GlassLayout>\n);`;\n case 'sidebar': {\n // `sidebar` slot file holds the whole sidebar system (desktop, mobile drawer, provider).\n // Base UI additionally exposes a `drawerHandle` for its swipeable drawer.\n const imports = ['Sidebar', 'SidebarDrawer', 'SidebarProvider', 'useSidebar'];\n if (uiLibrary === 'base-ui') imports.push('drawerHandle');\n\n return `import { ${imports.join(', ')} } from '${at}';\n\nreturn (\n <GlassLayout\n slots={{\n sidebar: {\n main: Sidebar,\n provider: SidebarProvider,\n use: useSidebar,\n drawer: SidebarDrawer,${uiLibrary === 'base-ui' ? '\\n drawerHandle,' : ''}\n },\n }}\n >\n ...\n </GlassLayout>\n);`;\n }\n default:\n return;\n }\n }\n\n if (isPage) {\n switch (name) {\n case 'toc':\n if (layoutId === 'flux') {\n return `import { TOCProvider, TOC } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n toc: {\n provider: TOCProvider,\n main: TOC,\n },\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n\n return `import { TOCProvider, TOC, TOCPopover } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n toc: {\n provider: TOCProvider,\n main: TOC,\n popover: TOCPopover,\n },\n }}\n >\n ...\n </DocsPage>\n);`;\n case 'container': {\n return `import { Container } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n container: Container,\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n case 'footer': {\n return `import { Footer } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n footer: Footer,\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n case 'breadcrumb': {\n return `import { Breadcrumb } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n breadcrumb: Breadcrumb,\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n default:\n return;\n }\n }\n\n switch (name) {\n case 'sidebar': {\n if (layoutId === 'notebook') {\n return `import {\n SidebarProvider,\n Sidebar,\n SidebarTrigger,\n SidebarCollapseTrigger,\n useSidebar,\n} from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n sidebar: {\n provider: SidebarProvider,\n root: Sidebar,\n trigger: SidebarTrigger,\n collapseTrigger: SidebarCollapseTrigger,\n useSidebar: useSidebar,\n },\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n\n return `import { SidebarProvider, Sidebar, SidebarTrigger, useSidebar } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n sidebar: {\n provider: SidebarProvider,\n root: Sidebar,\n trigger: SidebarTrigger,\n useSidebar: useSidebar,\n },\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n case 'container': {\n return `import { Container } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n container: Container,\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n case 'header': {\n return `import { Header } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n header: Header,\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n case 'tab-dropdown': {\n return `import { TabDropdown } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n tabDropdown: TabDropdown,\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n default:\n return;\n }\n}\n","import { autocompleteMultiselect, outro, spinner } from '@clack/prompts';\nimport { isCancel } from '@/utils/prompt';\nimport picocolors from 'picocolors';\nimport { UIRegistries } from '@/commands/shared';\nimport { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { LoadedConfig } from '@/config';\nimport { FumadocsComponentInstaller } from '@/registry/installer';\n\ninterface AddOption {\n label: string;\n value: Target;\n hint?: string;\n}\n\nexport interface Target {\n name: string;\n subRegistry?: string;\n}\n\nexport async function add(input: string[], connector: RegistryConnector, config: LoadedConfig) {\n let targets: Target[];\n const installer = new FumadocsComponentInstaller(connector, config);\n const subRegistry = UIRegistries[config.uiLibrary];\n\n if (input.length === 0) {\n const spin = spinner();\n spin.start('fetching registry');\n\n async function scan(subRegistry?: string, prefix?: string): Promise<AddOption[]> {\n const info = await connector.fetchRegistryInfo(subRegistry);\n\n return info.indexes.map((item) => ({\n label: `${prefix ? `${picocolors.bold(prefix)} - ` : ''}${item.title ?? item.name}`,\n value: { name: item.name, subRegistry },\n hint: item.description,\n }));\n }\n\n const groups = await Promise.all([\n scan(undefined, 'common'),\n scan('sanity', 'sanity'),\n scan('openapi', 'openapi'),\n scan('api-docs', 'api-docs'),\n scan(subRegistry, 'ui'),\n ]);\n\n spin.stop(picocolors.bold(picocolors.greenBright('registry fetched')));\n const value = await autocompleteMultiselect({\n message: 'Select components to install',\n options: groups.flat().sort((a, b) => a.label.localeCompare(b.label)),\n });\n\n if (isCancel(value)) {\n outro('Ended');\n return;\n }\n\n targets = value;\n } else {\n targets = await Promise.all(\n input.map(async (item) =>\n (await connector.hasComponent(item)) ? { name: item } : { subRegistry, name: item },\n ),\n );\n }\n\n for (const target of targets) {\n await installer.installInteractive(target.name, target.subRegistry);\n }\n\n outro(picocolors.bold(picocolors.greenBright('Successful')));\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { exists } from '@/utils/fs';\nimport picocolors from 'picocolors';\nimport { spinner } from '@clack/prompts';\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n}\n\nasync function readPackageJson(cwd: string): Promise<PackageJson | null> {\n try {\n const raw = await fs.readFile(path.join(cwd, 'package.json'), 'utf-8');\n return JSON.parse(raw) as PackageJson;\n } catch {\n return null;\n }\n}\n\n/** Path of pre-rendered EPUB, choose one according to your React framework. Next.js fetches from the running server instead. */\nconst EPUB_BUILD_PATHS: Record<string, string> = {\n next: '', // Fetched from /export/epub at runtime; route handlers don't produce static files\n astro: 'dist/export/epub',\n 'tanstack-start': '.output/public/export/epub',\n 'tanstack-start-spa': 'dist/client/export/epub',\n 'react-router': 'build/client/export/epub',\n 'react-router-spa': 'build/client/export/epub',\n waku: 'dist/public/export/epub',\n};\n\nexport async function exportEpub(options: { output?: string; framework: string }) {\n const cwd = process.cwd();\n const outputPath = path.resolve(cwd, options.output ?? 'docs.epub');\n const framework = options.framework;\n\n const spin = spinner();\n\n const buildPath = EPUB_BUILD_PATHS[framework];\n if (!(framework in EPUB_BUILD_PATHS)) {\n const valid = Object.keys(EPUB_BUILD_PATHS).join(', ');\n console.error(picocolors.red(`Invalid --framework \"${framework}\". Must be one of: ${valid}`));\n process.exit(1);\n }\n\n const pkg = await readPackageJson(cwd);\n // Check for fumadocs-epub dependency\n if (!pkg) {\n console.error(\n picocolors.red('Cannot read or parse package.json. Ensure it exists and is valid JSON.'),\n );\n process.exit(1);\n }\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (!deps['fumadocs-epub']) {\n console.log(picocolors.yellow('\\nInstalling fumadocs-epub...'));\n const packageManager = process.env.npm_execpath?.includes('pnpm')\n ? 'pnpm'\n : process.env.npm_execpath?.includes('bun')\n ? 'bun'\n : 'npm';\n const installCmd = `${packageManager} add fumadocs-epub`;\n try {\n await execAsync(installCmd, { cwd });\n } catch (err: unknown) {\n const stderr =\n err && typeof err === 'object' && 'stderr' in err\n ? String((err as { stderr?: string }).stderr)\n : '';\n console.error(picocolors.red(`Failed to install fumadocs-epub. Command: ${installCmd}`));\n if (stderr) console.error(stderr);\n process.exit(1);\n }\n }\n\n if (framework === 'next') {\n const secret = process.env.EXPORT_SECRET;\n if (!secret) {\n console.error(\n picocolors.red('EXPORT_SECRET is required for Next.js export. Set it in your environment.'),\n );\n process.exit(1);\n }\n const port = process.env.PORT || '3000';\n const url = `http://localhost:${port}/export/epub`;\n spin.start('Fetching EPUB from server');\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30_000);\n try {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${secret}` },\n signal: controller.signal,\n });\n if (!res.ok) {\n if (res.status === 401 || res.status === 403) {\n console.error(\n picocolors.red('Auth failed. Check that EXPORT_SECRET matches the value in your app.'),\n );\n } else if (res.status === 404) {\n console.error(\n picocolors.red('Export route not found, run `fumadocs feature epub` to create it.'),\n );\n } else {\n console.error(\n picocolors.red(\n `Server returned ${res.status}. Ensure the app is running (e.g. pnpm start) on port ${port}.`,\n ),\n );\n }\n process.exit(1);\n }\n const buffer = Buffer.from(await res.arrayBuffer());\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, buffer);\n spin.stop(picocolors.green(`EPUB saved to ${outputPath}`));\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n console.error(picocolors.red('Request timed out after 30 seconds.'));\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(picocolors.red(`Could not fetch EPUB: ${msg}`));\n }\n console.error(\n picocolors.yellow(`Ensure the server is running (e.g. pnpm start) on port ${port}.`),\n );\n process.exit(1);\n } finally {\n clearTimeout(timeoutId);\n }\n return;\n }\n\n const fullBuildPath = path.join(cwd, buildPath);\n if (!(await exists(fullBuildPath))) {\n console.error(\n picocolors.red(\n `EPUB not found at ${buildPath}. Run production build first (e.g. pnpm build).`,\n ),\n );\n process.exit(1);\n }\n\n spin.start('Copying EPUB');\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.copyFile(fullBuildPath, outputPath);\n spin.stop(picocolors.green(`EPUB saved to ${outputPath}`));\n}\n","import type { CAC, Command } from 'cac';\nimport { cancel, confirm, intro, log, note, outro, select, spinner } from '@clack/prompts';\nimport { isCancel } from '@/utils/prompt';\nimport picocolors from 'picocolors';\nimport type { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { createOrLoadConfig } from '@/config';\nimport { loadProject } from '@/project';\nimport { type AnyFeature, type FeatureIO, runFeature } from '@/features';\nimport { features } from '@/features/all';\n\ninterface CommandOptions extends Record<string, string | boolean | undefined> {\n config?: string;\n dir?: string;\n yes?: boolean;\n install?: boolean;\n}\n\nexport function registerFeatureCommands(\n cli: CAC,\n createConnector: (dir?: string) => RegistryConnector,\n) {\n const ids = features.map((feature) => feature.id).join(', ');\n\n register(\n cli.command('feature <id>', `configure a feature on your project (${ids})`),\n features,\n ).action(async (id: string, options: CommandOptions) => {\n const feature = features.find((item) => item.id === id);\n if (!feature) throw new Error(`unknown feature: ${id}, available: ${ids}`);\n\n await run(feature, options, createConnector(options.dir));\n });\n\n register(cli.command('init', 'set up Fumadocs on an existing app, same as `feature docs`'), [\n features[0],\n ]).action(async (options: CommandOptions) => {\n await run(features[0], options, createConnector(options.dir));\n });\n}\n\nfunction register(command: Command, list: AnyFeature[]) {\n command\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .option('-y, --yes', 'skip prompts, overwrite existing files')\n .option('--no-install', 'write dependencies to package.json without installing');\n\n for (const feature of list) {\n for (const [key, option] of Object.entries(feature.options ?? {})) {\n if ('choices' in option) {\n command.option(\n `--${key} <value>`,\n `${option.message} (${option.choices.map((choice) => choice.value).join(', ')})`,\n );\n } else {\n command.option(`--${key}`, option.message);\n }\n }\n }\n\n return command;\n}\n\nasync function run(feature: AnyFeature, options: CommandOptions, connector: RegistryConnector) {\n intro(picocolors.bgBlack(picocolors.whiteBright(feature.title)));\n const config = await createOrLoadConfig(options.config);\n const project = await loadProject(config);\n log.info(`${project.framework}${project.static ? ' (static)' : ''}, ${project.packageManager}`);\n\n const values: Record<string, unknown> = {};\n for (const [key, option] of Object.entries(feature.options ?? {})) {\n const given = options[key];\n if ('choices' in option) {\n if (given !== undefined) {\n if (!option.choices.some((choice) => choice.value === given))\n throw new Error(`invalid value for --${key}: ${given}`);\n values[key] = given;\n continue;\n }\n\n const value = await select({ message: option.message, options: option.choices });\n if (isCancel(value)) {\n cancel('Stopped.');\n process.exit(0);\n }\n values[key] = value;\n } else if (given !== undefined) {\n values[key] = given === true;\n } else if (options.yes) {\n values[key] = option.initialValue ?? false;\n } else {\n const value = await confirm({ message: option.message, initialValue: option.initialValue });\n if (isCancel(value)) {\n cancel('Stopped.');\n process.exit(0);\n }\n values[key] = value;\n }\n }\n\n const spin = spinner();\n const io: FeatureIO = {\n installDependencies: options.install !== false,\n log: (message) => spin.message(message),\n async confirmOverwrite(file) {\n if (options.yes) return true;\n spin.clear();\n const value = await confirm({ message: `Overwrite ${file}?`, initialValue: false });\n if (isCancel(value)) {\n cancel('Stopped.');\n process.exit(0);\n }\n spin.start();\n return value;\n },\n };\n\n spin.start(`Applying ${feature.title}`);\n try {\n const { notes } = await runFeature(feature, values, { project, connector, io });\n spin.stop(`${feature.title} applied`);\n if (notes.length > 0) note(notes.join('\\n\\n'), 'What is Next?');\n } catch (e) {\n spin.error(e instanceof Error ? e.message : String(e));\n process.exit(1);\n }\n outro(picocolors.bold(picocolors.greenBright('Done')));\n}\n","#!/usr/bin/env node\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { cac } from 'cac';\nimport picocolors from 'picocolors';\nimport { createOrLoadConfig, initConfig } from '@/config';\nimport { type JsonTreeNode, treeToJavaScript, treeToMdx } from '@/commands/file-tree';\nimport { runTree } from '@/utils/file-tree/run-tree';\nimport packageJson from '../package.json';\nimport { customise } from '@/commands/customise';\nimport { add } from '@/commands/add';\nimport { exportEpub } from '@/commands/export-epub';\nimport { registerFeatureCommands } from '@/commands/feature';\nimport { HttpRegistryConnector, LocalRegistryConnector } from 'fuma-cli/registry/connector';\n\nconst cli = cac('fumadocs');\ncli.option('--config <string>', 'path to the config file');\n\ncli.command('', 'init a `cli.json` config file').action(async () => {\n if (await initConfig()) {\n console.log(picocolors.green('Initialized a `./cli.json` config file.'));\n } else {\n console.log(picocolors.redBright('A config file already exists.'));\n }\n});\n\ncli\n .command('customise', 'simple way to customize layouts with Fumadocs UI')\n .alias('customize')\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .action(async (options: { config?: string; dir?: string }) => {\n const config = await createOrLoadConfig(options.config);\n await customise(config, createClientFromDir(options.dir));\n });\n\nconst dirShortcuts: Record<string, string> = {\n ':preview': 'https://preview.fumadocs.dev/registry',\n ':dev': 'http://localhost:3000/registry',\n};\n\ncli\n .command('add [...components]', 'add a new component to your docs')\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .action(async (input: string[], options: { config?: string; dir?: string }) => {\n const config = await createOrLoadConfig(options.config);\n const client = createClientFromDir(options.dir);\n await add(input, client, config);\n });\n\nregisterFeatureCommands(cli, createClientFromDir);\n\ncli\n .command(\n 'export <format>',\n 'export documentation to various formats (run after production build)',\n )\n .option('--framework <name>', 'framework: next, astro, tanstack-start, react-router, waku')\n .option('--output <path>', 'output file path', { default: 'docs.epub' })\n .action(async (format: string, options: { framework?: string; output: string }) => {\n if (format !== 'epub')\n throw new Error(`unsupported format: ${format}, only \\`epub\\` is supported.`);\n if (!options.framework) throw new Error('option `--framework <name>` is required.');\n\n await exportEpub({ framework: options.framework, output: options.output });\n });\n\ncli\n .command(\n 'tree [json_or_args] [output]',\n 'generate a file tree for the Files component, from a directory or JSON output of `tree`',\n )\n .option('--js', 'output as JavaScript file')\n .option('--no-root', 'remove the root node')\n .option('--import-name <name>', 'where to import components (JS only)')\n .action(\n async (\n str: string | undefined,\n output: string | undefined,\n { js, root, importName }: { js: boolean; root: boolean; importName?: string },\n ) => {\n const jsExtensions = ['.js', '.tsx', '.jsx'];\n const noRoot = !root;\n let nodes: JsonTreeNode[];\n\n try {\n nodes = JSON.parse(str ?? '') as JsonTreeNode[];\n } catch {\n nodes = await runTree(str ?? './');\n }\n\n const out =\n js || (output && jsExtensions.includes(path.extname(output)))\n ? treeToJavaScript(nodes, noRoot, importName)\n : treeToMdx(nodes, noRoot);\n\n if (output) {\n await fs.mkdir(path.dirname(output), { recursive: true });\n await fs.writeFile(output, out);\n } else {\n console.log(out);\n }\n },\n );\n\nfunction createClientFromDir(dir = 'https://fumadocs.dev/registry') {\n if (dir in dirShortcuts) dir = dirShortcuts[dir];\n\n return dir.startsWith('http://') || dir.startsWith('https://')\n ? new HttpRegistryConnector(dir)\n : new LocalRegistryConnector(dir);\n}\n\ncli.help();\ncli.version(packageJson.version);\n\ntry {\n cli.parse(process.argv, { run: false });\n await cli.runMatchedCommand();\n} catch (e) {\n console.error(picocolors.redBright(e instanceof Error ? e.message : String(e)));\n process.exit(1);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["isCancel","isCancelSymbol","isCancel","isCancel","packageJson.version"],"sources":["../src/commands/file-tree.ts","../src/utils/file-tree/run-tree.ts","../package.json","../src/commands/customise.ts","../src/utils/prompt.ts","../src/commands/add.ts","../src/commands/export-epub.ts","../src/commands/feature.ts","../src/index.ts"],"sourcesContent":["export type JsonTreeNode =\n | {\n type: 'file';\n name: string;\n }\n | {\n type: 'directory';\n name: string;\n contents: JsonTreeNode[];\n }\n | {\n type: 'report';\n }\n | {\n type: 'link';\n name: string;\n };\n\nconst scanned = ['file', 'directory', 'link'];\n\nexport function treeToMdx(input: JsonTreeNode[], noRoot = false): string {\n function toNode(item: JsonTreeNode): string {\n if (item.type === 'file' || item.type === 'link') {\n return `<File name=${JSON.stringify(item.name)} />`;\n }\n\n if (item.type === 'directory') {\n if (item.contents.length === 1 && 'name' in item.contents[0]) {\n const child = item.contents[0];\n\n return toNode({\n ...child,\n name: `${item.name}/${child.name}`,\n });\n }\n\n return `<Folder name=${JSON.stringify(item.name)}>\n${item.contents.map(toNode).filter(Boolean).join('\\n')}\n</Folder>`;\n }\n\n return '';\n }\n\n let children = input.filter((v) => scanned.includes(v.type));\n\n if (noRoot && children.length === 1 && input[0].type === 'directory') {\n children = input[0].contents;\n }\n\n return `<Files>\n${children.map(toNode).filter(Boolean).join('\\n')}\n</Files>`;\n}\n\nexport function treeToJavaScript(\n input: JsonTreeNode[],\n noRoot?: boolean,\n importName = 'fumadocs-ui/components/files',\n): string {\n return `import { File, Files, Folder } from ${JSON.stringify(importName)}\n\nexport default (${treeToMdx(input, noRoot)})`;\n}\n","import { x } from 'tinyexec';\nimport type { JsonTreeNode } from '@/commands/file-tree';\n\nexport async function runTree(args: string): Promise<JsonTreeNode[]> {\n const out = await x('tree', [args, '--gitignore', '--prune', '-J']);\n\n try {\n return JSON.parse(out.stdout) as JsonTreeNode[];\n } catch (e) {\n throw new Error('failed to run `tree` command', {\n cause: e,\n });\n }\n}\n","","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { cancel, group, intro, log, outro, select, type Option } from '@clack/prompts';\nimport picocolors from 'picocolors';\nimport { parseSourceFile } from '@/codemod';\nimport { frameworks } from '@/project';\nimport type { Target } from '@/commands/add';\nimport { UIRegistries } from '@/commands/shared';\nimport { LoadedConfig } from '@/config';\nimport { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { FumadocsComponentInstaller } from '@/registry/installer';\n\ninterface TargetInfo {\n targets: Target[];\n id: string;\n print?: () => void | Promise<void>;\n}\n\ninterface SlotPrintInfo {\n at: string;\n layoutId: string;\n name: string;\n isPage: boolean;\n uiLibrary: LoadedConfig['uiLibrary'];\n}\n\nexport async function customise(config: LoadedConfig, connector: RegistryConnector) {\n intro(picocolors.bgBlack(picocolors.whiteBright('Customize Fumadocs UI')));\n\n const installer = new FumadocsComponentInstaller(connector, config);\n const subRegistry = UIRegistries[config.uiLibrary];\n const manifest = await installer.fetchManifest(subRegistry);\n\n const result = await group(\n {\n layout: (): Promise<TargetInfo | symbol> =>\n select({\n message: 'What do you want to customize?',\n options: [\n {\n label: 'Docs Layout',\n value: {\n id: 'docs',\n targets: [{ subRegistry, name: 'layouts/docs' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n ),\n },\n hint: 'the default docs layout',\n },\n {\n label: 'Notebook Layout',\n value: {\n id: 'notebook',\n targets: [{ subRegistry, name: 'layouts/notebook' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/notebook', '@/layouts/notebook'],\n ['fumadocs-ui/layouts/notebook/page', '@/layouts/notebook/page'],\n ),\n },\n hint: 'a more compact version of docs layout',\n },\n {\n label: 'Flux Layout',\n value: {\n id: 'flux',\n targets: [{ subRegistry, name: 'layouts/flux' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/flux', '@/layouts/flux'],\n ['fumadocs-ui/layouts/flux/page', '@/layouts/flux/page'],\n ),\n },\n hint: 'the experimental variant of docs layout',\n },\n {\n label: 'Glass Layout',\n value: {\n id: 'glass',\n targets: [{ subRegistry, name: 'layouts/glass' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/glass', '@/layouts/glass'],\n ['fumadocs-ui/layouts/glass/page', '@/layouts/glass/page'],\n ),\n },\n hint: 'a docs layout with floating, translucent panels',\n },\n {\n label: 'Home Layout',\n value: {\n id: 'home',\n targets: [{ subRegistry, name: 'layouts/home' }],\n print: () => printLayout(config, ['fumadocs-ui/layouts/home', '@/layouts/home']),\n },\n hint: 'the layout for other non-docs pages',\n },\n ],\n }),\n target: (v): Promise<TargetInfo | symbol> => {\n const selected = v.results.layout!;\n if (selected.id === 'home') return Promise.resolve(selected);\n\n const slots: Option<TargetInfo>[] = [];\n const prefix = `layouts/${selected.id}/`;\n for (const { name: id } of manifest.components) {\n // <layout>/slots/<name> or <layout>/page/slots/<name>\n const [dir, name] = id.startsWith(prefix) ? id.slice(prefix.length).split('slots/') : [];\n if (name === undefined) continue;\n const isPage = dir === 'page/';\n\n slots.push({\n label: `${isPage ? 'Page' : 'Layout'}: ${name}`,\n hint: `only replace a part of layout${isPage ? \"'s page\" : ''}, useful for adjusting details`,\n value: {\n id,\n targets: [{ subRegistry, name: id }],\n print() {\n printSlot({\n at: `@/${id}`,\n layoutId: selected.id,\n name,\n isPage,\n uiLibrary: config.uiLibrary,\n });\n },\n },\n });\n }\n\n return select<TargetInfo>({\n message: 'Which part do you want to customize?',\n options: [\n {\n label: 'All',\n hint: 'install the entire layout',\n value: selected,\n },\n {\n label: 'Replace & rewrite from minimal styles',\n hint: 'for those who want to build their own UI from ground up',\n value: {\n id: 'docs-min',\n targets: [{ name: 'layouts/docs-min' }],\n print: () =>\n printLayout(\n config,\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n ),\n },\n },\n ...slots,\n ],\n });\n },\n },\n {\n onCancel: () => {\n cancel('Installation Stopped.');\n process.exit(0);\n },\n },\n );\n\n const targetInfo = result.target as TargetInfo;\n for (const target of targetInfo.targets) {\n await installer.installInteractive(target.name, target.subRegistry);\n }\n\n await targetInfo.print?.();\n\n outro(picocolors.bold('Have fun!'));\n}\n\n/** point the imports of route files at the installed layouts */\nexport async function rewriteLayoutImports(config: LoadedConfig, map: Map<string, string>) {\n const dir = path.join(config.baseDir, frameworks[config.framework].routesDir);\n const entries = await fs.readdir(dir, { recursive: true, withFileTypes: true }).catch(() => []);\n const updated: string[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.tsx')) continue;\n const file = path.join(entry.parentPath, entry.name);\n const source = parseSourceFile(file, await fs.readFile(file, 'utf-8'));\n for (const node of source.program.body) {\n if (node.type !== 'ImportDeclaration') continue;\n const to = map.get(node.source.value);\n if (to) source.s.overwrite(node.source.start + 1, node.source.end - 1, to);\n }\n if (!source.s.hasChanged()) continue;\n await source.save();\n updated.push(file);\n }\n\n return updated;\n}\n\nasync function printLayout(config: LoadedConfig, ...maps: [from: string, to: string][]) {\n intro(picocolors.bold('What is Next?'));\n const updated = await rewriteLayoutImports(config, new Map(maps));\n\n log.info(\n [\n 'You can check the installed layouts in `layouts` folder.',\n picocolors.dim('---'),\n updated.length > 0\n ? `Updated the imports of ${updated.join(', ')}.`\n : 'Open your `layout.tsx` files, replace the imports of components:',\n ...maps.map(([from, to]) => picocolors.greenBright(`\"${from}\" -> \"${to}\"`)),\n ].join('\\n'),\n );\n}\n\nfunction printSlot({ at, layoutId, name, isPage, uiLibrary }: SlotPrintInfo) {\n intro(picocolors.bold('What is Next?'));\n\n log.info(`You can check the installed layout slot in \"${at}\".`);\n\n const code = getSlotCode({ at, layoutId, name, isPage, uiLibrary });\n\n if (code) {\n const layoutComponent = layoutId === 'glass' ? '<GlassLayout />' : '<DocsLayout />';\n\n if (isPage) {\n log.info(\n `${picocolors.bold('At your <DocsPage /> component, update your \"slots\" prop:')}\\n\\n${code}`,\n );\n } else {\n log.info(\n `${picocolors.bold(`At your ${layoutComponent} component, update your \"slots\" prop:`)}\\n\\n${code}`,\n );\n }\n }\n}\n\nfunction getSlotCode({ at, layoutId, name, isPage, uiLibrary }: SlotPrintInfo): string | undefined {\n if (layoutId === 'glass') {\n // Glass layout wires its page slots directly, only layout-level slots are swappable.\n if (isPage) return;\n\n switch (name) {\n case 'header':\n return `import { Header } from '${at}';\n\nreturn (\n <GlassLayout\n slots={{\n header: Header,\n }}\n >\n ...\n </GlassLayout>\n);`;\n case 'sidebar': {\n // `sidebar` slot file holds the whole sidebar system (desktop, mobile drawer, provider).\n // Base UI additionally exposes a `drawerHandle` for its swipeable drawer.\n const imports = ['Sidebar', 'SidebarDrawer', 'SidebarProvider', 'useSidebar'];\n if (uiLibrary === 'base-ui') imports.push('drawerHandle');\n\n return `import { ${imports.join(', ')} } from '${at}';\n\nreturn (\n <GlassLayout\n slots={{\n sidebar: {\n main: Sidebar,\n provider: SidebarProvider,\n use: useSidebar,\n drawer: SidebarDrawer,${uiLibrary === 'base-ui' ? '\\n drawerHandle,' : ''}\n },\n }}\n >\n ...\n </GlassLayout>\n);`;\n }\n default:\n return;\n }\n }\n\n if (isPage) {\n switch (name) {\n case 'toc':\n if (layoutId === 'flux') {\n return `import { TOCProvider, TOC } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n toc: {\n provider: TOCProvider,\n main: TOC,\n },\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n\n return `import { TOCProvider, TOC, TOCPopover } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n toc: {\n provider: TOCProvider,\n main: TOC,\n popover: TOCPopover,\n },\n }}\n >\n ...\n </DocsPage>\n);`;\n case 'container': {\n return `import { Container } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n container: Container,\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n case 'footer': {\n return `import { Footer } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n footer: Footer,\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n case 'breadcrumb': {\n return `import { Breadcrumb } from '${at}';\n\nreturn (\n <DocsPage\n slots={{\n breadcrumb: Breadcrumb,\n }}\n >\n ...\n </DocsPage>\n);`;\n }\n default:\n return;\n }\n }\n\n switch (name) {\n case 'sidebar': {\n if (layoutId === 'notebook') {\n return `import {\n SidebarProvider,\n Sidebar,\n SidebarTrigger,\n SidebarCollapseTrigger,\n useSidebar,\n} from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n sidebar: {\n provider: SidebarProvider,\n root: Sidebar,\n trigger: SidebarTrigger,\n collapseTrigger: SidebarCollapseTrigger,\n useSidebar: useSidebar,\n },\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n\n return `import { SidebarProvider, Sidebar, SidebarTrigger, useSidebar } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n sidebar: {\n provider: SidebarProvider,\n root: Sidebar,\n trigger: SidebarTrigger,\n useSidebar: useSidebar,\n },\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n case 'container': {\n return `import { Container } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n container: Container,\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n case 'header': {\n return `import { Header } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n header: Header,\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n case 'tab-dropdown': {\n return `import { TabDropdown } from '${at}';\n\nreturn (\n <DocsLayout\n slots={{\n tabDropdown: TabDropdown,\n }}\n >\n ...\n </DocsLayout>\n);`;\n }\n default:\n return;\n }\n}\n","import { isCancel as isCancelSymbol } from '@clack/prompts';\n\n/**\n * `@clack/core` narrows to its own `unique symbol`, which can't exclude the wider `symbol` that\n * `@clack/prompts` returns, so a prompt result never narrows without this.\n */\nexport function isCancel<T>(value: T | symbol): value is symbol {\n return isCancelSymbol(value);\n}\n","import { autocompleteMultiselect, outro, spinner } from '@clack/prompts';\nimport { isCancel } from '@/utils/prompt';\nimport picocolors from 'picocolors';\nimport { UIRegistries } from '@/commands/shared';\nimport { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { LoadedConfig } from '@/config';\nimport { FumadocsComponentInstaller } from '@/registry/installer';\n\ninterface AddOption {\n label: string;\n value: Target;\n hint?: string;\n}\n\nexport interface Target {\n name: string;\n subRegistry?: string;\n}\n\nexport async function add(input: string[], connector: RegistryConnector, config: LoadedConfig) {\n let targets: Target[];\n const installer = new FumadocsComponentInstaller(connector, config);\n const subRegistry = UIRegistries[config.uiLibrary];\n\n if (input.length === 0) {\n const spin = spinner();\n spin.start('fetching registry');\n\n async function scan(subRegistry?: string, prefix?: string): Promise<AddOption[]> {\n const manifest = await installer.fetchManifest(subRegistry);\n const options: AddOption[] = [];\n\n for (const item of manifest.components) {\n if (item.unlisted) continue;\n options.push({\n label: `${prefix ? `${picocolors.bold(prefix)} - ` : ''}${item.title ?? item.name}`,\n value: { name: item.name, subRegistry },\n hint: item.description,\n });\n }\n return options;\n }\n\n const groups = await Promise.all([\n scan(undefined, 'common'),\n scan('sanity', 'sanity'),\n scan('openapi', 'openapi'),\n scan('api-docs', 'api-docs'),\n scan(subRegistry, 'ui'),\n ]);\n\n spin.stop(picocolors.bold(picocolors.greenBright('registry fetched')));\n const value = await autocompleteMultiselect({\n message: 'Select components to install',\n options: groups.flat().sort((a, b) => a.label.localeCompare(b.label)),\n });\n\n if (isCancel(value)) {\n outro('Ended');\n return;\n }\n\n targets = value;\n } else {\n const root = new Set<string>();\n for (const item of (await installer.fetchManifest()).components) root.add(item.name);\n targets = input.map((name) => (root.has(name) ? { name } : { subRegistry, name }));\n }\n\n for (const target of targets) {\n await installer.installInteractive(target.name, target.subRegistry);\n }\n\n outro(picocolors.bold(picocolors.greenBright('Successful')));\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { exists } from '@/utils/fs';\nimport picocolors from 'picocolors';\nimport { spinner } from '@clack/prompts';\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execAsync = promisify(exec);\n\ninterface PackageJson {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n}\n\nasync function readPackageJson(cwd: string): Promise<PackageJson | null> {\n try {\n const raw = await fs.readFile(path.join(cwd, 'package.json'), 'utf-8');\n return JSON.parse(raw) as PackageJson;\n } catch {\n return null;\n }\n}\n\n/** Path of pre-rendered EPUB, choose one according to your React framework. Next.js fetches from the running server instead. */\nconst EPUB_BUILD_PATHS: Record<string, string> = {\n next: '', // Fetched from /export/epub at runtime; route handlers don't produce static files\n astro: 'dist/export/epub',\n 'tanstack-start': '.output/public/export/epub',\n 'tanstack-start-spa': 'dist/client/export/epub',\n 'react-router': 'build/client/export/epub',\n 'react-router-spa': 'build/client/export/epub',\n waku: 'dist/public/export/epub',\n};\n\nexport async function exportEpub(options: { output?: string; framework: string }) {\n const cwd = process.cwd();\n const outputPath = path.resolve(cwd, options.output ?? 'docs.epub');\n const framework = options.framework;\n\n const spin = spinner();\n\n const buildPath = EPUB_BUILD_PATHS[framework];\n if (!(framework in EPUB_BUILD_PATHS)) {\n const valid = Object.keys(EPUB_BUILD_PATHS).join(', ');\n console.error(picocolors.red(`Invalid --framework \"${framework}\". Must be one of: ${valid}`));\n process.exit(1);\n }\n\n const pkg = await readPackageJson(cwd);\n // Check for fumadocs-epub dependency\n if (!pkg) {\n console.error(\n picocolors.red('Cannot read or parse package.json. Ensure it exists and is valid JSON.'),\n );\n process.exit(1);\n }\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (!deps['fumadocs-epub']) {\n console.log(picocolors.yellow('\\nInstalling fumadocs-epub...'));\n const packageManager = process.env.npm_execpath?.includes('pnpm')\n ? 'pnpm'\n : process.env.npm_execpath?.includes('bun')\n ? 'bun'\n : 'npm';\n const installCmd = `${packageManager} add fumadocs-epub`;\n try {\n await execAsync(installCmd, { cwd });\n } catch (err: unknown) {\n const stderr =\n err && typeof err === 'object' && 'stderr' in err\n ? String((err as { stderr?: string }).stderr)\n : '';\n console.error(picocolors.red(`Failed to install fumadocs-epub. Command: ${installCmd}`));\n if (stderr) console.error(stderr);\n process.exit(1);\n }\n }\n\n if (framework === 'next') {\n const secret = process.env.EXPORT_SECRET;\n if (!secret) {\n console.error(\n picocolors.red('EXPORT_SECRET is required for Next.js export. Set it in your environment.'),\n );\n process.exit(1);\n }\n const port = process.env.PORT || '3000';\n const url = `http://localhost:${port}/export/epub`;\n spin.start('Fetching EPUB from server');\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30_000);\n try {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${secret}` },\n signal: controller.signal,\n });\n if (!res.ok) {\n if (res.status === 401 || res.status === 403) {\n console.error(\n picocolors.red('Auth failed. Check that EXPORT_SECRET matches the value in your app.'),\n );\n } else if (res.status === 404) {\n console.error(\n picocolors.red('Export route not found, run `fumadocs feature epub` to create it.'),\n );\n } else {\n console.error(\n picocolors.red(\n `Server returned ${res.status}. Ensure the app is running (e.g. pnpm start) on port ${port}.`,\n ),\n );\n }\n process.exit(1);\n }\n const buffer = Buffer.from(await res.arrayBuffer());\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, buffer);\n spin.stop(picocolors.green(`EPUB saved to ${outputPath}`));\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n console.error(picocolors.red('Request timed out after 30 seconds.'));\n } else {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(picocolors.red(`Could not fetch EPUB: ${msg}`));\n }\n console.error(\n picocolors.yellow(`Ensure the server is running (e.g. pnpm start) on port ${port}.`),\n );\n process.exit(1);\n } finally {\n clearTimeout(timeoutId);\n }\n return;\n }\n\n const fullBuildPath = path.join(cwd, buildPath);\n if (!(await exists(fullBuildPath))) {\n console.error(\n picocolors.red(\n `EPUB not found at ${buildPath}. Run production build first (e.g. pnpm build).`,\n ),\n );\n process.exit(1);\n }\n\n spin.start('Copying EPUB');\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.copyFile(fullBuildPath, outputPath);\n spin.stop(picocolors.green(`EPUB saved to ${outputPath}`));\n}\n","import type { CAC, Command } from 'cac';\nimport { cancel, confirm, intro, log, note, outro, select, spinner } from '@clack/prompts';\nimport { isCancel } from '@/utils/prompt';\nimport picocolors from 'picocolors';\nimport type { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { createOrLoadConfig } from '@/config';\nimport { loadProject } from '@/project';\nimport { type AnyFeature, type FeatureIO, runFeature } from '@/features';\nimport { features } from '@/features/all';\n\ninterface CommandOptions extends Record<string, string | boolean | undefined> {\n config?: string;\n dir?: string;\n yes?: boolean;\n install?: boolean;\n}\n\nexport function registerFeatureCommands(\n cli: CAC,\n createConnector: (dir?: string) => RegistryConnector,\n) {\n const ids = features.map((feature) => feature.id).join(', ');\n\n register(\n cli.command('feature <id>', `configure a feature on your project (${ids})`),\n features,\n ).action(async (id: string, options: CommandOptions) => {\n const feature = features.find((item) => item.id === id);\n if (!feature) throw new Error(`unknown feature: ${id}, available: ${ids}`);\n\n await run(feature, options, createConnector(options.dir));\n });\n\n register(cli.command('init', 'set up Fumadocs on an existing app, same as `feature docs`'), [\n features[0],\n ]).action(async (options: CommandOptions) => {\n await run(features[0], options, createConnector(options.dir));\n });\n}\n\nfunction register(command: Command, list: AnyFeature[]) {\n command\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .option('-y, --yes', 'skip prompts, overwrite existing files')\n .option('--no-install', 'write dependencies to package.json without installing');\n\n for (const feature of list) {\n for (const [key, option] of Object.entries(feature.options ?? {})) {\n if ('choices' in option) {\n command.option(\n `--${key} <value>`,\n `${option.message} (${option.choices.map((choice) => choice.value).join(', ')})`,\n );\n } else {\n command.option(`--${key}`, option.message);\n }\n }\n }\n\n return command;\n}\n\nasync function run(feature: AnyFeature, options: CommandOptions, connector: RegistryConnector) {\n intro(picocolors.bgBlack(picocolors.whiteBright(feature.title)));\n const config = await createOrLoadConfig(options.config);\n const project = await loadProject(config);\n log.info(`${project.framework}${project.static ? ' (static)' : ''}, ${project.packageManager}`);\n\n const values: Record<string, unknown> = {};\n for (const [key, option] of Object.entries(feature.options ?? {})) {\n const given = options[key];\n if ('choices' in option) {\n if (given !== undefined) {\n if (!option.choices.some((choice) => choice.value === given))\n throw new Error(`invalid value for --${key}: ${given}`);\n values[key] = given;\n continue;\n }\n\n const value = await select({ message: option.message, options: option.choices });\n if (isCancel(value)) {\n cancel('Stopped.');\n process.exit(0);\n }\n values[key] = value;\n } else if (given !== undefined) {\n values[key] = given === true;\n } else if (options.yes) {\n values[key] = option.initialValue ?? false;\n } else {\n const value = await confirm({ message: option.message, initialValue: option.initialValue });\n if (isCancel(value)) {\n cancel('Stopped.');\n process.exit(0);\n }\n values[key] = value;\n }\n }\n\n const spin = spinner();\n const io: FeatureIO = {\n installDependencies: options.install !== false,\n log: (message) => spin.message(message),\n async confirmOverwrite(file) {\n if (options.yes) return true;\n spin.clear();\n const value = await confirm({ message: `Overwrite ${file}?`, initialValue: false });\n if (isCancel(value)) {\n cancel('Stopped.');\n process.exit(0);\n }\n spin.start();\n return value;\n },\n };\n\n spin.start(`Applying ${feature.title}`);\n try {\n const { notes } = await runFeature(feature, values, { project, connector, io });\n spin.stop(`${feature.title} applied`);\n if (notes.length > 0) note(notes.join('\\n\\n'), 'What is Next?');\n } catch (e) {\n spin.error(e instanceof Error ? e.message : String(e));\n process.exit(1);\n }\n outro(picocolors.bold(picocolors.greenBright('Done')));\n}\n","#!/usr/bin/env node\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { cac } from 'cac';\nimport picocolors from 'picocolors';\nimport { createOrLoadConfig, initConfig } from '@/config';\nimport { type JsonTreeNode, treeToJavaScript, treeToMdx } from '@/commands/file-tree';\nimport { runTree } from '@/utils/file-tree/run-tree';\nimport packageJson from '../package.json';\nimport { customise } from '@/commands/customise';\nimport { add } from '@/commands/add';\nimport { exportEpub } from '@/commands/export-epub';\nimport { registerFeatureCommands } from '@/commands/feature';\nimport { HttpRegistryConnector, LocalRegistryConnector } from 'fuma-cli/registry/connector';\n\nconst cli = cac('fumadocs');\ncli.option('--config <string>', 'path to the config file');\n\ncli.command('', 'init a `cli.json` config file').action(async () => {\n if (await initConfig()) {\n console.log(picocolors.green('Initialized a `./cli.json` config file.'));\n } else {\n console.log(picocolors.redBright('A config file already exists.'));\n }\n});\n\ncli\n .command('customise', 'simple way to customize layouts with Fumadocs UI')\n .alias('customize')\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .action(async (options: { config?: string; dir?: string }) => {\n const config = await createOrLoadConfig(options.config);\n await customise(config, createClientFromDir(options.dir));\n });\n\nconst dirShortcuts: Record<string, string> = {\n ':preview': 'https://preview.fumadocs.dev/registry',\n ':dev': 'http://localhost:3000/registry',\n};\n\ncli\n .command('add [...components]', 'add a new component to your docs')\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .action(async (input: string[], options: { config?: string; dir?: string }) => {\n const config = await createOrLoadConfig(options.config);\n const client = createClientFromDir(options.dir);\n await add(input, client, config);\n });\n\nregisterFeatureCommands(cli, createClientFromDir);\n\ncli\n .command(\n 'export <format>',\n 'export documentation to various formats (run after production build)',\n )\n .option('--framework <name>', 'framework: next, astro, tanstack-start, react-router, waku')\n .option('--output <path>', 'output file path', { default: 'docs.epub' })\n .action(async (format: string, options: { framework?: string; output: string }) => {\n if (format !== 'epub')\n throw new Error(`unsupported format: ${format}, only \\`epub\\` is supported.`);\n if (!options.framework) throw new Error('option `--framework <name>` is required.');\n\n await exportEpub({ framework: options.framework, output: options.output });\n });\n\ncli\n .command(\n 'tree [json_or_args] [output]',\n 'generate a file tree for the Files component, from a directory or JSON output of `tree`',\n )\n .option('--js', 'output as JavaScript file')\n .option('--no-root', 'remove the root node')\n .option('--import-name <name>', 'where to import components (JS only)')\n .action(\n async (\n str: string | undefined,\n output: string | undefined,\n { js, root, importName }: { js: boolean; root: boolean; importName?: string },\n ) => {\n const jsExtensions = ['.js', '.tsx', '.jsx'];\n const noRoot = !root;\n let nodes: JsonTreeNode[];\n\n try {\n nodes = JSON.parse(str ?? '') as JsonTreeNode[];\n } catch {\n nodes = await runTree(str ?? './');\n }\n\n const out =\n js || (output && jsExtensions.includes(path.extname(output)))\n ? treeToJavaScript(nodes, noRoot, importName)\n : treeToMdx(nodes, noRoot);\n\n if (output) {\n await fs.mkdir(path.dirname(output), { recursive: true });\n await fs.writeFile(output, out);\n } else {\n console.log(out);\n }\n },\n );\n\nfunction createClientFromDir(dir = 'https://fumadocs.dev/registry') {\n if (dir in dirShortcuts) dir = dirShortcuts[dir];\n\n return dir.startsWith('http://') || dir.startsWith('https://')\n ? new HttpRegistryConnector(dir)\n : new LocalRegistryConnector(dir);\n}\n\ncli.help();\ncli.version(packageJson.version);\n\ntry {\n cli.parse(process.argv, { run: false });\n await cli.runMatchedCommand();\n} catch (e) {\n console.error(picocolors.redBright(e instanceof Error ? e.message : String(e)));\n process.exit(1);\n}\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { InteractiveInstaller } from "@fuma-cli/interactive";
2
+ import { reuseUI } from "fuma-cli/registry/installer";
3
+ //#region src/commands/shared.ts
4
+ const UIRegistries = {
5
+ "base-ui": "fumadocs/base-ui",
6
+ "radix-ui": "fumadocs/radix-ui"
7
+ };
8
+ //#endregion
9
+ //#region src/registry/installer.ts
10
+ var FumadocsComponentInstaller = class extends InteractiveInstaller {
11
+ constructor(connector, config, cwd, io) {
12
+ const { aliases } = config;
13
+ super(connector, {
14
+ cwd,
15
+ io,
16
+ framework: config.framework === "astro" ? "none" : config.framework,
17
+ outDir: {
18
+ base: config.baseDir,
19
+ components: aliases.componentsDir,
20
+ css: aliases.cssDir,
21
+ layout: aliases.layoutDir,
22
+ lib: aliases.libDir,
23
+ ui: aliases.uiDir
24
+ },
25
+ registryAliases: { [UIRegistries["radix-ui"]]: UIRegistries[config.uiLibrary] },
26
+ plugins: [reuseUI({ files: aliases.utils ? { "utils/cn.ts": aliases.utils } : void 0 })]
27
+ });
28
+ }
29
+ };
30
+ //#endregion
31
+ export { UIRegistries as n, FumadocsComponentInstaller as t };
32
+
33
+ //# sourceMappingURL=installer-C4662Woc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer-C4662Woc.js","names":[],"sources":["../src/commands/shared.ts","../src/registry/installer.ts"],"sourcesContent":["export const UIRegistries = {\n 'base-ui': 'fumadocs/base-ui',\n 'radix-ui': 'fumadocs/radix-ui',\n};\n","import { InteractiveInstaller } from '@fuma-cli/interactive';\nimport { reuseUI, type IOInterface } from 'fuma-cli/registry/installer';\nimport type { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { UIRegistries } from '@/commands/shared';\nimport type { LoadedConfig } from '@/config';\n\nexport class FumadocsComponentInstaller extends InteractiveInstaller {\n constructor(\n connector: RegistryConnector,\n config: LoadedConfig,\n cwd?: string,\n io?: Partial<IOInterface>,\n ) {\n const { aliases } = config;\n\n super(connector, {\n cwd,\n io,\n framework: config.framework === 'astro' ? 'none' : config.framework,\n outDir: {\n base: config.baseDir,\n components: aliases.componentsDir,\n css: aliases.cssDir,\n layout: aliases.layoutDir,\n lib: aliases.libDir,\n ui: aliases.uiDir,\n },\n // other registries are compiled against Radix UI\n registryAliases: { [UIRegistries['radix-ui']]: UIRegistries[config.uiLibrary] },\n // `components/ui/*` follow the API of Shadcn UI, reuse the ones of project along with its `cn`\n plugins: [reuseUI({ files: aliases.utils ? { 'utils/cn.ts': aliases.utils } : undefined })],\n });\n }\n}\n"],"mappings":";;;AAAA,MAAa,eAAe;CAC1B,WAAW;CACX,YAAY;AACd;;;ACGA,IAAa,6BAAb,cAAgD,qBAAqB;CACnE,YACE,WACA,QACA,KACA,IACA;EACA,MAAM,EAAE,YAAY;EAEpB,MAAM,WAAW;GACf;GACA;GACA,WAAW,OAAO,cAAc,UAAU,SAAS,OAAO;GAC1D,QAAQ;IACN,MAAM,OAAO;IACb,YAAY,QAAQ;IACpB,KAAK,QAAQ;IACb,QAAQ,QAAQ;IAChB,KAAK,QAAQ;IACb,IAAI,QAAQ;GACd;GAEA,iBAAiB,GAAG,aAAa,cAAc,aAAa,OAAO,WAAW;GAE9E,SAAS,CAAC,QAAQ,EAAE,OAAO,QAAQ,QAAQ,EAAE,eAAe,QAAQ,MAAM,IAAI,KAAA,EAAU,CAAC,CAAC;EAC5F,CAAC;CACH;AACF"}
@@ -1,11 +1,10 @@
1
1
  import { r as LoadedConfig } from "../config-Bfg4yG8S.js";
2
- import { ComponentInstaller, IOInterface } from "fuma-cli/registry/installer";
2
+ import { InteractiveInstaller } from "@fuma-cli/interactive";
3
+ import { IOInterface } from "fuma-cli/registry/installer";
3
4
  import { RegistryConnector } from "fuma-cli/registry/connector";
4
5
  //#region src/registry/installer.d.ts
5
- export declare class FumadocsComponentInstaller extends ComponentInstaller {
6
- private interactive;
6
+ export declare class FumadocsComponentInstaller extends InteractiveInstaller {
7
7
  constructor(connector: RegistryConnector, config: LoadedConfig, cwd?: string, io?: Partial<IOInterface>);
8
- installInteractive(name: string, subRegistry?: string): Promise<void>;
9
8
  }
10
9
  //#endregion
11
10
  //# sourceMappingURL=installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","names":[],"sources":["../../src/registry/installer.ts"],"mappings":";;;;qBAyCa,mCAAmC;UACtC;EAKR,YACE,WAAW,mBACX,QAAQ,cACR,cACA,KAAK,QAAQ;EAyCT,mBAAmB,cAAc,uBAAuB"}
1
+ {"version":3,"file":"installer.d.ts","names":[],"sources":["../../src/registry/installer.ts"],"mappings":";;;;;qBAMa,mCAAmC;EAC9C,YACE,WAAW,mBACX,QAAQ,cACR,cACA,KAAK,QAAQ"}
@@ -1,2 +1,2 @@
1
- import { t as FumadocsComponentInstaller } from "../installer-CzhVfs7i.js";
1
+ import { t as FumadocsComponentInstaller } from "../installer-C4662Woc.js";
2
2
  export { FumadocsComponentInstaller };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fumadocs/cli",
3
- "version": "1.6.2",
3
+ "version": "1.7.0",
4
4
  "description": "The CLI tool for Fumadocs",
5
5
  "keywords": [
6
6
  "Docs",
@@ -46,8 +46,9 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "@clack/prompts": "^1.8.1",
49
+ "@fuma-cli/interactive": "^0.1.2",
49
50
  "cac": "^7.0.0",
50
- "fuma-cli": "^0.1.1",
51
+ "fuma-cli": "^0.3.1",
51
52
  "magic-string": "^1.4.1",
52
53
  "oxc-parser": "^0.150.0",
53
54
  "picocolors": "^1.1.1",
@@ -1 +0,0 @@
1
- {"version":3,"file":"features-CXFCgOWD.js","names":[],"sources":["../src/features/index.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { x } from 'tinyexec';\nimport type { RegistryConnector } from 'fuma-cli/registry/connector';\nimport type { Project } from '@/project';\nimport { createSourceFile, type SourceFile } from '@/codemod';\nimport { FumadocsComponentInstaller } from '@/registry/installer';\nimport { exists } from '@/utils/fs';\n\nexport interface SelectOption<T> {\n message: string;\n choices: { value: T; label: string; hint?: string }[];\n}\n\nexport interface ConfirmOption {\n message: string;\n initialValue?: boolean;\n}\n\nexport type FeatureOption<T> = [T] extends [boolean] ? ConfirmOption : SelectOption<T>;\n\nexport interface Feature<Options extends object = Record<never, never>> {\n id: string;\n title: string;\n description: string;\n options?: { [K in keyof Options]: FeatureOption<Options[K]> };\n /** features that must be applied first */\n requires?: AnyFeature[];\n /** whether the feature is already applied to the project */\n detect?: (project: Project) => Promise<boolean>;\n /** @returns `true` if supported, otherwise the reason */\n supports?: (project: Project) => true | string;\n apply(ctx: FeatureContext, options: Options): Promise<void>;\n}\n\nexport type AnyFeature = Omit<Feature<Record<string, unknown>>, 'options'> & {\n options?: Record<string, SelectOption<unknown> | ConfirmOption>;\n};\n\nexport interface PackageJson {\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport interface FeatureContext {\n project: Project;\n /** write a file (relative to project root), asks before overwriting */\n write: (file: string, content: string) => Promise<void>;\n /** edit a file with codemods, `false` if the file doesn't exist */\n source: (file: string, edit: (file: SourceFile) => void) => Promise<boolean>;\n /** append lines to a text file, skip lines already present */\n append: (file: string, lines: string[]) => Promise<void>;\n remove: (file: string) => Promise<void>;\n packageJson: (edit: (data: PackageJson) => void) => Promise<void>;\n /** install a registry component */\n install: (name: string, subRegistry?: string) => Promise<void>;\n /** `null` to install the latest version */\n addDependencies: (deps: Record<string, string | null>, dev?: boolean) => void;\n env: (key: string, value: string) => void;\n /** instructions to show after the feature is applied */\n note: (text: string) => void;\n log: (message: string) => void;\n}\n\nexport interface FeatureIO {\n log: (message: string) => void;\n confirmOverwrite: (file: string) => Promise<boolean>;\n /** run package manager to install dependencies, otherwise only write them into `package.json` */\n installDependencies: boolean;\n}\n\nexport interface RunOptions {\n project: Project;\n connector: RegistryConnector;\n io: FeatureIO;\n /** preferred versions of dependencies, when a feature doesn't specify one */\n versions?: Record<string, string>;\n}\n\nexport async function runFeature<O extends object>(\n feature: Omit<Feature<O>, 'options'>,\n options: O,\n { project, connector, io, versions }: RunOptions,\n): Promise<{ notes: string[] }> {\n for (const dep of feature.requires ?? []) {\n if (dep.detect && !(await dep.detect(project)))\n throw new Error(\n `\"${feature.id}\" requires \"${dep.id}\", run \\`fumadocs feature ${dep.id}\\` first.`,\n );\n }\n const supported = feature.supports?.(project) ?? true;\n if (supported !== true) throw new Error(`\"${feature.id}\" is unavailable: ${supported}`);\n\n const { cwd } = project;\n const deps: Record<string, string | null> = {};\n const devDeps: Record<string, string | null> = {};\n const env: string[] = [];\n const notes: string[] = [];\n const installer = new FumadocsComponentInstaller(connector, project.config, cwd, {\n onWarn: io.log,\n confirmFileOverride: ({ path: file }) => io.confirmOverwrite(path.relative(cwd, file)),\n onFileDownloaded: ({ path: file }) => io.log(`installed ${path.relative(cwd, file)}`),\n });\n\n const ctx: FeatureContext = {\n project,\n log: io.log,\n note: (text) => notes.push(text),\n env: (key, value) => env.push(`${key}=${value}`),\n addDependencies(input, dev) {\n Object.assign(dev ? devDeps : deps, input);\n },\n async write(file, content) {\n const target = path.join(cwd, file);\n const current = await fs.readFile(target, 'utf-8').catch(() => null);\n if (current !== null) {\n if (current.trim() === content.trim()) return;\n if (!(await io.confirmOverwrite(file))) return;\n }\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, content);\n io.log(`${current === null ? 'created' : 'updated'} ${file}`);\n },\n async source(file, edit) {\n const target = path.join(cwd, file);\n if (!(await exists(target))) return false;\n const source = await createSourceFile(target);\n edit(source);\n if (source.s.hasChanged()) {\n await source.save();\n io.log(`updated ${file}`);\n }\n return true;\n },\n async append(file, lines) {\n const target = path.join(cwd, file);\n const current = await fs.readFile(target, 'utf-8').catch(() => '');\n const missing = lines.filter((line) => !current.includes(line));\n if (missing.length === 0) return;\n const separator = current.length === 0 || current.endsWith('\\n') ? '' : '\\n';\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, `${current}${separator}${missing.join('\\n')}\\n`);\n io.log(`updated ${file}`);\n },\n async remove(file) {\n await fs.rm(path.join(cwd, file), { force: true });\n io.log(`removed ${file}`);\n },\n async packageJson(edit) {\n const target = path.join(cwd, 'package.json');\n const data = JSON.parse(await fs.readFile(target, 'utf-8'));\n edit(data);\n await fs.writeFile(target, `${JSON.stringify(data, null, 2)}\\n`);\n io.log('updated package.json');\n },\n async install(name, subRegistry) {\n const result = await installer.install(name, subRegistry);\n const manager = await result.deps();\n // encoded as `name` or `name@version`\n const decode = (target: Record<string, string | null>, dep: string) => {\n const at = dep.indexOf('@', 1);\n if (at === -1) target[dep] = null;\n else target[dep.slice(0, at)] = dep.slice(at + 1);\n };\n for (const dep of manager.dependencies) decode(deps, dep);\n for (const dep of manager.devDependencies) decode(devDeps, dep);\n },\n };\n\n await feature.apply(ctx, options);\n\n if (env.length > 0) await ctx.append('.env.local', env);\n await installDependencies(project, deps, devDeps, io, versions);\n return { notes };\n}\n\nasync function installDependencies(\n project: Project,\n deps: Record<string, string | null>,\n devDeps: Record<string, string | null>,\n io: FeatureIO,\n versions: Record<string, string> = {},\n) {\n const { dependencies = {}, devDependencies = {} } = project.packageJson;\n const filter = (input: Record<string, string | null>) =>\n Object.entries(input).filter(([name]) => !(name in dependencies) && !(name in devDependencies));\n const missing = filter(deps);\n const missingDev = filter(devDeps);\n if (missing.length === 0 && missingDev.length === 0) return;\n\n if (io.installDependencies) {\n const pm = project.packageManager;\n const run = async (flags: string[], entries: [string, string | null][]) => {\n if (entries.length === 0) return;\n const args = [\n 'add',\n ...flags,\n ...entries.map(([name, version]) => (version ? `${name}@${version}` : name)),\n ];\n io.log(`${pm} ${args.join(' ')}`);\n await x(pm, args, {\n nodeOptions: { cwd: project.cwd, stdio: 'inherit' },\n throwOnError: true,\n });\n };\n await run([], missing);\n await run(['-D'], missingDev);\n return;\n }\n\n const file = path.join(project.cwd, 'package.json');\n const json = JSON.parse(await fs.readFile(file, 'utf-8'));\n const write = (field: string, entries: [string, string | null][]) => {\n if (entries.length === 0) return;\n const target = (json[field] ??= {});\n for (const [name, version] of entries) target[name] = version ?? versions[name] ?? 'latest';\n };\n write('dependencies', missing);\n write('devDependencies', missingDev);\n await fs.writeFile(file, `${JSON.stringify(json, null, 2)}\\n`);\n io.log('updated package.json');\n}\n"],"mappings":"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"}
@@ -1,204 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import path from "node:path";
3
- import { detectPackageManager } from "fuma-cli/detect";
4
- import picocolors from "picocolors";
5
- import { box, confirm, isCancel, log, outro, spinner } from "@clack/prompts";
6
- import { ComponentInstaller } from "fuma-cli/registry/installer";
7
- //#region src/commands/shared.ts
8
- const UIRegistries = {
9
- "base-ui": "fumadocs/base-ui",
10
- "radix-ui": "fumadocs/radix-ui"
11
- };
12
- //#endregion
13
- //#region src/registry/plugins/preserve.ts
14
- /**
15
- * keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them directly.
16
- */
17
- function pluginPreserveLayouts() {
18
- const layoutNames = [
19
- "layouts/home",
20
- "layouts/flux",
21
- "layouts/glass",
22
- "layouts/notebook",
23
- "layouts/docs",
24
- "layouts/shared"
25
- ];
26
- const layoutComps = {
27
- "local:<dir>/home/index.tsx": "layouts/home",
28
- "local:<dir>/shared/index.tsx": "layouts/shared",
29
- "local:<dir>/shared/client.tsx": "layouts/shared",
30
- "local:<dir>/notebook/index.tsx": "layouts/notebook",
31
- "local:<dir>/notebook/client.tsx": "layouts/notebook",
32
- "local:<dir>/notebook/page/index.tsx": "layouts/notebook/page",
33
- "local:<dir>/notebook/page/client.tsx": "layouts/notebook/page",
34
- "local:<dir>/docs/index.tsx": "layouts/docs",
35
- "local:<dir>/docs/client.tsx": "layouts/docs",
36
- "local:<dir>/docs/page/index.tsx": "layouts/docs/page",
37
- "local:<dir>/docs/page/client.tsx": "layouts/docs/page",
38
- "local:<dir>/flux/index.tsx": "layouts/flux",
39
- "local:<dir>/flux/page/index.tsx": "layouts/flux/page",
40
- "local:<dir>/flux/page/client.tsx": "layouts/flux/page",
41
- "local:<dir>/glass/index.tsx": "layouts/glass",
42
- "local:<dir>/glass/page/index.tsx": "layouts/glass/page"
43
- };
44
- const layoutNameSet = new Set(layoutNames);
45
- return {
46
- beforeInstall(comp, { stack }) {
47
- if (layoutNameSet.has(stack[0].name)) return;
48
- return {
49
- ...comp,
50
- $subComponents: comp.$subComponents.filter((child) => !layoutNameSet.has(child.name))
51
- };
52
- },
53
- transformImport(specifier, { stack }) {
54
- if (layoutNameSet.has(stack[0].name) || !(specifier in layoutComps)) return specifier;
55
- return `fumadocs-ui/${layoutComps[specifier]}`;
56
- }
57
- };
58
- }
59
- //#endregion
60
- //#region src/registry/plugins/shadcn.ts
61
- /**
62
- * `components/ui/*` follow the API of Shadcn UI, reuse the ones the project already has along with its `cn`.
63
- */
64
- function pluginReuseUI(config, cwd = process.cwd()) {
65
- const { baseDir, aliases } = config;
66
- const utils = aliases.utils && path.resolve(cwd, baseDir, aliases.utils);
67
- return {
68
- beforeInstall(comp) {
69
- const files = comp.files.filter((file) => {
70
- if (file.type === "lib") return !(utils && file.path === "utils/cn.ts");
71
- if (file.type !== "ui") return true;
72
- return !existsSync(path.resolve(cwd, baseDir, aliases.uiDir, path.basename(file.path)));
73
- });
74
- if (files.length === comp.files.length) return;
75
- if (files.length === 0) return {
76
- ...comp,
77
- files,
78
- dependencies: {},
79
- devDependencies: {}
80
- };
81
- return {
82
- ...comp,
83
- files
84
- };
85
- },
86
- transformImport(specifier, { filePath }) {
87
- if (!utils || specifier !== "local:utils/cn.ts") return specifier;
88
- const relative = path.relative(path.dirname(filePath), utils).replaceAll(path.sep, "/");
89
- return relative.startsWith(".") ? relative : `./${relative}`;
90
- }
91
- };
92
- }
93
- //#endregion
94
- //#region src/utils/prompt.ts
95
- /**
96
- * `@clack/core` narrows to its own `unique symbol`, which can't exclude the wider `symbol` that
97
- * `@clack/prompts` returns, so a prompt result never narrows without this.
98
- */
99
- function isCancel$1(value) {
100
- return isCancel(value);
101
- }
102
- //#endregion
103
- //#region src/registry/installer.ts
104
- const uiRegistries = new Set(Object.values(UIRegistries));
105
- /** components are compiled against one UI library, fetch them from the sub-registry of the configured one */
106
- var UILibraryConnector = class {
107
- constructor(base, ui) {
108
- this.base = base;
109
- this.ui = ui;
110
- this.id = base.id;
111
- }
112
- resolve(subRegistry) {
113
- return subRegistry && uiRegistries.has(subRegistry) ? this.ui : subRegistry;
114
- }
115
- fetchRegistryInfo(subRegistry) {
116
- return this.base.fetchRegistryInfo(this.resolve(subRegistry));
117
- }
118
- fetchComponent(name, subRegistry) {
119
- return this.base.fetchComponent(name, this.resolve(subRegistry));
120
- }
121
- hasComponent(name, subRegistry) {
122
- return this.base.hasComponent(name, this.resolve(subRegistry));
123
- }
124
- };
125
- var FumadocsComponentInstaller = class extends ComponentInstaller {
126
- constructor(connector, config, cwd, io) {
127
- super(new UILibraryConnector(connector, UIRegistries[config.uiLibrary]), {
128
- cwd,
129
- framework: config.framework === "astro" ? "none" : config.framework,
130
- outDir: {
131
- base: config.baseDir,
132
- components: config.aliases.componentsDir,
133
- css: config.aliases.cssDir,
134
- layout: config.aliases.layoutDir,
135
- lib: config.aliases.libDir,
136
- ui: config.aliases.uiDir
137
- },
138
- io: {
139
- onWarn: (message) => {
140
- this.interactive?.spin.message(message);
141
- },
142
- confirmFileOverride: async (options) => {
143
- if (!this.interactive) return true;
144
- const { name, spin } = this.interactive;
145
- spin.clear();
146
- const value = await confirm({
147
- message: `Do you want to override ${options.path}?`,
148
- initialValue: false
149
- });
150
- if (isCancel$1(value)) {
151
- outro("Installation terminated");
152
- process.exit(0);
153
- }
154
- spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));
155
- return value;
156
- },
157
- onFileDownloaded: (options) => {
158
- this.interactive?.spin.message(options.path);
159
- },
160
- ...io
161
- },
162
- plugins: [pluginPreserveLayouts(), pluginReuseUI(config, cwd)]
163
- });
164
- this.interactive = null;
165
- }
166
- async installInteractive(name, subRegistry) {
167
- if (this.interactive) throw new Error(`cannot install while installing another component`);
168
- const spin = spinner();
169
- spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));
170
- try {
171
- this.interactive = {
172
- name,
173
- spin
174
- };
175
- const deps = await super.install(name, subRegistry).then((res) => res.deps());
176
- spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));
177
- if (deps.hasRequired()) {
178
- log.message();
179
- box([...deps.dependencies, ...deps.devDependencies].join("\n"), "New Dependencies");
180
- const pm = (await detectPackageManager())?.name ?? "npm";
181
- const value = await confirm({ message: `Do you want to install with ${pm}?` });
182
- if (isCancel$1(value)) {
183
- outro("Installation terminated");
184
- process.exit(0);
185
- }
186
- if (value) {
187
- const spin = spinner({ errorMessage: "Failed to install dependencies" });
188
- spin.start("Installing dependencies");
189
- await deps.installRequired(pm);
190
- spin.stop("Dependencies installed");
191
- } else await deps.writeRequired();
192
- }
193
- } catch (e) {
194
- spin.error(e instanceof Error ? e.message : String(e));
195
- process.exit(-1);
196
- } finally {
197
- this.interactive = null;
198
- }
199
- }
200
- };
201
- //#endregion
202
- export { isCancel$1 as n, UIRegistries as r, FumadocsComponentInstaller as t };
203
-
204
- //# sourceMappingURL=installer-CzhVfs7i.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"installer-CzhVfs7i.js","names":["isCancel","isCancelSymbol","isCancel"],"sources":["../src/commands/shared.ts","../src/registry/plugins/preserve.ts","../src/registry/plugins/shadcn.ts","../src/utils/prompt.ts","../src/registry/installer.ts"],"sourcesContent":["export const UIRegistries = {\n 'base-ui': 'fumadocs/base-ui',\n 'radix-ui': 'fumadocs/radix-ui',\n};\n","import type { InstallerPlugin } from 'fuma-cli/registry/installer';\n\n/**\n * keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them directly.\n */\nexport function pluginPreserveLayouts(): InstallerPlugin {\n const layoutNames = [\n 'layouts/home',\n 'layouts/flux',\n 'layouts/glass',\n 'layouts/notebook',\n 'layouts/docs',\n 'layouts/shared',\n ];\n // original specifier -> new specifier\n const layoutComps: Record<string, string> = {\n 'local:<dir>/home/index.tsx': 'layouts/home',\n 'local:<dir>/shared/index.tsx': 'layouts/shared',\n 'local:<dir>/shared/client.tsx': 'layouts/shared',\n 'local:<dir>/notebook/index.tsx': 'layouts/notebook',\n 'local:<dir>/notebook/client.tsx': 'layouts/notebook',\n 'local:<dir>/notebook/page/index.tsx': 'layouts/notebook/page',\n 'local:<dir>/notebook/page/client.tsx': 'layouts/notebook/page',\n 'local:<dir>/docs/index.tsx': 'layouts/docs',\n 'local:<dir>/docs/client.tsx': 'layouts/docs',\n 'local:<dir>/docs/page/index.tsx': 'layouts/docs/page',\n 'local:<dir>/docs/page/client.tsx': 'layouts/docs/page',\n 'local:<dir>/flux/index.tsx': 'layouts/flux',\n 'local:<dir>/flux/page/index.tsx': 'layouts/flux/page',\n 'local:<dir>/flux/page/client.tsx': 'layouts/flux/page',\n 'local:<dir>/glass/index.tsx': 'layouts/glass',\n 'local:<dir>/glass/page/index.tsx': 'layouts/glass/page',\n };\n const layoutNameSet = new Set(layoutNames);\n\n return {\n beforeInstall(comp, { stack }) {\n const isDirectInstall = layoutNameSet.has(stack[0].name);\n if (isDirectInstall) return;\n\n return {\n ...comp,\n $subComponents: comp.$subComponents.filter((child) => !layoutNameSet.has(child.name)),\n };\n },\n transformImport(specifier, { stack }) {\n const isDirectInstall = layoutNameSet.has(stack[0].name);\n // skip if direct install or unrelated to layout component\n if (isDirectInstall || !(specifier in layoutComps)) return specifier;\n\n return `fumadocs-ui/${layoutComps[specifier]}`;\n },\n };\n}\n","import { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport type { InstallerPlugin } from 'fuma-cli/registry/installer';\nimport type { LoadedConfig } from '@/config';\n\n/**\n * `components/ui/*` follow the API of Shadcn UI, reuse the ones the project already has along with its `cn`.\n */\nexport function pluginReuseUI(config: LoadedConfig, cwd = process.cwd()): InstallerPlugin {\n const { baseDir, aliases } = config;\n const utils = aliases.utils && path.resolve(cwd, baseDir, aliases.utils);\n\n return {\n beforeInstall(comp) {\n const files = comp.files.filter((file) => {\n if (file.type === 'lib') return !(utils && file.path === 'utils/cn.ts');\n if (file.type !== 'ui') return true;\n return !existsSync(path.resolve(cwd, baseDir, aliases.uiDir, path.basename(file.path)));\n });\n\n if (files.length === comp.files.length) return;\n // a fully reused component brings no dependencies\n if (files.length === 0) return { ...comp, files, dependencies: {}, devDependencies: {} };\n return { ...comp, files };\n },\n transformImport(specifier, { filePath }) {\n if (!utils || specifier !== 'local:utils/cn.ts') return specifier;\n const relative = path.relative(path.dirname(filePath), utils).replaceAll(path.sep, '/');\n return relative.startsWith('.') ? relative : `./${relative}`;\n },\n };\n}\n","import { isCancel as isCancelSymbol } from '@clack/prompts';\n\n/**\n * `@clack/core` narrows to its own `unique symbol`, which can't exclude the wider `symbol` that\n * `@clack/prompts` returns, so a prompt result never narrows without this.\n */\nexport function isCancel<T>(value: T | symbol): value is symbol {\n return isCancelSymbol(value);\n}\n","import { ComponentInstaller, type IOInterface } from 'fuma-cli/registry/installer';\nimport { pluginPreserveLayouts } from './plugins/preserve';\nimport { pluginReuseUI } from './plugins/shadcn';\nimport type { RegistryConnector } from 'fuma-cli/registry/connector';\nimport { UIRegistries } from '@/commands/shared';\nimport type { LoadedConfig } from '@/config';\nimport { box, confirm, log, outro, spinner, SpinnerResult } from '@clack/prompts';\nimport { isCancel } from '@/utils/prompt';\nimport picocolors from 'picocolors';\nimport { detectPackageManager } from 'fuma-cli/detect';\n\nconst uiRegistries = new Set(Object.values(UIRegistries));\n\n/** components are compiled against one UI library, fetch them from the sub-registry of the configured one */\nclass UILibraryConnector implements RegistryConnector {\n readonly id: string;\n\n constructor(\n private readonly base: RegistryConnector,\n private readonly ui: string,\n ) {\n this.id = base.id;\n }\n\n private resolve(subRegistry?: string) {\n return subRegistry && uiRegistries.has(subRegistry) ? this.ui : subRegistry;\n }\n\n fetchRegistryInfo(subRegistry?: string) {\n return this.base.fetchRegistryInfo(this.resolve(subRegistry));\n }\n\n fetchComponent(name: string, subRegistry?: string) {\n return this.base.fetchComponent(name, this.resolve(subRegistry));\n }\n\n hasComponent(name: string, subRegistry?: string) {\n return this.base.hasComponent(name, this.resolve(subRegistry));\n }\n}\n\nexport class FumadocsComponentInstaller extends ComponentInstaller {\n private interactive: {\n name: string;\n spin: SpinnerResult;\n } | null = null;\n\n constructor(\n connector: RegistryConnector,\n config: LoadedConfig,\n cwd?: string,\n io?: Partial<IOInterface>,\n ) {\n super(new UILibraryConnector(connector, UIRegistries[config.uiLibrary]), {\n cwd,\n framework: config.framework === 'astro' ? 'none' : config.framework,\n outDir: {\n base: config.baseDir,\n components: config.aliases.componentsDir,\n css: config.aliases.cssDir,\n layout: config.aliases.layoutDir,\n lib: config.aliases.libDir,\n ui: config.aliases.uiDir,\n },\n io: {\n onWarn: (message) => {\n this.interactive?.spin.message(message);\n },\n confirmFileOverride: async (options) => {\n if (!this.interactive) return true;\n const { name, spin } = this.interactive;\n spin.clear();\n const value = await confirm({\n message: `Do you want to override ${options.path}?`,\n initialValue: false,\n });\n if (isCancel(value)) {\n outro('Installation terminated');\n process.exit(0);\n }\n spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));\n return value;\n },\n onFileDownloaded: (options) => {\n this.interactive?.spin.message(options.path);\n },\n ...io,\n },\n plugins: [pluginPreserveLayouts(), pluginReuseUI(config, cwd)],\n });\n }\n\n async installInteractive(name: string, subRegistry?: string): Promise<void> {\n if (this.interactive) {\n throw new Error(`cannot install while installing another component`);\n }\n\n const spin = spinner();\n spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));\n\n try {\n this.interactive = { name, spin };\n const deps = await super.install(name, subRegistry).then((res) => res.deps());\n spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));\n\n if (deps.hasRequired()) {\n log.message();\n box([...deps.dependencies, ...deps.devDependencies].join('\\n'), 'New Dependencies');\n const pm = (await detectPackageManager())?.name ?? 'npm';\n const value = await confirm({\n message: `Do you want to install with ${pm}?`,\n });\n\n if (isCancel(value)) {\n outro('Installation terminated');\n process.exit(0);\n }\n\n if (value) {\n const spin = spinner({\n errorMessage: 'Failed to install dependencies',\n });\n spin.start('Installing dependencies');\n await deps.installRequired(pm);\n spin.stop('Dependencies installed');\n } else {\n await deps.writeRequired();\n }\n }\n } catch (e) {\n spin.error(e instanceof Error ? e.message : String(e));\n process.exit(-1);\n } finally {\n this.interactive = null;\n }\n }\n}\n"],"mappings":"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"}