@fumadocs/cli 1.3.3 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +53 -2
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +9 -6
- package/dist/config.js.map +1 -1
- package/dist/index.js +87 -157
- package/dist/index.js.map +1 -1
- package/dist/installer-zD4jTQWp.js +128 -0
- package/dist/installer-zD4jTQWp.js.map +1 -0
- package/dist/registry/installer.d.ts +13 -0
- package/dist/registry/installer.d.ts.map +1 -0
- package/dist/registry/installer.js +2 -0
- package/package.json +7 -15
- package/dist/build/index.d.ts +0 -148
- package/dist/build/index.d.ts.map +0 -1
- package/dist/build/index.js +0 -289
- package/dist/build/index.js.map +0 -1
- package/dist/client-C2A4Jf2w.js +0 -102
- package/dist/client-C2A4Jf2w.js.map +0 -1
- package/dist/config-Bx-m6awG.d.ts +0 -53
- package/dist/config-Bx-m6awG.d.ts.map +0 -1
- package/dist/fs-C3j4H046.js +0 -18
- package/dist/fs-C3j4H046.js.map +0 -1
- package/dist/import-CSbSteB3.js +0 -124
- package/dist/import-CSbSteB3.js.map +0 -1
- package/dist/installer-BWoLnsXE.js +0 -673
- package/dist/installer-BWoLnsXE.js.map +0 -1
- package/dist/registry/client.d.ts +0 -112
- package/dist/registry/client.d.ts.map +0 -1
- package/dist/registry/client.js +0 -2
- package/dist/registry/installer/index.d.ts +0 -96
- package/dist/registry/installer/index.d.ts.map +0 -1
- package/dist/registry/installer/index.js +0 -2
- package/dist/registry/macros/route-handler.d.ts +0 -21
- package/dist/registry/macros/route-handler.d.ts.map +0 -1
- package/dist/registry/macros/route-handler.js +0 -11
- package/dist/registry/macros/route-handler.js.map +0 -1
- package/dist/registry/schema.d.ts +0 -2
- package/dist/registry/schema.js +0 -50
- package/dist/registry/schema.js.map +0 -1
- package/dist/schema-BAaUX4uu.d.ts +0 -77
- package/dist/schema-BAaUX4uu.d.ts.map +0 -1
- package/dist/types-79PW0lgM.d.ts +0 -6
- package/dist/types-79PW0lgM.d.ts.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["packageJson.version"],"sources":["../src/commands/file-tree.ts","../src/utils/file-tree/run-tree.ts","../package.json","../src/commands/shared.ts","../src/registry/plugins/preserve.ts","../src/commands/add.ts","../src/commands/customise.ts","../src/commands/export-epub.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","","export const UIRegistries = {\n 'base-ui': 'fumadocs/base-ui',\n 'radix-ui': 'fumadocs/radix-ui',\n};\n","import type { ComponentInstallerPlugin } from '../installer';\n\n/**\n * keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them directly.\n */\nexport function pluginPreserveLayouts(): ComponentInstallerPlugin {\n const layoutNames = [\n 'layouts/home',\n 'layouts/flux',\n 'layouts/notebook',\n 'layouts/docs',\n 'layouts/shared',\n ];\n // original specifier -> new specifier\n const layoutComps: Record<string, string> = {\n '@/<dir>/home/index.tsx': 'layouts/home',\n '@/<dir>/shared/index.tsx': 'layouts/shared',\n '@/<dir>/shared/client.tsx': 'layouts/shared',\n '@/<dir>/notebook/index.tsx': 'layouts/notebook',\n '@/<dir>/notebook/client.tsx': 'layouts/notebook',\n '@/<dir>/notebook/page/index.tsx': 'layouts/notebook/page',\n '@/<dir>/notebook/page/client.tsx': 'layouts/notebook/page',\n '@/<dir>/docs/index.tsx': 'layouts/docs',\n '@/<dir>/docs/client.tsx': 'layouts/docs',\n '@/<dir>/docs/page/index.tsx': 'layouts/docs/page',\n '@/<dir>/docs/page/client.tsx': 'layouts/docs/page',\n '@/<dir>/flux/index.tsx': 'layouts/flux',\n '@/<dir>/flux/page/index.tsx': 'layouts/flux/page',\n '@/<dir>/flux/page/client.tsx': 'layouts/flux/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 {\n isCancel,\n autocompleteMultiselect,\n outro,\n spinner,\n confirm,\n box,\n log,\n} from '@clack/prompts';\nimport picocolors from 'picocolors';\nimport { ComponentInstaller } from '@/registry/installer';\nimport type { RegistryClient } from '@/registry/client';\nimport { UIRegistries } from '@/commands/shared';\nimport { pluginPreserveLayouts } from '@/registry/plugins/preserve';\nimport { detect } from 'package-manager-detector';\n\nexport async function add(input: string[], client: RegistryClient) {\n const config = client.config;\n let target: string[];\n const installer = new ComponentInstaller(client, {\n plugins: [pluginPreserveLayouts()],\n });\n const registry = UIRegistries[config.uiLibrary];\n\n if (input.length === 0) {\n const spin = spinner();\n spin.start('fetching registry');\n const info = await client.fetchRegistryInfo();\n const options: {\n label: string;\n value: string;\n hint?: string;\n }[] = [];\n\n for (const item of info.indexes) {\n options.push({\n label: item.title ?? item.name,\n value: item.name,\n hint: item.description,\n });\n }\n const { indexes } = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();\n\n for (const item of indexes) {\n options.push({\n label: item.title ?? item.name,\n value: `${registry}/${item.name}`,\n hint: item.description,\n });\n }\n\n spin.stop(picocolors.bold(picocolors.greenBright('registry fetched')));\n const value = await autocompleteMultiselect({\n message: 'Select components to install',\n options,\n });\n\n if (isCancel(value)) {\n outro('Ended');\n return;\n }\n\n target = value;\n } else {\n target = await Promise.all(\n input.map(async (item) => ((await client.hasComponent(item)) ? item : `${registry}/${item}`)),\n );\n }\n\n await install(target, installer);\n}\n\nexport async function install(target: string[], installer: ComponentInstaller) {\n for (const name of target) {\n const spin = spinner();\n spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));\n\n try {\n await installer.install(name, {\n onWarn(message) {\n spin.message(message);\n },\n async confirmFileOverride(options) {\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 spin.message(options.path);\n },\n });\n spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));\n } catch (e) {\n spin.error(e instanceof Error ? e.message : String(e));\n process.exit(-1);\n }\n }\n\n const deps = await installer.deps();\n if (deps.hasRequired()) {\n log.message();\n box([...deps.dependencies, ...deps.devDependencies].join('\\n'), 'New Dependencies');\n const pm = (await detect())?.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\n await installer.onEnd();\n outro(picocolors.bold(picocolors.greenBright('Successful')));\n}\n","import { cancel, group, intro, log, outro, select } from '@clack/prompts';\nimport picocolors from 'picocolors';\nimport { install } from '@/commands/add';\nimport type { RegistryClient } from '@/registry/client';\nimport { ComponentInstaller } from '@/registry/installer';\nimport { UIRegistries } from '@/commands/shared';\nimport { pluginPreserveLayouts } from '@/registry/plugins/preserve';\n\ninterface TargetInfo {\n target: string[];\n id: string;\n print?: () => void;\n}\n\ninterface SlotPrintInfo {\n at: string;\n layoutId: string;\n name: string;\n isPage: boolean;\n}\n\nexport async function customise(client: RegistryClient) {\n intro(picocolors.bgBlack(picocolors.whiteBright('Customise Fumadocs UI')));\n const config = client.config;\n const installer = new ComponentInstaller(client, {\n plugins: [pluginPreserveLayouts()],\n });\n const registry = UIRegistries[config.uiLibrary];\n const info = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();\n\n const result = await group(\n {\n layout: (): Promise<TargetInfo | symbol> =>\n select({\n message: 'What do you want to customise?',\n options: [\n {\n label: 'Docs Layout',\n value: {\n id: 'docs',\n target: [`${registry}/layouts/docs`],\n print() {\n printLayout(\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n );\n },\n },\n hint: 'the default docs layout',\n },\n {\n label: 'Notebook Layout',\n value: {\n id: 'notebook',\n target: [`${registry}/layouts/notebook`],\n print() {\n printLayout(\n ['fumadocs-ui/layouts/notebook', '@/layouts/notebook'],\n ['fumadocs-ui/layouts/notebook/page', '@/layouts/notebook/page'],\n );\n },\n },\n hint: 'a more compact version of docs layout',\n },\n {\n label: 'Flux Layout',\n value: {\n id: 'flux',\n target: [`${registry}/layouts/flux`],\n print() {\n printLayout(\n ['fumadocs-ui/layouts/flux', '@/layouts/flux'],\n ['fumadocs-ui/layouts/flux/page', '@/layouts/flux/page'],\n );\n },\n },\n hint: 'the experimental variant of docs layout',\n },\n {\n label: 'Home Layout',\n value: {\n id: 'home',\n target: [`${registry}/layouts/home`],\n print() {\n printLayout(['fumadocs-ui/layouts/home', `@/layouts/home`]);\n },\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 customise?',\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 target: ['layouts/docs-min'],\n print() {\n printLayout(\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n );\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 target: [`${registry}/${index.name}`],\n print() {\n printSlot({\n at: `@/layouts/${selected.id}/page/slots/${name}`,\n layoutId: selected.id,\n name,\n isPage: true,\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 target: [`${registry}/${index.name}`],\n print() {\n printSlot({\n at: `@/layouts/${selected.id}/slots/${name}`,\n layoutId: selected.id,\n name,\n isPage: false,\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 target = result.target as TargetInfo;\n await install(target.target, installer);\n target.print?.();\n\n outro(picocolors.bold('Have fun!'));\n}\n\nfunction printLayout(...maps: [from: string, to: string][]) {\n intro(picocolors.bold('What is Next?'));\n\n log.info(\n [\n 'You can check the installed layouts in `layouts` folder.',\n picocolors.dim('---'),\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 }: 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 });\n\n if (code) {\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 <DocsLayout /> component, update your \"slots\" prop:')}\\n\\n${code}`,\n );\n }\n }\n}\n\nfunction getSlotCode({ at, layoutId, name, isPage }: SlotPrintInfo): string | undefined {\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 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 '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\nconst API_ROUTE_TEMPLATE = `import { source } from '@/lib/source';\nimport { exportEpub } from 'fumadocs-epub';\n\nexport const revalidate = false;\n\nexport async function GET(request: Request): Promise<Response> {\n // Require EXPORT_SECRET to prevent unauthenticated abuse. Pass via Authorization: Bearer <secret>\n const secret = process.env.EXPORT_SECRET;\n if (!secret) {\n return new Response('EXPORT_SECRET is not configured. Set it in your environment to protect this endpoint.', { status: 503 });\n }\n const authHeader = request.headers.get('authorization');\n const token = authHeader?.replace(/^Bearer\\\\s+/i, '') ?? '';\n if (token !== secret) {\n return new Response('Unauthorized', { status: authHeader ? 403 : 401 });\n }\n const buffer = await exportEpub({\n source,\n title: 'Documentation',\n author: 'Your Team',\n description: 'Exported documentation',\n cover: '/cover.png',\n });\n return new Response(new Uint8Array(buffer), {\n headers: {\n 'Content-Type': 'application/epub+zip',\n 'Content-Disposition': 'attachment; filename=\"docs.epub\"',\n },\n });\n}\n`;\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n const appPaths = ['app', 'src/app'];\n for (const appPath of appPaths) {\n const fullPath = path.join(cwd, appPath);\n if (await exists(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\nasync function scaffoldEpubRoute(cwd: string): Promise<boolean> {\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.error(picocolors.red('Could not find app directory (app/ or src/app/)'));\n return false;\n }\n\n const routePath = path.join(appDir, 'export', 'epub', 'route.ts');\n if (await exists(routePath)) {\n console.log(picocolors.yellow('EPUB route already exists at'), routePath);\n return true;\n }\n\n await fs.mkdir(path.dirname(routePath), { recursive: true });\n await fs.writeFile(routePath, API_ROUTE_TEMPLATE);\n console.log(picocolors.green('Created EPUB route at'), routePath);\n return true;\n}\n\nexport async function exportEpub(options: {\n output?: string;\n framework: string;\n scaffoldOnly?: boolean;\n}) {\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 // Check for Next.js when scaffolding (only Next.js scaffold is implemented)\n const pkg = await readPackageJson(cwd);\n const hasNextConfig =\n (await exists(path.join(cwd, 'next.config.js'))) ||\n (await exists(path.join(cwd, 'next.config.ts'))) ||\n (await exists(path.join(cwd, 'next.config.mjs')));\n const nextDeps = pkg\n ? { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies }\n : {};\n const hasNextInPkg = !!nextDeps?.next;\n const hasAppOrPages =\n (await exists(path.join(cwd, 'app'))) ||\n (await exists(path.join(cwd, 'pages'))) ||\n (await exists(path.join(cwd, 'src', 'app'))) ||\n (await exists(path.join(cwd, 'src', 'pages')));\n const hasNext = hasNextConfig || (hasNextInPkg && hasAppOrPages);\n\n if (!hasNext && framework === 'next') {\n console.error(\n picocolors.red(\n 'Next.js project not found. Run this command from a Fumadocs Next.js project root.',\n ),\n );\n process.exit(1);\n }\n\n // Scaffold EPUB route (Next.js only for now)\n if (framework === 'next') {\n spin.start('Scaffolding EPUB route');\n const scaffolded = await scaffoldEpubRoute(cwd);\n spin.stop(scaffolded ? 'EPUB route ready' : 'Scaffolding failed');\n\n if (!scaffolded) {\n process.exit(1);\n }\n }\n\n if (options.scaffoldOnly) {\n console.log(picocolors.cyan('\\nTo export:'));\n console.log(' 1. Add fumadocs-epub to your dependencies: pnpm add fumadocs-epub');\n console.log(' 2. Ensure includeProcessedMarkdown: true in your docs collection config');\n if (framework === 'next') {\n console.log(\n ' 3. Set EXPORT_SECRET in your environment to protect the /export/epub endpoint',\n );\n console.log(' 4. Run production build: pnpm build');\n console.log(' 5. Start the server (e.g. pnpm start) and keep it running');\n console.log(' 6. Run: fumadocs export epub --framework next');\n } else {\n console.log(` 3. Add a prerender route that outputs EPUB to ${buildPath}`);\n console.log(' 4. Run production build: pnpm build');\n console.log(` 5. Run: fumadocs export epub --framework ${framework}`);\n }\n return;\n }\n\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 {\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","#!/usr/bin/env node\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport picocolors from 'picocolors';\nimport { createOrLoadConfig, initConfig, type LoadedConfig } 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 { HttpRegistryClient, LocalRegistryClient } from '@/registry/client';\n\nconst program = new Command().option('--config <string>');\n\nprogram\n .name('fumadocs')\n .description('CLI to setup Fumadocs, init a config')\n .version(packageJson.version)\n .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\nprogram\n .command('customise')\n .alias('customize')\n .description('simple way to customise layouts with Fumadocs UI')\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .action(async (options: { config?: string; dir?: string }) => {\n await customise(createClientFromDir(options.dir, await createOrLoadConfig(options.config)));\n });\n\nconst dirShortcuts: Record<string, string> = {\n ':preview': 'https://preview.fumadocs.dev/registry',\n ':dev': 'http://localhost:3000/registry',\n};\n\nprogram\n .command('add')\n .description('add a new component to your docs')\n .argument('[components...]', 'components to download')\n .option('--dir <string>', 'the root url or directory to resolve registry')\n .action(async (input: string[], options: { config?: string; dir?: string }) => {\n const client = createClientFromDir(options.dir, await createOrLoadConfig(options.config));\n await add(input, client);\n });\n\nconst exportCmd = program.command('export').description('export documentation to various formats');\n\nexportCmd\n .command('epub')\n .description('export documentation to EPUB format (run after production build)')\n .requiredOption('--framework <name>', 'React framework: next, tanstack-start, react-router, waku')\n .option('--output <path>', 'output file path', 'docs.epub')\n .option('--scaffold-only', 'only scaffold the EPUB route, do not copy')\n .action(async (options: { output?: string; framework: string; scaffoldOnly?: boolean }) => {\n await exportEpub({\n output: options.output,\n framework: options.framework,\n scaffoldOnly: options.scaffoldOnly,\n });\n });\n\nprogram\n .command('tree')\n .argument('[json_or_args]', 'JSON output of `tree` command or arguments for the `tree` command')\n .argument('[output]', 'output path of file')\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', config: LoadedConfig) {\n if (dir in dirShortcuts) dir = dirShortcuts[dir];\n\n return dir.startsWith('http://') || dir.startsWith('https://')\n ? new HttpRegistryClient(dir, config)\n : new LocalRegistryClient(dir, config);\n}\n\nprogram.parse();\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,UAAU;CAAC;CAAQ;CAAa;CAAO;AAE7C,SAAgB,UAAU,OAAuB,SAAS,OAAe;CACvE,SAAS,OAAO,MAA4B;AAC1C,MAAI,KAAK,SAAS,UAAU,KAAK,SAAS,OACxC,QAAO,cAAc,KAAK,UAAU,KAAK,KAAK,CAAC;AAGjD,MAAI,KAAK,SAAS,aAAa;AAC7B,OAAI,KAAK,SAAS,WAAW,KAAK,UAAU,KAAK,SAAS,IAAI;IAC5D,MAAM,QAAQ,KAAK,SAAS;AAE5B,WAAO,OAAO;KACZ,GAAG;KACH,MAAM,GAAG,KAAK,KAAK,GAAG,MAAM;KAC7B,CAAC;;AAGJ,UAAO,gBAAgB,KAAK,UAAU,KAAK,KAAK,CAAC;EACrD,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK,CAAC;;;AAInD,SAAO;;CAGT,IAAI,WAAW,MAAM,QAAQ,MAAM,QAAQ,SAAS,EAAE,KAAK,CAAC;AAE5D,KAAI,UAAU,SAAS,WAAW,KAAK,MAAM,GAAG,SAAS,YACvD,YAAW,MAAM,GAAG;AAGtB,QAAO;EACP,SAAS,IAAI,OAAO,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK,CAAC;;;AAIlD,SAAgB,iBACd,OACA,QACA,aAAa,gCACL;AACR,QAAO,uCAAuC,KAAK,UAAU,WAAW,CAAC;;kBAEzD,UAAU,OAAO,OAAO,CAAC;;;;AC3D3C,eAAsB,QAAQ,MAAuC;CACnE,MAAM,MAAM,MAAM,EAAE,QAAQ;EAAC;EAAM;EAAe;EAAW;EAAK,CAAC;AAEnE,KAAI;AACF,SAAO,KAAK,MAAM,IAAI,OAAO;UACtB,GAAG;AACV,QAAM,IAAI,MAAM,gCAAgC,EAC9C,OAAO,GACR,CAAC;;;;;;;;AEXN,MAAa,eAAe;CAC1B,WAAW;CACX,YAAY;CACb;;;;;;ACED,SAAgB,wBAAkD;CAChE,MAAM,cAAc;EAClB;EACA;EACA;EACA;EACA;EACD;CAED,MAAM,cAAsC;EAC1C,0BAA0B;EAC1B,4BAA4B;EAC5B,6BAA6B;EAC7B,8BAA8B;EAC9B,+BAA+B;EAC/B,mCAAmC;EACnC,oCAAoC;EACpC,0BAA0B;EAC1B,2BAA2B;EAC3B,+BAA+B;EAC/B,gCAAgC;EAChC,0BAA0B;EAC1B,+BAA+B;EAC/B,gCAAgC;EACjC;CACD,MAAM,gBAAgB,IAAI,IAAI,YAAY;AAE1C,QAAO;EACL,cAAc,MAAM,EAAE,SAAS;AAE7B,OADwB,cAAc,IAAI,MAAM,GAAG,KAAK,CACnC;AAErB,UAAO;IACL,GAAG;IACH,gBAAgB,KAAK,eAAe,QAAQ,UAAU,CAAC,cAAc,IAAI,MAAM,KAAK,CAAC;IACtF;;EAEH,gBAAgB,WAAW,EAAE,SAAS;AAGpC,OAFwB,cAAc,IAAI,MAAM,GAAG,KAAK,IAEjC,EAAE,aAAa,aAAc,QAAO;AAE3D,UAAO,eAAe,YAAY;;EAErC;;;;ACjCH,eAAsB,IAAI,OAAiB,QAAwB;CACjE,MAAM,SAAS,OAAO;CACtB,IAAI;CACJ,MAAM,YAAY,IAAI,mBAAmB,QAAQ,EAC/C,SAAS,CAAC,uBAAuB,CAAC,EACnC,CAAC;CACF,MAAM,WAAW,aAAa,OAAO;AAErC,KAAI,MAAM,WAAW,GAAG;EACtB,MAAM,OAAO,SAAS;AACtB,OAAK,MAAM,oBAAoB;EAC/B,MAAM,OAAO,MAAM,OAAO,mBAAmB;EAC7C,MAAM,UAIA,EAAE;AAER,OAAK,MAAM,QAAQ,KAAK,QACtB,SAAQ,KAAK;GACX,OAAO,KAAK,SAAS,KAAK;GAC1B,OAAO,KAAK;GACZ,MAAM,KAAK;GACZ,CAAC;EAEJ,MAAM,EAAE,YAAY,MAAM,OAAO,2BAA2B,SAAS,CAAC,mBAAmB;AAEzF,OAAK,MAAM,QAAQ,QACjB,SAAQ,KAAK;GACX,OAAO,KAAK,SAAS,KAAK;GAC1B,OAAO,GAAG,SAAS,GAAG,KAAK;GAC3B,MAAM,KAAK;GACZ,CAAC;AAGJ,OAAK,KAAK,WAAW,KAAK,WAAW,YAAY,mBAAmB,CAAC,CAAC;EACtE,MAAM,QAAQ,MAAM,wBAAwB;GAC1C,SAAS;GACT;GACD,CAAC;AAEF,MAAI,SAAS,MAAM,EAAE;AACnB,SAAM,QAAQ;AACd;;AAGF,WAAS;OAET,UAAS,MAAM,QAAQ,IACrB,MAAM,IAAI,OAAO,SAAW,MAAM,OAAO,aAAa,KAAK,GAAI,OAAO,GAAG,SAAS,GAAG,OAAQ,CAC9F;AAGH,OAAM,QAAQ,QAAQ,UAAU;;AAGlC,eAAsB,QAAQ,QAAkB,WAA+B;AAC7E,MAAK,MAAM,QAAQ,QAAQ;EACzB,MAAM,OAAO,SAAS;AACtB,OAAK,MAAM,WAAW,KAAK,WAAW,WAAW,cAAc,OAAO,CAAC,CAAC;AAExE,MAAI;AACF,SAAM,UAAU,QAAQ,MAAM;IAC5B,OAAO,SAAS;AACd,UAAK,QAAQ,QAAQ;;IAEvB,MAAM,oBAAoB,SAAS;AACjC,UAAK,OAAO;KACZ,MAAM,QAAQ,MAAM,QAAQ;MAC1B,SAAS,2BAA2B,QAAQ,KAAK;MACjD,cAAc;MACf,CAAC;AACF,SAAI,SAAS,MAAM,EAAE;AACnB,YAAM,0BAA0B;AAChC,cAAQ,KAAK,EAAE;;AAEjB,UAAK,MAAM,WAAW,KAAK,WAAW,WAAW,cAAc,OAAO,CAAC,CAAC;AACxE,YAAO;;IAET,iBAAiB,SAAS;AACxB,UAAK,QAAQ,QAAQ,KAAK;;IAE7B,CAAC;AACF,QAAK,KAAK,WAAW,KAAK,WAAW,YAAY,GAAG,KAAK,YAAY,CAAC,CAAC;WAChE,GAAG;AACV,QAAK,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC;AACtD,WAAQ,KAAK,GAAG;;;CAIpB,MAAM,OAAO,MAAM,UAAU,MAAM;AACnC,KAAI,KAAK,aAAa,EAAE;AACtB,MAAI,SAAS;AACb,MAAI,CAAC,GAAG,KAAK,cAAc,GAAG,KAAK,gBAAgB,CAAC,KAAK,KAAK,EAAE,mBAAmB;EACnF,MAAM,MAAM,MAAM,QAAQ,GAAG,QAAQ;EACrC,MAAM,QAAQ,MAAM,QAAQ,EAC1B,SAAS,+BAA+B,GAAG,IAC5C,CAAC;AAEF,MAAI,SAAS,MAAM,EAAE;AACnB,SAAM,0BAA0B;AAChC,WAAQ,KAAK,EAAE;;AAGjB,MAAI,OAAO;GACT,MAAM,OAAO,QAAQ,EACnB,cAAc,kCACf,CAAC;AACF,QAAK,MAAM,0BAA0B;AACrC,SAAM,KAAK,gBAAgB,GAAG;AAC9B,QAAK,KAAK,yBAAyB;QAEnC,OAAM,KAAK,eAAe;;AAI9B,OAAM,UAAU,OAAO;AACvB,OAAM,WAAW,KAAK,WAAW,YAAY,aAAa,CAAC,CAAC;;;;AChH9D,eAAsB,UAAU,QAAwB;AACtD,OAAM,WAAW,QAAQ,WAAW,YAAY,wBAAwB,CAAC,CAAC;CAC1E,MAAM,SAAS,OAAO;CACtB,MAAM,YAAY,IAAI,mBAAmB,QAAQ,EAC/C,SAAS,CAAC,uBAAuB,CAAC,EACnC,CAAC;CACF,MAAM,WAAW,aAAa,OAAO;CACrC,MAAM,OAAO,MAAM,OAAO,2BAA2B,SAAS,CAAC,mBAAmB;CAgJlF,MAAM,UA9IS,MAAM,MACnB;EACE,cACE,OAAO;GACL,SAAS;GACT,SAAS;IACP;KACE,OAAO;KACP,OAAO;MACL,IAAI;MACJ,QAAQ,CAAC,GAAG,SAAS,eAAe;MACpC,QAAQ;AACN,mBACE,CAAC,4BAA4B,iBAAiB,EAC9C,CAAC,iCAAiC,sBAAsB,CACzD;;MAEJ;KACD,MAAM;KACP;IACD;KACE,OAAO;KACP,OAAO;MACL,IAAI;MACJ,QAAQ,CAAC,GAAG,SAAS,mBAAmB;MACxC,QAAQ;AACN,mBACE,CAAC,gCAAgC,qBAAqB,EACtD,CAAC,qCAAqC,0BAA0B,CACjE;;MAEJ;KACD,MAAM;KACP;IACD;KACE,OAAO;KACP,OAAO;MACL,IAAI;MACJ,QAAQ,CAAC,GAAG,SAAS,eAAe;MACpC,QAAQ;AACN,mBACE,CAAC,4BAA4B,iBAAiB,EAC9C,CAAC,iCAAiC,sBAAsB,CACzD;;MAEJ;KACD,MAAM;KACP;IACD;KACE,OAAO;KACP,OAAO;MACL,IAAI;MACJ,QAAQ,CAAC,GAAG,SAAS,eAAe;MACpC,QAAQ;AACN,mBAAY,CAAC,4BAA4B,iBAAiB,CAAC;;MAE9D;KACD,MAAM;KACP;IACF;GACF,CAAC;EACJ,SAAS,MAAoC;GAC3C,MAAM,WAAW,EAAE,QAAQ;AAC3B,OAAI,SAAS,OAAO,OAAQ,QAAO,QAAQ,QAAQ,SAAS;AAE5D,UAAO,OAAmB;IACxB,SAAS;IACT,SAAS;KACP;MACE,OAAO;MACP,MAAM;MACN,OAAO;MACR;KACD;MACE,OAAO;MACP,MAAM;MACN,OAAO;OACL,IAAI;OACJ,QAAQ,CAAC,mBAAmB;OAC5B,QAAQ;AACN,oBACE,CAAC,4BAA4B,iBAAiB,EAC9C,CAAC,iCAAiC,sBAAsB,CACzD;;OAEJ;MACF;KACD,GAAG,KAAK,gBAAgB,SAAS,UAAU;MACzC,MAAM,SAAS,SAAS,SAAS;AACjC,UAAI,CAAC,MAAM,KAAK,WAAW,OAAO,CAAE,QAAO,EAAE;MAC7C,IAAI,OAAO,MAAM,KAAK,MAAM,OAAO,SAAS,EAAE;AAE9C,UAAI,KAAK,WAAW,QAAQ,EAAE;AAC5B,cAAO,KAAK,MAAM,EAAe;AAEjC,cAAO;QACL,OAAO,SAAS;QAChB,MAAM;QACN,OAAO;SACL,IAAI,MAAM;SACV,QAAQ,CAAC,GAAG,SAAS,GAAG,MAAM,OAAO;SACrC,QAAQ;AACN,oBAAU;WACR,IAAI,aAAa,SAAS,GAAG,cAAc;WAC3C,UAAU,SAAS;WACnB;WACA,QAAQ;WACT,CAAC;;SAEL;QACF;;AAGH,aAAO;OACL,OAAO,WAAW;OAClB,MAAM;OACN,OAAO;QACL,IAAI,MAAM;QACV,QAAQ,CAAC,GAAG,SAAS,GAAG,MAAM,OAAO;QACrC,QAAQ;AACN,mBAAU;UACR,IAAI,aAAa,SAAS,GAAG,SAAS;UACtC,UAAU,SAAS;UACnB;UACA,QAAQ;UACT,CAAC;;QAEL;OACF;OACD;KACH;IACF,CAAC;;EAEL,EACD,EACE,gBAAgB;AACd,SAAO,wBAAwB;AAC/B,UAAQ,KAAK,EAAE;IAElB,CACF,EAEqB;AACtB,OAAM,QAAQ,OAAO,QAAQ,UAAU;AACvC,QAAO,SAAS;AAEhB,OAAM,WAAW,KAAK,YAAY,CAAC;;AAGrC,SAAS,YAAY,GAAG,MAAoC;AAC1D,OAAM,WAAW,KAAK,gBAAgB,CAAC;AAEvC,KAAI,KACF;EACE;EACA,WAAW,IAAI,MAAM;EACrB;EACA,GAAG,KAAK,KAAK,CAAC,MAAM,QAAQ,WAAW,YAAY,IAAI,KAAK,QAAQ,GAAG,GAAG,CAAC;EAC5E,CAAC,KAAK,KAAK,CACb;;AAGH,SAAS,UAAU,EAAE,IAAI,UAAU,MAAM,UAAyB;AAChE,OAAM,WAAW,KAAK,gBAAgB,CAAC;AAEvC,KAAI,KAAK,+CAA+C,GAAG,IAAI;CAE/D,MAAM,OAAO,YAAY;EAAE;EAAI;EAAU;EAAM;EAAQ,CAAC;AAExD,KAAI,KACF,KAAI,OACF,KAAI,KACF,GAAG,WAAW,KAAK,8DAA4D,CAAC,MAAM,OACvF;KAED,KAAI,KACF,GAAG,WAAW,KAAK,gEAA8D,CAAC,MAAM,OACzF;;AAKP,SAAS,YAAY,EAAE,IAAI,UAAU,MAAM,UAA6C;AACtF,KAAI,OACF,SAAQ,MAAR;EACE,KAAK;AACH,OAAI,aAAa,OACf,QAAO,qCAAqC,GAAG;;;;;;;;;;;;;;AAgBjD,UAAO,iDAAiD,GAAG;;;;;;;;;;;;;;;EAe7D,KAAK,YACH,QAAO,8BAA8B,GAAG;;;;;;;;;;;EAY1C,KAAK,SACH,QAAO,2BAA2B,GAAG;;;;;;;;;;;EAYvC,KAAK,aACH,QAAO,+BAA+B,GAAG;;;;;;;;;;;EAY3C,QACE;;AAIN,SAAQ,MAAR;EACE,KAAK;AACH,OAAI,aAAa,WACf,QAAO;;;;;;UAML,GAAG;;;;;;;;;;;;;;;;;AAmBP,UAAO,yEAAyE,GAAG;;;;;;;;;;;;;;;;EAiBrF,KAAK,YACH,QAAO,8BAA8B,GAAG;;;;;;;;;;;EAY1C,KAAK,SACH,QAAO,2BAA2B,GAAG;;;;;;;;;;;EAYvC,KAAK,eACH,QAAO,gCAAgC,GAAG;;;;;;;;;;;EAY5C,QACE;;;;;ACjXN,MAAM,YAAY,UAAU,KAAK;AAQjC,eAAe,gBAAgB,KAA0C;AACvE,KAAI;EACF,MAAM,MAAM,MAAM,GAAG,SAAS,KAAK,KAAK,KAAK,eAAe,EAAE,QAAQ;AACtE,SAAO,KAAK,MAAM,IAAI;SAChB;AACN,SAAO;;;;AAKX,MAAM,mBAA2C;CAC/C,MAAM;CACN,kBAAkB;CAClB,sBAAsB;CACtB,gBAAgB;CAChB,oBAAoB;CACpB,MAAM;CACP;AAED,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC3B,eAAe,WAAW,KAAqC;AAE7D,MAAK,MAAM,WADM,CAAC,OAAO,UAAU,EACH;EAC9B,MAAM,WAAW,KAAK,KAAK,KAAK,QAAQ;AACxC,MAAI,MAAM,OAAO,SAAS,CACxB,QAAO;;AAGX,QAAO;;AAGT,eAAe,kBAAkB,KAA+B;CAC9D,MAAM,SAAS,MAAM,WAAW,IAAI;AACpC,KAAI,CAAC,QAAQ;AACX,UAAQ,MAAM,WAAW,IAAI,kDAAkD,CAAC;AAChF,SAAO;;CAGT,MAAM,YAAY,KAAK,KAAK,QAAQ,UAAU,QAAQ,WAAW;AACjE,KAAI,MAAM,OAAO,UAAU,EAAE;AAC3B,UAAQ,IAAI,WAAW,OAAO,+BAA+B,EAAE,UAAU;AACzE,SAAO;;AAGT,OAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,EAAE,EAAE,WAAW,MAAM,CAAC;AAC5D,OAAM,GAAG,UAAU,WAAW,mBAAmB;AACjD,SAAQ,IAAI,WAAW,MAAM,wBAAwB,EAAE,UAAU;AACjE,QAAO;;AAGT,eAAsB,WAAW,SAI9B;CACD,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,aAAa,KAAK,QAAQ,KAAK,QAAQ,UAAU,YAAY;CACnE,MAAM,YAAY,QAAQ;CAE1B,MAAM,OAAO,SAAS;CAEtB,MAAM,YAAY,iBAAiB;AACnC,KAAI,EAAE,aAAa,mBAAmB;EACpC,MAAM,QAAQ,OAAO,KAAK,iBAAiB,CAAC,KAAK,KAAK;AACtD,UAAQ,MAAM,WAAW,IAAI,wBAAwB,UAAU,qBAAqB,QAAQ,CAAC;AAC7F,UAAQ,KAAK,EAAE;;CAIjB,MAAM,MAAM,MAAM,gBAAgB,IAAI;CACtC,MAAM,gBACH,MAAM,OAAO,KAAK,KAAK,KAAK,iBAAiB,CAAC,IAC9C,MAAM,OAAO,KAAK,KAAK,KAAK,iBAAiB,CAAC,IAC9C,MAAM,OAAO,KAAK,KAAK,KAAK,kBAAkB,CAAC;CAIlD,MAAM,eAAe,CAAC,EAHL,MACb;EAAE,GAAG,IAAI;EAAc,GAAG,IAAI;EAAiB,GAAG,IAAI;EAAkB,GACxE,EAAE,GAC2B;CACjC,MAAM,gBACH,MAAM,OAAO,KAAK,KAAK,KAAK,MAAM,CAAC,IACnC,MAAM,OAAO,KAAK,KAAK,KAAK,QAAQ,CAAC,IACrC,MAAM,OAAO,KAAK,KAAK,KAAK,OAAO,MAAM,CAAC,IAC1C,MAAM,OAAO,KAAK,KAAK,KAAK,OAAO,QAAQ,CAAC;AAG/C,KAAI,EAFY,iBAAkB,gBAAgB,kBAElC,cAAc,QAAQ;AACpC,UAAQ,MACN,WAAW,IACT,oFACD,CACF;AACD,UAAQ,KAAK,EAAE;;AAIjB,KAAI,cAAc,QAAQ;AACxB,OAAK,MAAM,yBAAyB;EACpC,MAAM,aAAa,MAAM,kBAAkB,IAAI;AAC/C,OAAK,KAAK,aAAa,qBAAqB,qBAAqB;AAEjE,MAAI,CAAC,WACH,SAAQ,KAAK,EAAE;;AAInB,KAAI,QAAQ,cAAc;AACxB,UAAQ,IAAI,WAAW,KAAK,eAAe,CAAC;AAC5C,UAAQ,IAAI,sEAAsE;AAClF,UAAQ,IAAI,4EAA4E;AACxF,MAAI,cAAc,QAAQ;AACxB,WAAQ,IACN,kFACD;AACD,WAAQ,IAAI,wCAAwC;AACpD,WAAQ,IAAI,8DAA8D;AAC1E,WAAQ,IAAI,kDAAkD;SACzD;AACL,WAAQ,IAAI,mDAAmD,YAAY;AAC3E,WAAQ,IAAI,wCAAwC;AACpD,WAAQ,IAAI,8CAA8C,YAAY;;AAExE;;AAIF,KAAI,CAAC,KAAK;AACR,UAAQ,MACN,WAAW,IAAI,yEAAyE,CACzF;AACD,UAAQ,KAAK,EAAE;;AAGjB,KAAI,CADS;EAAE,GAAG,IAAI;EAAc,GAAG,IAAI;EAAiB,CAClD,kBAAkB;AAC1B,UAAQ,IAAI,WAAW,OAAO,gCAAgC,CAAC;EAM/D,MAAM,aAAa,GALI,QAAQ,IAAI,cAAc,SAAS,OAAO,GAC7D,SACA,QAAQ,IAAI,cAAc,SAAS,MAAM,GACvC,QACA,MAC+B;AACrC,MAAI;AACF,SAAM,UAAU,YAAY,EAAE,KAAK,CAAC;WAC7B,KAAc;GACrB,MAAM,SACJ,OAAO,OAAO,QAAQ,YAAY,YAAY,MAC1C,OAAQ,IAA4B,OAAO,GAC3C;AACN,WAAQ,MAAM,WAAW,IAAI,6CAA6C,aAAa,CAAC;AACxF,OAAI,OAAQ,SAAQ,MAAM,OAAO;AACjC,WAAQ,KAAK,EAAE;;;AAInB,KAAI,cAAc,QAAQ;EACxB,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,QAAQ;AACX,WAAQ,MACN,WAAW,IAAI,4EAA4E,CAC5F;AACD,WAAQ,KAAK,EAAE;;EAEjB,MAAM,OAAO,QAAQ,IAAI,QAAQ;EACjC,MAAM,MAAM,oBAAoB,KAAK;AACrC,OAAK,MAAM,4BAA4B;EACvC,MAAM,aAAa,IAAI,iBAAiB;EACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAO;AAC9D,MAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK;IAC3B,SAAS,EAAE,eAAe,UAAU,UAAU;IAC9C,QAAQ,WAAW;IACpB,CAAC;AACF,OAAI,CAAC,IAAI,IAAI;AACX,QAAI,IAAI,WAAW,OAAO,IAAI,WAAW,IACvC,SAAQ,MACN,WAAW,IAAI,uEAAuE,CACvF;QAED,SAAQ,MACN,WAAW,IACT,mBAAmB,IAAI,OAAO,wDAAwD,KAAK,GAC5F,CACF;AAEH,YAAQ,KAAK,EAAE;;GAEjB,MAAM,SAAS,OAAO,KAAK,MAAM,IAAI,aAAa,CAAC;AACnD,SAAM,GAAG,MAAM,KAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7D,SAAM,GAAG,UAAU,YAAY,OAAO;AACtC,QAAK,KAAK,WAAW,MAAM,iBAAiB,aAAa,CAAC;WACnD,KAAc;AACrB,OAAI,eAAe,SAAS,IAAI,SAAS,aACvC,SAAQ,MAAM,WAAW,IAAI,sCAAsC,CAAC;QAC/D;IACL,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,YAAQ,MAAM,WAAW,IAAI,yBAAyB,MAAM,CAAC;;AAE/D,WAAQ,MACN,WAAW,OAAO,0DAA0D,KAAK,GAAG,CACrF;AACD,WAAQ,KAAK,EAAE;YACP;AACR,gBAAa,UAAU;;AAEzB;;CAGF,MAAM,gBAAgB,KAAK,KAAK,KAAK,UAAU;AAC/C,KAAI,CAAE,MAAM,OAAO,cAAc,EAAG;AAClC,UAAQ,MACN,WAAW,IACT,qBAAqB,UAAU,iDAChC,CACF;AACD,UAAQ,KAAK,EAAE;;AAGjB,MAAK,MAAM,eAAe;AAC1B,OAAM,GAAG,MAAM,KAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7D,OAAM,GAAG,SAAS,eAAe,WAAW;AAC5C,MAAK,KAAK,WAAW,MAAM,iBAAiB,aAAa,CAAC;;;;AC5P5D,MAAM,UAAU,IAAI,SAAS,CAAC,OAAO,oBAAoB;AAEzD,QACG,KAAK,WAAW,CAChB,YAAY,uCAAuC,CACnD,QAAQA,QAAoB,CAC5B,OAAO,YAAY;AAClB,KAAI,MAAM,YAAY,CACpB,SAAQ,IAAI,WAAW,MAAM,0CAA0C,CAAC;KAExE,SAAQ,IAAI,WAAW,UAAU,gCAAgC,CAAC;EAEpE;AAEJ,QACG,QAAQ,YAAY,CACpB,MAAM,YAAY,CAClB,YAAY,mDAAmD,CAC/D,OAAO,kBAAkB,gDAAgD,CACzE,OAAO,OAAO,YAA+C;AAC5D,OAAM,UAAU,oBAAoB,QAAQ,KAAK,MAAM,mBAAmB,QAAQ,OAAO,CAAC,CAAC;EAC3F;AAEJ,MAAM,eAAuC;CAC3C,YAAY;CACZ,QAAQ;CACT;AAED,QACG,QAAQ,MAAM,CACd,YAAY,mCAAmC,CAC/C,SAAS,mBAAmB,yBAAyB,CACrD,OAAO,kBAAkB,gDAAgD,CACzE,OAAO,OAAO,OAAiB,YAA+C;AAE7E,OAAM,IAAI,OADK,oBAAoB,QAAQ,KAAK,MAAM,mBAAmB,QAAQ,OAAO,CAAC,CACjE;EACxB;AAEc,QAAQ,QAAQ,SAAS,CAAC,YAAY,0CAA0C,CAG/F,QAAQ,OAAO,CACf,YAAY,mEAAmE,CAC/E,eAAe,sBAAsB,4DAA4D,CACjG,OAAO,mBAAmB,oBAAoB,YAAY,CAC1D,OAAO,mBAAmB,4CAA4C,CACtE,OAAO,OAAO,YAA4E;AACzF,OAAM,WAAW;EACf,QAAQ,QAAQ;EAChB,WAAW,QAAQ;EACnB,cAAc,QAAQ;EACvB,CAAC;EACF;AAEJ,QACG,QAAQ,OAAO,CACf,SAAS,kBAAkB,oEAAoE,CAC/F,SAAS,YAAY,sBAAsB,CAC3C,OAAO,QAAQ,4BAA4B,CAC3C,OAAO,aAAa,uBAAuB,CAC3C,OAAO,wBAAwB,uCAAuC,CACtE,OACC,OACE,KACA,QACA,EAAE,IAAI,MAAM,iBACT;CACH,MAAM,eAAe;EAAC;EAAO;EAAQ;EAAO;CAC5C,MAAM,SAAS,CAAC;CAChB,IAAI;AAEJ,KAAI;AACF,UAAQ,KAAK,MAAM,OAAO,GAAG;SACvB;AACN,UAAQ,MAAM,QAAQ,OAAO,KAAK;;CAGpC,MAAM,MACJ,MAAO,UAAU,aAAa,SAAS,KAAK,QAAQ,OAAO,CAAC,GACxD,iBAAiB,OAAO,QAAQ,WAAW,GAC3C,UAAU,OAAO,OAAO;AAE9B,KAAI,QAAQ;AACV,QAAM,GAAG,MAAM,KAAK,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACzD,QAAM,GAAG,UAAU,QAAQ,IAAI;OAE/B,SAAQ,IAAI,IAAI;EAGrB;AAEH,SAAS,oBAAoB,MAAM,iCAAiC,QAAsB;AACxF,KAAI,OAAO,aAAc,OAAM,aAAa;AAE5C,QAAO,IAAI,WAAW,UAAU,IAAI,IAAI,WAAW,WAAW,GAC1D,IAAI,mBAAmB,KAAK,OAAO,GACnC,IAAI,oBAAoB,KAAK,OAAO;;AAG1C,QAAQ,OAAO"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["packageJson.version"],"sources":["../src/commands/file-tree.ts","../src/utils/file-tree/run-tree.ts","../package.json","../src/commands/shared.ts","../src/commands/customise.ts","../src/commands/add.ts","../src/utils/fs.ts","../src/commands/export-epub.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","","export const UIRegistries = {\n 'base-ui': 'fumadocs/base-ui',\n 'radix-ui': 'fumadocs/radix-ui',\n};\n","import { cancel, group, intro, log, outro, select } from '@clack/prompts';\nimport picocolors from 'picocolors';\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;\n}\n\ninterface SlotPrintInfo {\n at: string;\n layoutId: string;\n name: string;\n isPage: boolean;\n}\n\nexport async function customise(config: LoadedConfig, connector: RegistryConnector) {\n intro(picocolors.bgBlack(picocolors.whiteBright('Customise 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 customise?',\n options: [\n {\n label: 'Docs Layout',\n value: {\n id: 'docs',\n targets: [{ subRegistry, name: 'layouts/docs' }],\n print() {\n printLayout(\n ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n );\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 ['fumadocs-ui/layouts/notebook', '@/layouts/notebook'],\n ['fumadocs-ui/layouts/notebook/page', '@/layouts/notebook/page'],\n );\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 ['fumadocs-ui/layouts/flux', '@/layouts/flux'],\n ['fumadocs-ui/layouts/flux/page', '@/layouts/flux/page'],\n );\n },\n },\n hint: 'the experimental variant of docs layout',\n },\n {\n label: 'Home Layout',\n value: {\n id: 'home',\n targets: [{ subRegistry, name: 'layouts/home' }],\n print() {\n printLayout(['fumadocs-ui/layouts/home', `@/layouts/home`]);\n },\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 customise?',\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 ['fumadocs-ui/layouts/docs', '@/layouts/docs'],\n ['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],\n );\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 });\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 });\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 targetInfo.print?.();\n\n outro(picocolors.bold('Have fun!'));\n}\n\nfunction printLayout(...maps: [from: string, to: string][]) {\n intro(picocolors.bold('What is Next?'));\n\n log.info(\n [\n 'You can check the installed layouts in `layouts` folder.',\n picocolors.dim('---'),\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 }: 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 });\n\n if (code) {\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 <DocsLayout /> component, update your \"slots\" prop:')}\\n\\n${code}`,\n );\n }\n }\n}\n\nfunction getSlotCode({ at, layoutId, name, isPage }: SlotPrintInfo): string | undefined {\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, autocompleteMultiselect, outro, spinner } from '@clack/prompts';\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): Promise<AddOption[]> {\n const info = await connector.fetchRegistryInfo(subRegistry);\n\n return info.indexes.map((item) => ({\n label: item.title ?? item.name,\n value: { name: item.name, subRegistry },\n hint: item.description,\n }));\n }\n\n spin.stop(picocolors.bold(picocolors.greenBright('registry fetched')));\n const value = await autocompleteMultiselect({\n message: 'Select components to install',\n options: [...(await scan()), ...(await scan(subRegistry))],\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';\n\nexport async function exists(pathLike: string): Promise<boolean> {\n try {\n await fs.access(pathLike);\n return true;\n } catch {\n return false;\n }\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 '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\nconst API_ROUTE_TEMPLATE = `import { source } from '@/lib/source';\nimport { exportEpub } from 'fumadocs-epub';\n\nexport const revalidate = false;\n\nexport async function GET(request: Request): Promise<Response> {\n // Require EXPORT_SECRET to prevent unauthenticated abuse. Pass via Authorization: Bearer <secret>\n const secret = process.env.EXPORT_SECRET;\n if (!secret) {\n return new Response('EXPORT_SECRET is not configured. Set it in your environment to protect this endpoint.', { status: 503 });\n }\n const authHeader = request.headers.get('authorization');\n const token = authHeader?.replace(/^Bearer\\\\s+/i, '') ?? '';\n if (token !== secret) {\n return new Response('Unauthorized', { status: authHeader ? 403 : 401 });\n }\n const buffer = await exportEpub({\n source,\n title: 'Documentation',\n author: 'Your Team',\n description: 'Exported documentation',\n cover: '/cover.png',\n });\n return new Response(new Uint8Array(buffer), {\n headers: {\n 'Content-Type': 'application/epub+zip',\n 'Content-Disposition': 'attachment; filename=\"docs.epub\"',\n },\n });\n}\n`;\n\nasync function findAppDir(cwd: string): Promise<string | null> {\n const appPaths = ['app', 'src/app'];\n for (const appPath of appPaths) {\n const fullPath = path.join(cwd, appPath);\n if (await exists(fullPath)) {\n return fullPath;\n }\n }\n return null;\n}\n\nasync function scaffoldEpubRoute(cwd: string): Promise<boolean> {\n const appDir = await findAppDir(cwd);\n if (!appDir) {\n console.error(picocolors.red('Could not find app directory (app/ or src/app/)'));\n return false;\n }\n\n const routePath = path.join(appDir, 'export', 'epub', 'route.ts');\n if (await exists(routePath)) {\n console.log(picocolors.yellow('EPUB route already exists at'), routePath);\n return true;\n }\n\n await fs.mkdir(path.dirname(routePath), { recursive: true });\n await fs.writeFile(routePath, API_ROUTE_TEMPLATE);\n console.log(picocolors.green('Created EPUB route at'), routePath);\n return true;\n}\n\nexport async function exportEpub(options: {\n output?: string;\n framework: string;\n scaffoldOnly?: boolean;\n}) {\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 // Check for Next.js when scaffolding (only Next.js scaffold is implemented)\n const pkg = await readPackageJson(cwd);\n const hasNextConfig =\n (await exists(path.join(cwd, 'next.config.js'))) ||\n (await exists(path.join(cwd, 'next.config.ts'))) ||\n (await exists(path.join(cwd, 'next.config.mjs')));\n const nextDeps = pkg\n ? { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies }\n : {};\n const hasNextInPkg = !!nextDeps?.next;\n const hasAppOrPages =\n (await exists(path.join(cwd, 'app'))) ||\n (await exists(path.join(cwd, 'pages'))) ||\n (await exists(path.join(cwd, 'src', 'app'))) ||\n (await exists(path.join(cwd, 'src', 'pages')));\n const hasNext = hasNextConfig || (hasNextInPkg && hasAppOrPages);\n\n if (!hasNext && framework === 'next') {\n console.error(\n picocolors.red(\n 'Next.js project not found. Run this command from a Fumadocs Next.js project root.',\n ),\n );\n process.exit(1);\n }\n\n // Scaffold EPUB route (Next.js only for now)\n if (framework === 'next') {\n spin.start('Scaffolding EPUB route');\n const scaffolded = await scaffoldEpubRoute(cwd);\n spin.stop(scaffolded ? 'EPUB route ready' : 'Scaffolding failed');\n\n if (!scaffolded) {\n process.exit(1);\n }\n }\n\n if (options.scaffoldOnly) {\n console.log(picocolors.cyan('\\nTo export:'));\n console.log(' 1. Add fumadocs-epub to your dependencies: pnpm add fumadocs-epub');\n console.log(' 2. Ensure includeProcessedMarkdown: true in your docs collection config');\n if (framework === 'next') {\n console.log(\n ' 3. Set EXPORT_SECRET in your environment to protect the /export/epub endpoint',\n );\n console.log(' 4. Run production build: pnpm build');\n console.log(' 5. Start the server (e.g. pnpm start) and keep it running');\n console.log(' 6. Run: fumadocs export epub --framework next');\n } else {\n console.log(` 3. Add a prerender route that outputs EPUB to ${buildPath}`);\n console.log(' 4. Run production build: pnpm build');\n console.log(` 5. Run: fumadocs export epub --framework ${framework}`);\n }\n return;\n }\n\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 {\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","#!/usr/bin/env node\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\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 { HttpRegistryConnector, LocalRegistryConnector } from 'fuma-cli/registry/connector';\n\nconst program = new Command().option('--config <string>');\n\nprogram\n .name('fumadocs')\n .description('CLI to setup Fumadocs, init a config')\n .version(packageJson.version)\n .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\nprogram\n .command('customise')\n .alias('customize')\n .description('simple way to customise layouts with Fumadocs UI')\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\nprogram\n .command('add')\n .description('add a new component to your docs')\n .argument('[components...]', 'components to download')\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\nconst exportCmd = program.command('export').description('export documentation to various formats');\n\nexportCmd\n .command('epub')\n .description('export documentation to EPUB format (run after production build)')\n .requiredOption('--framework <name>', 'React framework: next, tanstack-start, react-router, waku')\n .option('--output <path>', 'output file path', 'docs.epub')\n .option('--scaffold-only', 'only scaffold the EPUB route, do not copy')\n .action(async (options: { output?: string; framework: string; scaffoldOnly?: boolean }) => {\n await exportEpub({\n output: options.output,\n framework: options.framework,\n scaffoldOnly: options.scaffoldOnly,\n });\n });\n\nprogram\n .command('tree')\n .argument('[json_or_args]', 'JSON output of `tree` command or arguments for the `tree` command')\n .argument('[output]', 'output path of file')\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\nprogram.parse();\n"],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import picocolors from "picocolors";
|
|
2
|
+
import { box, confirm, isCancel, log, outro, spinner } from "@clack/prompts";
|
|
3
|
+
import { ComponentInstaller } from "fuma-cli/registry/installer";
|
|
4
|
+
import { detect } from "package-manager-detector";
|
|
5
|
+
//#region src/registry/plugins/preserve.ts
|
|
6
|
+
/**
|
|
7
|
+
* keep references to `fumadocs-ui/layouts/*` components as original, unless the user is installing them directly.
|
|
8
|
+
*/
|
|
9
|
+
function pluginPreserveLayouts() {
|
|
10
|
+
const layoutNames = [
|
|
11
|
+
"layouts/home",
|
|
12
|
+
"layouts/flux",
|
|
13
|
+
"layouts/notebook",
|
|
14
|
+
"layouts/docs",
|
|
15
|
+
"layouts/shared"
|
|
16
|
+
];
|
|
17
|
+
const layoutComps = {
|
|
18
|
+
"@/<dir>/home/index.tsx": "layouts/home",
|
|
19
|
+
"@/<dir>/shared/index.tsx": "layouts/shared",
|
|
20
|
+
"@/<dir>/shared/client.tsx": "layouts/shared",
|
|
21
|
+
"@/<dir>/notebook/index.tsx": "layouts/notebook",
|
|
22
|
+
"@/<dir>/notebook/client.tsx": "layouts/notebook",
|
|
23
|
+
"@/<dir>/notebook/page/index.tsx": "layouts/notebook/page",
|
|
24
|
+
"@/<dir>/notebook/page/client.tsx": "layouts/notebook/page",
|
|
25
|
+
"@/<dir>/docs/index.tsx": "layouts/docs",
|
|
26
|
+
"@/<dir>/docs/client.tsx": "layouts/docs",
|
|
27
|
+
"@/<dir>/docs/page/index.tsx": "layouts/docs/page",
|
|
28
|
+
"@/<dir>/docs/page/client.tsx": "layouts/docs/page",
|
|
29
|
+
"@/<dir>/flux/index.tsx": "layouts/flux",
|
|
30
|
+
"@/<dir>/flux/page/index.tsx": "layouts/flux/page",
|
|
31
|
+
"@/<dir>/flux/page/client.tsx": "layouts/flux/page"
|
|
32
|
+
};
|
|
33
|
+
const layoutNameSet = new Set(layoutNames);
|
|
34
|
+
return {
|
|
35
|
+
beforeInstall(comp, { stack }) {
|
|
36
|
+
if (layoutNameSet.has(stack[0].name)) return;
|
|
37
|
+
return {
|
|
38
|
+
...comp,
|
|
39
|
+
$subComponents: comp.$subComponents.filter((child) => !layoutNameSet.has(child.name))
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
transformImport(specifier, { stack }) {
|
|
43
|
+
if (layoutNameSet.has(stack[0].name) || !(specifier in layoutComps)) return specifier;
|
|
44
|
+
return `fumadocs-ui/${layoutComps[specifier]}`;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/registry/installer.ts
|
|
50
|
+
var FumadocsComponentInstaller = class extends ComponentInstaller {
|
|
51
|
+
constructor(connector, config, cwd) {
|
|
52
|
+
super(connector, {
|
|
53
|
+
cwd,
|
|
54
|
+
framework: config.framework,
|
|
55
|
+
outDir: {
|
|
56
|
+
base: config.baseDir,
|
|
57
|
+
components: config.aliases.componentsDir,
|
|
58
|
+
css: config.aliases.cssDir,
|
|
59
|
+
layout: config.aliases.layoutDir,
|
|
60
|
+
lib: config.aliases.libDir,
|
|
61
|
+
ui: config.aliases.uiDir
|
|
62
|
+
},
|
|
63
|
+
io: {
|
|
64
|
+
onWarn: (message) => {
|
|
65
|
+
this.interactive?.spin.message(message);
|
|
66
|
+
},
|
|
67
|
+
confirmFileOverride: async (options) => {
|
|
68
|
+
if (!this.interactive) return true;
|
|
69
|
+
const { name, spin } = this.interactive;
|
|
70
|
+
spin.clear();
|
|
71
|
+
const value = await confirm({
|
|
72
|
+
message: `Do you want to override ${options.path}?`,
|
|
73
|
+
initialValue: false
|
|
74
|
+
});
|
|
75
|
+
if (isCancel(value)) {
|
|
76
|
+
outro("Installation terminated");
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));
|
|
80
|
+
return value;
|
|
81
|
+
},
|
|
82
|
+
onFileDownloaded: (options) => {
|
|
83
|
+
this.interactive?.spin.message(options.path);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
plugins: [pluginPreserveLayouts()]
|
|
87
|
+
});
|
|
88
|
+
this.interactive = null;
|
|
89
|
+
}
|
|
90
|
+
async installInteractive(name, subRegistry) {
|
|
91
|
+
if (this.interactive) throw new Error(`cannot install while installing another component`);
|
|
92
|
+
const spin = spinner();
|
|
93
|
+
spin.start(picocolors.bold(picocolors.cyanBright(`Installing ${name}`)));
|
|
94
|
+
try {
|
|
95
|
+
this.interactive = {
|
|
96
|
+
name,
|
|
97
|
+
spin
|
|
98
|
+
};
|
|
99
|
+
const deps = await super.install(name, subRegistry).then((res) => res.deps());
|
|
100
|
+
spin.stop(picocolors.bold(picocolors.greenBright(`${name} installed`)));
|
|
101
|
+
if (deps.hasRequired()) {
|
|
102
|
+
log.message();
|
|
103
|
+
box([...deps.dependencies, ...deps.devDependencies].join("\n"), "New Dependencies");
|
|
104
|
+
const pm = (await detect())?.name ?? "npm";
|
|
105
|
+
const value = await confirm({ message: `Do you want to install with ${pm}?` });
|
|
106
|
+
if (isCancel(value)) {
|
|
107
|
+
outro("Installation terminated");
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
if (value) {
|
|
111
|
+
const spin = spinner({ errorMessage: "Failed to install dependencies" });
|
|
112
|
+
spin.start("Installing dependencies");
|
|
113
|
+
await deps.installRequired(pm);
|
|
114
|
+
spin.stop("Dependencies installed");
|
|
115
|
+
} else await deps.writeRequired();
|
|
116
|
+
}
|
|
117
|
+
} catch (e) {
|
|
118
|
+
spin.error(e instanceof Error ? e.message : String(e));
|
|
119
|
+
process.exit(-1);
|
|
120
|
+
} finally {
|
|
121
|
+
this.interactive = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
//#endregion
|
|
126
|
+
export { FumadocsComponentInstaller as t };
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=installer-zD4jTQWp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer-zD4jTQWp.js","names":[],"sources":["../src/registry/plugins/preserve.ts","../src/registry/installer.ts"],"sourcesContent":["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/notebook',\n 'layouts/docs',\n 'layouts/shared',\n ];\n // original specifier -> new specifier\n const layoutComps: Record<string, string> = {\n '@/<dir>/home/index.tsx': 'layouts/home',\n '@/<dir>/shared/index.tsx': 'layouts/shared',\n '@/<dir>/shared/client.tsx': 'layouts/shared',\n '@/<dir>/notebook/index.tsx': 'layouts/notebook',\n '@/<dir>/notebook/client.tsx': 'layouts/notebook',\n '@/<dir>/notebook/page/index.tsx': 'layouts/notebook/page',\n '@/<dir>/notebook/page/client.tsx': 'layouts/notebook/page',\n '@/<dir>/docs/index.tsx': 'layouts/docs',\n '@/<dir>/docs/client.tsx': 'layouts/docs',\n '@/<dir>/docs/page/index.tsx': 'layouts/docs/page',\n '@/<dir>/docs/page/client.tsx': 'layouts/docs/page',\n '@/<dir>/flux/index.tsx': 'layouts/flux',\n '@/<dir>/flux/page/index.tsx': 'layouts/flux/page',\n '@/<dir>/flux/page/client.tsx': 'layouts/flux/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 { ComponentInstaller } from 'fuma-cli/registry/installer';\nimport { pluginPreserveLayouts } from './plugins/preserve';\nimport { RegistryConnector } from 'fuma-cli/registry/connector';\nimport type { LoadedConfig } from '@/config';\nimport { box, confirm, isCancel, log, outro, spinner, SpinnerResult } from '@clack/prompts';\nimport picocolors from 'picocolors';\nimport { detect } from 'package-manager-detector';\n\nexport class FumadocsComponentInstaller extends ComponentInstaller {\n private interactive: {\n name: string;\n spin: SpinnerResult;\n } | null = null;\n\n constructor(connector: RegistryConnector, config: LoadedConfig, cwd?: string) {\n super(connector, {\n cwd,\n framework: 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 },\n plugins: [pluginPreserveLayouts()],\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 detect())?.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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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LoadedConfig } from "../config.js";
|
|
2
|
+
import { ComponentInstaller } from "fuma-cli/registry/installer";
|
|
3
|
+
import { RegistryConnector } from "fuma-cli/registry/connector";
|
|
4
|
+
|
|
5
|
+
//#region src/registry/installer.d.ts
|
|
6
|
+
declare class FumadocsComponentInstaller extends ComponentInstaller {
|
|
7
|
+
private interactive;
|
|
8
|
+
constructor(connector: RegistryConnector, config: LoadedConfig, cwd?: string);
|
|
9
|
+
installInteractive(name: string, subRegistry?: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
export { FumadocsComponentInstaller };
|
|
13
|
+
//# sourceMappingURL=installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.d.ts","names":[],"sources":["../../src/registry/installer.ts"],"mappings":";;;;;cAQa,0BAAA,SAAmC,kBAAA;EAAA,QACtC,WAAA;cAKI,SAAA,EAAW,iBAAA,EAAmB,MAAA,EAAQ,YAAA,EAAc,GAAA;EAuC1D,kBAAA,CAAmB,IAAA,UAAc,WAAA,YAAuB,OAAA;AAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumadocs/cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "The CLI tool for Fumadocs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Docs",
|
|
@@ -18,33 +18,25 @@
|
|
|
18
18
|
],
|
|
19
19
|
"type": "module",
|
|
20
20
|
"exports": {
|
|
21
|
-
"./build": "./dist/build/index.js",
|
|
22
21
|
"./config": "./dist/config.js",
|
|
23
|
-
"./registry/
|
|
24
|
-
"./registry/installer": "./dist/registry/installer/index.js",
|
|
25
|
-
"./registry/macros/route-handler": "./dist/registry/macros/route-handler.js",
|
|
26
|
-
"./registry/schema": "./dist/registry/schema.js",
|
|
22
|
+
"./registry/installer": "./dist/registry/installer.js",
|
|
27
23
|
"./package.json": "./package.json"
|
|
28
24
|
},
|
|
29
25
|
"publishConfig": {
|
|
30
26
|
"access": "public"
|
|
31
27
|
},
|
|
32
28
|
"dependencies": {
|
|
33
|
-
"@clack/prompts": "^1.
|
|
29
|
+
"@clack/prompts": "^1.2.0",
|
|
34
30
|
"commander": "^14.0.3",
|
|
35
|
-
"
|
|
36
|
-
"oxc-parser": "^0.121.0",
|
|
37
|
-
"oxc-resolver": "^11.19.1",
|
|
31
|
+
"fuma-cli": "^0.0.3",
|
|
38
32
|
"package-manager-detector": "^1.6.0",
|
|
39
33
|
"picocolors": "^1.1.1",
|
|
40
|
-
"tinyexec": "^1.
|
|
34
|
+
"tinyexec": "^1.1.1",
|
|
41
35
|
"zod": "^4.3.6"
|
|
42
36
|
},
|
|
43
37
|
"devDependencies": {
|
|
44
|
-
"@
|
|
45
|
-
"
|
|
46
|
-
"shadcn": "4.1.1",
|
|
47
|
-
"tsdown": "0.21.6",
|
|
38
|
+
"@types/node": "25.5.2",
|
|
39
|
+
"tsdown": "0.21.7",
|
|
48
40
|
"tsconfig": "0.0.0"
|
|
49
41
|
},
|
|
50
42
|
"scripts": {
|
package/dist/build/index.d.ts
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { d as registryInfoSchema, n as CompiledFile, t as CompiledComponent } from "../schema-BAaUX4uu.js";
|
|
2
|
-
import { n as DistributiveOmit } from "../types-79PW0lgM.js";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { ResolverFactory } from "oxc-resolver";
|
|
5
|
-
|
|
6
|
-
//#region src/build/compiler.d.ts
|
|
7
|
-
type OnResolve = (reference: SourceReference, from: {
|
|
8
|
-
component: Component;
|
|
9
|
-
file: ComponentFile;
|
|
10
|
-
}) => Reference;
|
|
11
|
-
interface CompiledRegistry {
|
|
12
|
-
name: string;
|
|
13
|
-
components: CompiledComponent[];
|
|
14
|
-
info: z.output<typeof registryInfoSchema>;
|
|
15
|
-
}
|
|
16
|
-
type ComponentFile = DistributiveOmit<CompiledFile, 'content'> & {
|
|
17
|
-
path: string;
|
|
18
|
-
};
|
|
19
|
-
interface Component {
|
|
20
|
-
name: string;
|
|
21
|
-
title?: string;
|
|
22
|
-
description?: string;
|
|
23
|
-
files: ComponentFile[];
|
|
24
|
-
dependencies?: Record<string, string>;
|
|
25
|
-
devDependencies?: Record<string, string>;
|
|
26
|
-
/**
|
|
27
|
-
* Don't list the component in registry index file
|
|
28
|
-
*/
|
|
29
|
-
unlisted?: boolean;
|
|
30
|
-
/**
|
|
31
|
-
* Map imported file paths, inherit from registry if not defined.
|
|
32
|
-
*/
|
|
33
|
-
onResolve?: OnResolve;
|
|
34
|
-
}
|
|
35
|
-
interface PackageJson {
|
|
36
|
-
dependencies?: Record<string, string>;
|
|
37
|
-
devDependencies?: Record<string, string>;
|
|
38
|
-
}
|
|
39
|
-
interface Registry extends Omit<z.input<typeof registryInfoSchema>, 'indexes'> {
|
|
40
|
-
name: string;
|
|
41
|
-
packageJson: string | PackageJson;
|
|
42
|
-
tsconfigPath: string;
|
|
43
|
-
components: Component[];
|
|
44
|
-
/**
|
|
45
|
-
* The directory of registry, used to resolve relative paths
|
|
46
|
-
*/
|
|
47
|
-
dir: string;
|
|
48
|
-
/**
|
|
49
|
-
* Map import paths of components
|
|
50
|
-
*/
|
|
51
|
-
onResolve?: OnResolve;
|
|
52
|
-
/**
|
|
53
|
-
* When a referenced file is not found in component files, this function is called.
|
|
54
|
-
* @returns file, or `false` to mark as external.
|
|
55
|
-
*/
|
|
56
|
-
onUnknownFile?: (absolutePath: string) => ComponentFile | false | undefined;
|
|
57
|
-
dependencies?: Record<string, string | null>;
|
|
58
|
-
devDependencies?: Record<string, string | null>;
|
|
59
|
-
}
|
|
60
|
-
declare class RegistryCompiler {
|
|
61
|
-
readonly raw: Registry;
|
|
62
|
-
resolver: RegistryResolver;
|
|
63
|
-
constructor(registry: Registry);
|
|
64
|
-
private readPackageJson;
|
|
65
|
-
compile(): Promise<CompiledRegistry>;
|
|
66
|
-
}
|
|
67
|
-
declare class RegistryResolver {
|
|
68
|
-
private readonly compiler;
|
|
69
|
-
private readonly deps;
|
|
70
|
-
private readonly devDeps;
|
|
71
|
-
private readonly fileToComponent;
|
|
72
|
-
readonly oxc: ResolverFactory;
|
|
73
|
-
constructor(compiler: RegistryCompiler, packageJson?: PackageJson);
|
|
74
|
-
getDepFromSpecifier(specifier: string): string;
|
|
75
|
-
getDepInfo(name: string): {
|
|
76
|
-
type: 'runtime' | 'dev';
|
|
77
|
-
name: string;
|
|
78
|
-
version: string | null;
|
|
79
|
-
} | undefined;
|
|
80
|
-
getComponentByName(name: string): Component | undefined;
|
|
81
|
-
getSubComponent(file: string): {
|
|
82
|
-
component: Component;
|
|
83
|
-
file: ComponentFile;
|
|
84
|
-
} | undefined;
|
|
85
|
-
}
|
|
86
|
-
type SourceReference = {
|
|
87
|
-
type: 'file';
|
|
88
|
-
/**
|
|
89
|
-
* Absolute path
|
|
90
|
-
*/
|
|
91
|
-
file: string;
|
|
92
|
-
} | {
|
|
93
|
-
type: 'dependency';
|
|
94
|
-
dep: string;
|
|
95
|
-
specifier: string;
|
|
96
|
-
} | {
|
|
97
|
-
type: 'sub-component';
|
|
98
|
-
resolved: {
|
|
99
|
-
type: 'local';
|
|
100
|
-
component: Component;
|
|
101
|
-
file: ComponentFile;
|
|
102
|
-
} | {
|
|
103
|
-
type: 'remote';
|
|
104
|
-
component: Component;
|
|
105
|
-
file: ComponentFile;
|
|
106
|
-
registryName: string;
|
|
107
|
-
};
|
|
108
|
-
} | {
|
|
109
|
-
type: 'unknown-specifier';
|
|
110
|
-
specifier: string;
|
|
111
|
-
};
|
|
112
|
-
type Reference = SourceReference | {
|
|
113
|
-
type: 'custom';
|
|
114
|
-
specifier: string;
|
|
115
|
-
};
|
|
116
|
-
declare class ComponentCompiler {
|
|
117
|
-
private readonly compiler;
|
|
118
|
-
private readonly component;
|
|
119
|
-
private readonly processedFiles;
|
|
120
|
-
private readonly registry;
|
|
121
|
-
private readonly subComponents;
|
|
122
|
-
private readonly devDependencies;
|
|
123
|
-
private readonly dependencies;
|
|
124
|
-
constructor(compiler: RegistryCompiler, component: Component);
|
|
125
|
-
build(): Promise<CompiledComponent>;
|
|
126
|
-
private onBuildFile;
|
|
127
|
-
private buildFile;
|
|
128
|
-
}
|
|
129
|
-
declare function resolveFromRemote(r: Registry, component: string, selectFile: (file: ComponentFile) => boolean): Reference | undefined;
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/build/index.d.ts
|
|
132
|
-
interface MonoRegistry extends CompiledRegistry {
|
|
133
|
-
registries: CompiledRegistry[];
|
|
134
|
-
}
|
|
135
|
-
declare function combineRegistry(root: CompiledRegistry, ...items: CompiledRegistry[]): MonoRegistry;
|
|
136
|
-
declare function writeFumadocsRegistry(out: CompiledRegistry | MonoRegistry, options: {
|
|
137
|
-
dir: string;
|
|
138
|
-
/**
|
|
139
|
-
* Remove previous outputs
|
|
140
|
-
*
|
|
141
|
-
* @defaultValue false
|
|
142
|
-
*/
|
|
143
|
-
cleanDir?: boolean;
|
|
144
|
-
log?: boolean;
|
|
145
|
-
}): Promise<void>;
|
|
146
|
-
//#endregion
|
|
147
|
-
export { CompiledRegistry, Component, ComponentCompiler, ComponentFile, MonoRegistry, OnResolve, PackageJson, Reference, Registry, RegistryCompiler, SourceReference, combineRegistry, resolveFromRemote, writeFumadocsRegistry };
|
|
148
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/build/compiler.ts","../../src/build/index.ts"],"mappings":"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"}
|