UI: Redesign registry

This commit is contained in:
Fuma Nama
2025-12-17 22:26:47 +08:00
parent d4c87fe534
commit a69b06061a
12 changed files with 289 additions and 698 deletions
+9
View File
@@ -0,0 +1,9 @@
---
'fumadocs-ui': minor
'@fumadocs/base-ui': minor
'fumadocs-openapi': minor
'fumadocs-core': minor
'@fumadocs/ui': minor
---
Support both Base UI and Radix UI as base component libraries
+12 -25
View File
@@ -1,19 +1,12 @@
import { type Registry } from '@fumadocs/cli/build';
import * as ui from '../../../packages/ui/src/_registry';
import * as radixUi from '../../../packages/radix-ui/src/_registry';
import { fileURLToPath } from 'node:url';
import * as path from 'node:path';
import { resolveFromRemote } from '@fumadocs/cli/build';
const baseDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../');
function selectFrom(r: Registry, component: string, filename: string) {
const comp = r.components.find((comp) => comp.name === component)!;
return {
component: comp,
file: comp.files.find((file) => path.basename(file.path) === filename)!,
};
}
export const registry: Registry = {
dir: baseDir,
name: 'fumadocs',
@@ -30,14 +23,11 @@ export const registry: Registry = {
const filePath = path.relative(baseDir, ref.file);
if (filePath === 'lib/cn.ts') {
return {
type: 'sub-component',
resolved: {
type: 'remote',
registryName: ui.registry.name,
...selectFrom(ui.registry, 'cn', 'cn.ts'),
},
};
return resolveFromRemote(
ui.registry,
'cn',
(file) => file.path === 'cn.ts',
)!;
}
}
@@ -45,14 +35,11 @@ export const registry: Registry = {
ref.type === 'dependency' &&
ref.specifier === 'fumadocs-ui/components/ui/button'
) {
return {
type: 'sub-component',
resolved: {
type: 'remote',
registryName: ui.registry.name,
...selectFrom(ui.registry, 'button', 'button.tsx'),
},
};
return resolveFromRemote(
radixUi.registry,
'button',
(file) => path.basename(file.path) === 'button.tsx',
)!;
}
return ref;
+6 -2
View File
@@ -6,13 +6,17 @@ import {
} from '@fumadocs/cli/build';
import { registry } from '@/components/registry.js';
import * as ui from '../../../packages/ui/src/_registry';
import * as radixUi from '../../../packages/radix-ui/src/_registry';
import * as baseUi from '../../../packages/base-ui/src/_registry';
export async function buildRegistry() {
const [mainRegistry, uiRegistry] = await Promise.all([
const results = await Promise.all([
new RegistryCompiler(registry).compile(),
new RegistryCompiler(ui.registry).compile(),
new RegistryCompiler(radixUi.registry).compile(),
new RegistryCompiler(baseUi.registry).compile(),
]);
const all = combineRegistry(mainRegistry, uiRegistry);
const all = combineRegistry(...results);
await Promise.all([
writeFumadocsRegistry(all, {
+40 -138
View File
@@ -1,101 +1,20 @@
import { fileURLToPath } from 'node:url';
import type { Registry } from '@fumadocs/cli/build';
import * as path from 'node:path';
import { resolveForwardedAPIs } from '../../../ui/src/_registry';
const srcDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../');
// in shadcn cli, the order of files matters when writing import paths on consumer's codebase
export const registry: Registry = {
name: 'fumadocs-ui',
name: 'fumadocs/base-ui',
dir: srcDir,
tsconfigPath: '../tsconfig.json',
packageJson: '../package.json',
onResolve(ref) {
if (ref.type !== 'file') return ref;
const filePath = path.relative(srcDir, ref.file);
if (filePath === 'icons.tsx')
return {
type: 'dependency',
dep: 'lucide-react',
specifier: 'lucide-react',
};
if (
(filePath.startsWith('utils/') || filePath.startsWith('contexts/')) &&
filePath !== 'utils/cn.ts'
) {
return {
type: 'dependency',
dep: 'fumadocs-ui',
specifier: `fumadocs-ui/${filePath.slice(0, -path.extname(filePath).length)}`,
};
}
return ref;
},
onUnknownFile(file) {
const relativePath = path.relative(srcDir, file);
if (relativePath.startsWith('utils/'))
return {
type: 'lib',
path: relativePath,
};
if (relativePath.startsWith('components/ui/')) {
return {
type: 'components',
path: relativePath,
};
}
return resolveForwardedAPIs(ref, '@fumadocs/base-ui', registry) ?? ref;
},
components: [
{
name: 'layouts/docs-min',
description: 'Replace Docs Layout (Minimal)',
files: [
{
type: 'block',
path: '_registry/layout/docs-min.tsx',
target: 'components/layout/docs/index.tsx',
},
{
type: 'block',
path: '_registry/layout/page-min.tsx',
target: 'components/layout/docs/page.tsx',
},
],
unlisted: true,
},
{
name: 'is-active',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/is-active.ts',
},
],
},
{
name: 'cn',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/cn.ts',
},
],
},
{
name: 'merge-refs',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/merge-refs.ts',
},
],
},
{
name: 'layouts/shared',
unlisted: true,
@@ -103,67 +22,47 @@ export const registry: Registry = {
{
type: 'components',
path: 'layouts/shared/index.tsx',
target: 'components/layout/shared.tsx',
target: '<dir>/layout/shared.tsx',
},
{
type: 'components',
path: 'layouts/shared/language-toggle.tsx',
target: 'components/layout/language-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/link-item.tsx',
target: 'components/layout/link-item.tsx',
target: '<dir>/layout/language-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/search-toggle.tsx',
target: 'components/layout/search-toggle.tsx',
target: '<dir>/layout/search-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/theme-toggle.tsx',
target: 'components/layout/theme-toggle.tsx',
},
{
type: 'components',
path: 'components/toc/clerk.tsx',
target: 'components/toc/clerk.tsx',
},
{
type: 'components',
path: 'components/toc/default.tsx',
target: 'components/toc/default.tsx',
},
{
type: 'components',
path: 'components/toc/index.tsx',
target: 'components/toc/index.tsx',
target: '<dir>/layout/theme-toggle.tsx',
},
{
type: 'components',
path: 'components/sidebar/base.tsx',
target: 'components/layout/sidebar/base.tsx',
target: '<dir>/layout/sidebar/base.tsx',
},
{
type: 'components',
path: 'components/sidebar/page-tree.tsx',
target: 'components/layout/sidebar/page-tree.tsx',
target: '<dir>/layout/sidebar/page-tree.tsx',
},
{
type: 'components',
path: 'components/sidebar/link-item.tsx',
target: 'components/layout/sidebar/link-item.tsx',
target: '<dir>/layout/sidebar/link-item.tsx',
},
{
type: 'components',
path: 'components/sidebar/tabs/index.tsx',
target: 'components/layout/sidebar/tabs/index.tsx',
target: '<dir>/layout/sidebar/tabs/index.tsx',
},
{
type: 'components',
path: 'components/sidebar/tabs/dropdown.tsx',
target: 'components/layout/sidebar/tabs/dropdown.tsx',
target: '<dir>/layout/sidebar/tabs/dropdown.tsx',
},
],
},
@@ -171,29 +70,29 @@ export const registry: Registry = {
name: 'layouts/docs',
files: [
{
type: 'block',
type: 'components',
path: 'layouts/docs/index.tsx',
target: 'components/layout/docs/index.tsx',
target: '<dir>/layout/docs/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/client.tsx',
target: 'components/layout/docs/client.tsx',
target: '<dir>/layout/docs/client.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/sidebar.tsx',
target: 'components/layout/docs/sidebar.tsx',
target: '<dir>/layout/docs/sidebar.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/page/index.tsx',
target: 'components/layout/docs/page/index.tsx',
target: '<dir>/layout/docs/page/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/page/client.tsx',
target: 'components/layout/docs/page/client.tsx',
target: '<dir>/layout/docs/page/client.tsx',
},
],
unlisted: true,
@@ -202,29 +101,29 @@ export const registry: Registry = {
name: 'layouts/notebook',
files: [
{
type: 'block',
type: 'components',
path: 'layouts/notebook/index.tsx',
target: 'components/layout/notebook/index.tsx',
target: '<dir>/layout/notebook/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/client.tsx',
target: 'components/layout/notebook/client.tsx',
target: '<dir>/layout/notebook/client.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/sidebar.tsx',
target: 'components/layout/notebook/sidebar.tsx',
target: '<dir>/layout/notebook/sidebar.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/page/index.tsx',
target: 'components/layout/notebook/page/index.tsx',
target: '<dir>/layout/notebook/page/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/page/client.tsx',
target: 'components/layout/notebook/page/client.tsx',
target: '<dir>/layout/notebook/page/client.tsx',
},
],
unlisted: true,
@@ -233,14 +132,18 @@ export const registry: Registry = {
name: 'layouts/home',
files: [
{
type: 'block',
type: 'components',
path: 'layouts/home/index.tsx',
target: 'components/layout/home/index.tsx',
target: '<dir>/layout/home/index.tsx',
},
{
type: 'components',
path: 'layouts/home/client.tsx',
target: 'components/layout/home/client.tsx',
target: '<dir>/layout/home/client.tsx',
},
{
type: 'ui',
path: 'components/ui/navigation-menu.tsx',
},
],
unlisted: true,
@@ -419,8 +322,7 @@ export const registry: Registry = {
],
dependencies: {
'fumadocs-core': null,
'fumadocs-ui': null,
'@fumadocs/ui/icons': null,
'@fumadocs/base-ui': null,
react: null,
},
};
+21
View File
@@ -464,3 +464,24 @@ export class ComponentCompiler {
};
}
}
export function resolveFromRemote(
r: Registry,
component: string,
selectFile: (file: ComponentFile) => boolean,
): Reference | undefined {
const comp = r.components.find((comp) => comp.name === component);
if (!comp) return;
const file = comp.files.find(selectFile);
if (!file) return;
return {
type: 'sub-component',
resolved: {
type: 'remote',
registryName: r.name,
component: comp,
file,
},
};
}
+2
View File
@@ -14,10 +14,12 @@ export function combineRegistry(
const out: CompiledRegistry = {
index: [],
components: [],
switchables: {},
name: items[0].name,
};
for (const item of items) {
Object.assign(out.switchables!, item.switchables);
out.components.push(...item.components);
out.index.push(...item.index);
}
+4 -6
View File
@@ -184,11 +184,6 @@ export class ComponentInstaller {
private resolveOutputPath(ref: File): string {
const config = this.client.config;
if (ref.target) {
return path.join(config.baseDir, ref.target);
}
const base = path.basename(ref.path);
const dir = (
{
components: config.aliases.componentsDir,
@@ -199,7 +194,10 @@ export class ComponentInstaller {
route: './',
} as const
)[ref.type];
if (ref.target) {
return path.join(config.baseDir, ref.target.replace('<dir>', dir));
}
return path.join(config.baseDir, dir, base);
return path.join(config.baseDir, dir, path.basename(ref.path));
}
}
+40 -140
View File
@@ -1,101 +1,18 @@
import { fileURLToPath } from 'node:url';
import type { Registry } from '@fumadocs/cli/build';
import * as path from 'node:path';
const srcDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../');
import { resolveForwardedAPIs } from '../../../ui/src/_registry';
// in shadcn cli, the order of files matters when writing import paths on consumer's codebase
export const registry: Registry = {
name: 'fumadocs-ui',
dir: srcDir,
name: 'fumadocs/radix-ui',
dir: path.join(path.dirname(fileURLToPath(import.meta.url)), '../'),
tsconfigPath: '../tsconfig.json',
packageJson: '../package.json',
onResolve(ref) {
if (ref.type !== 'file') return ref;
const filePath = path.relative(srcDir, ref.file);
if (filePath === 'icons.tsx')
return {
type: 'dependency',
dep: 'lucide-react',
specifier: 'lucide-react',
};
if (
(filePath.startsWith('utils/') || filePath.startsWith('contexts/')) &&
filePath !== 'utils/cn.ts'
) {
return {
type: 'dependency',
dep: 'fumadocs-ui',
specifier: `fumadocs-ui/${filePath.slice(0, -path.extname(filePath).length)}`,
};
}
return ref;
},
onUnknownFile(file) {
const relativePath = path.relative(srcDir, file);
if (relativePath.startsWith('utils/'))
return {
type: 'lib',
path: relativePath,
};
if (relativePath.startsWith('components/ui/')) {
return {
type: 'components',
path: relativePath,
};
}
return resolveForwardedAPIs(ref, 'fumadocs-ui', registry) ?? ref;
},
components: [
{
name: 'layouts/docs-min',
description: 'Replace Docs Layout (Minimal)',
files: [
{
type: 'block',
path: '_registry/layout/docs-min.tsx',
target: 'components/layout/docs/index.tsx',
},
{
type: 'block',
path: '_registry/layout/page-min.tsx',
target: 'components/layout/docs/page.tsx',
},
],
unlisted: true,
},
{
name: 'is-active',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/is-active.ts',
},
],
},
{
name: 'cn',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/cn.ts',
},
],
},
{
name: 'merge-refs',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/merge-refs.ts',
},
],
},
{
name: 'layouts/shared',
unlisted: true,
@@ -103,67 +20,47 @@ export const registry: Registry = {
{
type: 'components',
path: 'layouts/shared/index.tsx',
target: 'components/layout/shared.tsx',
target: '<dir>/layout/shared.tsx',
},
{
type: 'components',
path: 'layouts/shared/language-toggle.tsx',
target: 'components/layout/language-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/link-item.tsx',
target: 'components/layout/link-item.tsx',
target: '<dir>/layout/language-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/search-toggle.tsx',
target: 'components/layout/search-toggle.tsx',
target: '<dir>/layout/search-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/theme-toggle.tsx',
target: 'components/layout/theme-toggle.tsx',
},
{
type: 'components',
path: 'components/toc/clerk.tsx',
target: 'components/toc/clerk.tsx',
},
{
type: 'components',
path: 'components/toc/default.tsx',
target: 'components/toc/default.tsx',
},
{
type: 'components',
path: 'components/toc/index.tsx',
target: 'components/toc/index.tsx',
target: '<dir>/layout/theme-toggle.tsx',
},
{
type: 'components',
path: 'components/sidebar/base.tsx',
target: 'components/layout/sidebar/base.tsx',
target: '<dir>/layout/sidebar/base.tsx',
},
{
type: 'components',
path: 'components/sidebar/page-tree.tsx',
target: 'components/layout/sidebar/page-tree.tsx',
target: '<dir>/layout/sidebar/page-tree.tsx',
},
{
type: 'components',
path: 'components/sidebar/link-item.tsx',
target: 'components/layout/sidebar/link-item.tsx',
target: '<dir>/layout/sidebar/link-item.tsx',
},
{
type: 'components',
path: 'components/sidebar/tabs/index.tsx',
target: 'components/layout/sidebar/tabs/index.tsx',
target: '<dir>/layout/sidebar/tabs/index.tsx',
},
{
type: 'components',
path: 'components/sidebar/tabs/dropdown.tsx',
target: 'components/layout/sidebar/tabs/dropdown.tsx',
target: '<dir>/layout/sidebar/tabs/dropdown.tsx',
},
],
},
@@ -171,29 +68,29 @@ export const registry: Registry = {
name: 'layouts/docs',
files: [
{
type: 'block',
type: 'components',
path: 'layouts/docs/index.tsx',
target: 'components/layout/docs/index.tsx',
target: '<dir>/layout/docs/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/client.tsx',
target: 'components/layout/docs/client.tsx',
target: '<dir>/layout/docs/client.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/sidebar.tsx',
target: 'components/layout/docs/sidebar.tsx',
target: '<dir>/layout/docs/sidebar.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/page/index.tsx',
target: 'components/layout/docs/page/index.tsx',
target: '<dir>/layout/docs/page/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/docs/page/client.tsx',
target: 'components/layout/docs/page/client.tsx',
target: '<dir>/layout/docs/page/client.tsx',
},
],
unlisted: true,
@@ -202,29 +99,29 @@ export const registry: Registry = {
name: 'layouts/notebook',
files: [
{
type: 'block',
type: 'components',
path: 'layouts/notebook/index.tsx',
target: 'components/layout/notebook/index.tsx',
target: '<dir>/layout/notebook/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/client.tsx',
target: 'components/layout/notebook/client.tsx',
target: '<dir>/layout/notebook/client.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/sidebar.tsx',
target: 'components/layout/notebook/sidebar.tsx',
target: '<dir>/layout/notebook/sidebar.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/page/index.tsx',
target: 'components/layout/notebook/page/index.tsx',
target: '<dir>/layout/notebook/page/index.tsx',
},
{
type: 'block',
type: 'components',
path: 'layouts/notebook/page/client.tsx',
target: 'components/layout/notebook/page/client.tsx',
target: '<dir>/layout/notebook/page/client.tsx',
},
],
unlisted: true,
@@ -233,14 +130,18 @@ export const registry: Registry = {
name: 'layouts/home',
files: [
{
type: 'block',
type: 'components',
path: 'layouts/home/index.tsx',
target: 'components/layout/home/index.tsx',
target: '<dir>/layout/home/index.tsx',
},
{
type: 'components',
path: 'layouts/home/client.tsx',
target: 'components/layout/home/client.tsx',
target: '<dir>/layout/home/client.tsx',
},
{
type: 'ui',
path: 'components/ui/navigation-menu.tsx',
},
],
unlisted: true,
@@ -420,7 +321,6 @@ export const registry: Registry = {
dependencies: {
'fumadocs-core': null,
'fumadocs-ui': null,
'@fumadocs/ui/icons': null,
react: null,
},
};
+1
View File
@@ -38,6 +38,7 @@
},
"devDependencies": {
"@fumadocs/cli": "workspace:*",
"@types/bun": "^1.3.4",
"@types/lodash.merge": "^4.6.9",
"@types/node": "^24.10.2",
"@types/react": "^19.2.7",
+145 -332
View File
@@ -1,19 +1,118 @@
import { fileURLToPath } from 'node:url';
import type { Registry } from '@fumadocs/cli/build';
import type {
Registry,
Reference,
SourceReference,
Component,
ComponentFile,
} from '@fumadocs/cli/build';
import * as path from 'node:path';
import { Glob } from 'bun';
const srcDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '../');
// in shadcn cli, the order of files matters when writing import paths on consumer's codebase
// import specifier -> sub component & file
const installables: Record<
string,
{
comp: Component;
file: ComponentFile;
}
> = {};
const contextFiles = new Set(
new Glob('*').scanSync({ cwd: path.join(srcDir, 'contexts') }),
);
const hookFiles = new Set(
new Glob('*').scanSync({ cwd: path.join(srcDir, 'hooks') }),
);
const componentFiles = new Set(
new Glob('**/*').scanSync({ cwd: path.join(srcDir, 'components') }),
);
export function resolveForwardedAPIs(
ref: SourceReference,
upstreamPackage: string,
upstreamRegistry: Registry,
): Reference | undefined {
if (ref.type === 'dependency' && ref.dep === '@fumadocs/ui') {
const specifier = ref.specifier;
if (specifier === '@fumadocs/ui/icons') {
return {
type: 'dependency',
dep: 'lucide-react',
specifier: 'lucide-react',
};
}
if (specifier in installables) {
const { comp, file } = installables[specifier];
return {
type: 'sub-component',
resolved: {
type: 'remote',
registryName: registry.name,
component: comp,
file,
},
};
}
return {
type: 'dependency',
dep: upstreamPackage,
specifier: specifier
.replace('@fumadocs/ui/hooks', `${upstreamPackage}/utils`)
.replace('@fumadocs/ui', upstreamPackage),
};
}
if (ref.type === 'file') {
const filePath = path
.relative(upstreamRegistry.dir, ref.file)
.replaceAll('\\', '/');
let hasForwarding: boolean;
const [dir, rest] = splitDir(filePath);
switch (dir) {
case 'contexts':
hasForwarding = contextFiles.has(rest);
break;
case 'components':
hasForwarding = componentFiles.has(rest);
break;
case 'utils':
hasForwarding = hookFiles.has(rest);
break;
default:
hasForwarding = filePath === 'i18n.tsx';
}
if (hasForwarding)
return resolveForwardedAPIs(
{
type: 'dependency',
dep: '@fumadocs/ui',
specifier: `@fumadocs/ui/${filePath.slice(0, -path.extname(filePath).length)}`,
},
upstreamPackage,
upstreamRegistry,
);
}
}
export const registry: Registry = {
name: 'fumadocs-ui',
name: 'fumadocs/ui',
dir: srcDir,
tsconfigPath: '../tsconfig.json',
packageJson: '../package.json',
onResolve(ref) {
if (ref.type !== 'file') return ref;
const filePath = path
.relative(registry.dir, ref.file)
.replaceAll('\\', '/');
const filePath = path.relative(srcDir, ref.file);
if (filePath === 'icons.tsx')
return {
type: 'dependency',
@@ -21,33 +120,17 @@ export const registry: Registry = {
specifier: 'lucide-react',
};
if (
(filePath.startsWith('utils/') || filePath.startsWith('contexts/')) &&
filePath !== 'utils/cn.ts'
) {
return {
type: 'dependency',
dep: 'fumadocs-ui',
specifier: `fumadocs-ui/${filePath.slice(0, -path.extname(filePath).length)}`,
};
const [dir, rest] = splitDir(filePath);
switch (dir) {
case 'contexts':
return {
type: 'custom',
specifier: `<ui>/${dir}/${rest.slice(0, -path.extname(rest).length)}`,
};
}
return ref;
},
onUnknownFile(file) {
const relativePath = path.relative(srcDir, file);
if (relativePath.startsWith('utils/'))
return {
type: 'lib',
path: relativePath,
};
if (relativePath.startsWith('components/ui/')) {
return {
type: 'components',
path: relativePath,
};
}
},
components: [
{
name: 'layouts/docs-min',
@@ -67,12 +150,12 @@ export const registry: Registry = {
unlisted: true,
},
{
name: 'is-active',
name: 'urls',
unlisted: true,
files: [
{
type: 'lib',
path: 'utils/is-active.ts',
path: 'urls.ts',
},
],
},
@@ -82,7 +165,7 @@ export const registry: Registry = {
files: [
{
type: 'lib',
path: 'utils/cn.ts',
path: 'cn.ts',
},
],
},
@@ -92,334 +175,64 @@ export const registry: Registry = {
files: [
{
type: 'lib',
path: 'utils/merge-refs.ts',
path: 'merge-refs.ts',
},
],
},
{
name: 'layouts/shared',
name: 'link-item',
unlisted: true,
files: [
{
type: 'components',
path: 'layouts/shared/index.tsx',
target: 'components/layout/shared.tsx',
},
{
type: 'components',
path: 'layouts/shared/language-toggle.tsx',
target: 'components/layout/language-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/link-item.tsx',
target: 'components/layout/link-item.tsx',
},
{
type: 'components',
path: 'layouts/shared/search-toggle.tsx',
target: 'components/layout/search-toggle.tsx',
},
{
type: 'components',
path: 'layouts/shared/theme-toggle.tsx',
target: 'components/layout/theme-toggle.tsx',
path: 'link-item.tsx',
target: '<dir>/layout/link-item.tsx',
},
],
},
{
name: 'toc',
unlisted: true,
files: [
{
type: 'components',
path: 'components/toc/clerk.tsx',
target: 'components/toc/clerk.tsx',
target: '<dir>/toc/clerk.tsx',
},
{
type: 'components',
path: 'components/toc/default.tsx',
target: 'components/toc/default.tsx',
target: '<dir>/toc/default.tsx',
},
{
type: 'components',
path: 'components/toc/index.tsx',
target: 'components/toc/index.tsx',
},
{
type: 'components',
path: 'components/sidebar/base.tsx',
target: 'components/layout/sidebar/base.tsx',
},
{
type: 'components',
path: 'components/sidebar/page-tree.tsx',
target: 'components/layout/sidebar/page-tree.tsx',
},
{
type: 'components',
path: 'components/sidebar/link-item.tsx',
target: 'components/layout/sidebar/link-item.tsx',
},
{
type: 'components',
path: 'components/sidebar/tabs/index.tsx',
target: 'components/layout/sidebar/tabs/index.tsx',
},
{
type: 'components',
path: 'components/sidebar/tabs/dropdown.tsx',
target: 'components/layout/sidebar/tabs/dropdown.tsx',
},
],
},
{
name: 'layouts/docs',
files: [
{
type: 'block',
path: 'layouts/docs/index.tsx',
target: 'components/layout/docs/index.tsx',
},
{
type: 'block',
path: 'layouts/docs/client.tsx',
target: 'components/layout/docs/client.tsx',
},
{
type: 'block',
path: 'layouts/docs/sidebar.tsx',
target: 'components/layout/docs/sidebar.tsx',
},
{
type: 'block',
path: 'layouts/docs/page/index.tsx',
target: 'components/layout/docs/page/index.tsx',
},
{
type: 'block',
path: 'layouts/docs/page/client.tsx',
target: 'components/layout/docs/page/client.tsx',
},
],
unlisted: true,
},
{
name: 'layouts/notebook',
files: [
{
type: 'block',
path: 'layouts/notebook/index.tsx',
target: 'components/layout/notebook/index.tsx',
},
{
type: 'block',
path: 'layouts/notebook/client.tsx',
target: 'components/layout/notebook/client.tsx',
},
{
type: 'block',
path: 'layouts/notebook/sidebar.tsx',
target: 'components/layout/notebook/sidebar.tsx',
},
{
type: 'block',
path: 'layouts/notebook/page/index.tsx',
target: 'components/layout/notebook/page/index.tsx',
},
{
type: 'block',
path: 'layouts/notebook/page/client.tsx',
target: 'components/layout/notebook/page/client.tsx',
},
],
unlisted: true,
},
{
name: 'layouts/home',
files: [
{
type: 'block',
path: 'layouts/home/index.tsx',
target: 'components/layout/home/index.tsx',
},
{
type: 'components',
path: 'layouts/home/client.tsx',
target: 'components/layout/home/client.tsx',
},
],
unlisted: true,
},
{
name: 'accordion',
files: [
{
type: 'components',
path: 'components/accordion.tsx',
},
{
type: 'ui',
path: 'components/ui/accordion.tsx',
},
],
},
{
name: 'github-info',
files: [
{
type: 'components',
path: 'components/github-info.tsx',
},
],
description: 'A card to display GitHub repo info',
},
{
name: 'banner',
files: [
{
type: 'components',
path: 'components/banner.tsx',
},
],
},
{
name: 'callout',
files: [
{
type: 'components',
path: 'components/callout.tsx',
},
],
},
{
name: 'card',
files: [
{
type: 'components',
path: 'components/card.tsx',
},
],
},
{
name: 'codeblock',
files: [
{
type: 'components',
path: 'components/codeblock.tsx',
},
],
},
{
name: 'files',
files: [
{
type: 'components',
path: 'components/files.tsx',
},
],
},
{
name: 'heading',
files: [
{
type: 'components',
path: 'components/heading.tsx',
},
],
},
{
name: 'image-zoom',
description: 'Zoomable Image',
files: [
{
type: 'components',
path: 'components/image-zoom.tsx',
},
{
type: 'css',
path: 'components/image-zoom.css',
},
],
},
{
name: 'inline-toc',
files: [
{
type: 'components',
path: 'components/inline-toc.tsx',
},
],
},
{
name: 'steps',
files: [
{
type: 'components',
path: 'components/steps.tsx',
},
],
},
{
name: 'tabs',
files: [
{
type: 'components',
path: 'components/tabs.tsx',
},
{
type: 'ui',
path: 'components/ui/tabs.tsx',
},
],
},
{
name: 'type-table',
files: [
{
type: 'components',
path: 'components/type-table.tsx',
},
],
},
{
name: 'button',
unlisted: true,
files: [
{
type: 'ui',
path: 'components/ui/button.tsx',
},
],
},
{
name: 'popover',
unlisted: true,
files: [
{
type: 'ui',
path: 'components/ui/popover.tsx',
},
],
},
{
name: 'scroll-area',
unlisted: true,
files: [
{
type: 'ui',
path: 'components/ui/scroll-area.tsx',
},
],
},
{
name: 'collapsible',
unlisted: true,
files: [
{
type: 'ui',
path: 'components/ui/collapsible.tsx',
target: '<dir>/toc/index.tsx',
},
],
},
],
dependencies: {
'fumadocs-core': null,
'fumadocs-ui': null,
react: null,
},
};
function splitDir(filePath: string): [dir: string, rest: string] {
const idx = filePath.indexOf('/');
if (idx === -1) {
return ['', filePath];
} else {
return [filePath.substring(0, idx), filePath.substring(idx + 1)];
}
}
for (const comp of registry.components) {
for (const file of comp.files) {
const importPath = file.path.slice(0, -path.extname(file.path).length);
installables[`@fumadocs/ui/${importPath}`] = {
comp,
file,
};
}
}
+2 -1
View File
@@ -3,7 +3,8 @@
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"types": ["bun"]
},
"tsc-alias": {
"resolveFullPaths": true
+7 -54
View File
@@ -802,7 +802,7 @@ importers:
devDependencies:
'@react-router/dev':
specifier: ^7.10.1
version: 7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)
version: 7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
@@ -869,7 +869,7 @@ importers:
devDependencies:
'@react-router/dev':
specifier: ^7.10.1
version: 7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)
version: 7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
@@ -1000,7 +1000,7 @@ importers:
devDependencies:
'@react-router/dev':
specifier: ^7.10.1
version: 7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)
version: 7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
@@ -2460,6 +2460,9 @@ importers:
'@fumadocs/cli':
specifier: workspace:*
version: link:../cli
'@types/bun':
specifier: ^1.3.4
version: 1.3.4
'@types/lodash.merge':
specifier: ^4.6.9
version: 4.6.9
@@ -6771,6 +6774,7 @@ packages:
bun@1.3.4:
resolution: {integrity: sha512-xV6KgD5ImquuKsoghzbWmYzeCXmmSgN6yJGz444hri2W+NGKNRFUNrEhy9+/rRXbvNA2qF0K0jAwqFNy1/GhBg==}
cpu: [arm64, x64]
os: [darwin, linux, win32]
hasBin: true
@@ -14271,57 +14275,6 @@ snapshots:
- tsx
- yaml
'@react-router/dev@7.10.1(@react-router/serve@7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3))(@types/node@24.10.2)(@vitejs/plugin-rsc@0.5.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(babel-plugin-macros@3.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(yaml@2.8.2)':
dependencies:
'@babel/core': 7.28.5
'@babel/generator': 7.28.5
'@babel/parser': 7.28.5
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5)
'@babel/preset-typescript': 7.28.5(@babel/core@7.28.5)
'@babel/traverse': 7.28.5
'@babel/types': 7.28.5
'@react-router/node': 7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)
'@remix-run/node-fetch-server': 0.9.0
arg: 5.0.2
babel-dead-code-elimination: 1.0.10
chokidar: 4.0.3
dedent: 1.7.0(babel-plugin-macros@3.1.0)
es-module-lexer: 1.7.0
exit-hook: 2.2.1
isbot: 5.1.32
jsesc: 3.0.2
lodash: 4.17.21
p-map: 7.0.4
pathe: 1.1.2
picocolors: 1.1.1
pkg-types: 2.3.0
prettier: 3.7.4
react-refresh: 0.14.2
react-router: 7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
semver: 7.7.3
tinyglobby: 0.2.15
valibot: 1.2.0(typescript@5.9.3)
vite: 7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
vite-node: 3.2.4(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)
optionalDependencies:
'@react-router/serve': 7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)
'@vitejs/plugin-rsc': 0.5.7(react-dom@19.2.3(react@19.2.3))(react-server-dom-webpack@19.2.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(webpack@5.103.0(@swc/core@1.15.4)(esbuild@0.27.0)))(react@19.2.3)(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))
typescript: 5.9.3
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
- jiti
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
'@react-router/express@7.10.1(express@4.22.1)(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)':
dependencies:
'@react-router/node': 7.10.1(react-router@7.10.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@5.9.3)