mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
CLI: Support slot in customise command
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@fumadocs/cli': patch
|
||||
---
|
||||
|
||||
Support slot in customise command
|
||||
@@ -104,6 +104,7 @@ export class RegistryCompiler {
|
||||
name: registry.name,
|
||||
info: {
|
||||
indexes: [],
|
||||
unlistedIndexes: [],
|
||||
env: registry.env,
|
||||
variables: registry.variables,
|
||||
},
|
||||
@@ -119,14 +120,13 @@ export class RegistryCompiler {
|
||||
);
|
||||
|
||||
for (const [input, comp] of builtComps) {
|
||||
if (!input.unlisted) {
|
||||
output.info.indexes.push({
|
||||
name: input.name,
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
});
|
||||
}
|
||||
const arr = input.unlisted ? output.info.unlistedIndexes : output.info.indexes;
|
||||
|
||||
arr.push({
|
||||
name: input.name,
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
});
|
||||
output.components.push(comp);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,15 @@ import { pluginPreserveLayouts } from '@/registry/plugins/preserve';
|
||||
|
||||
interface TargetInfo {
|
||||
target: string[];
|
||||
replace: [string, string][];
|
||||
id: string;
|
||||
print?: () => void;
|
||||
}
|
||||
|
||||
interface SlotPrintInfo {
|
||||
at: string;
|
||||
layoutId: string;
|
||||
name: string;
|
||||
isPage: boolean;
|
||||
}
|
||||
|
||||
export async function customise(client: RegistryClient) {
|
||||
@@ -18,79 +26,138 @@ export async function customise(client: RegistryClient) {
|
||||
plugins: [pluginPreserveLayouts()],
|
||||
});
|
||||
const registry = UIRegistries[config.uiLibrary];
|
||||
const info = await client.createLinkedRegistryClient(registry).fetchRegistryInfo();
|
||||
|
||||
const result = await group(
|
||||
{
|
||||
layout: () =>
|
||||
layout: (): Promise<TargetInfo | symbol> =>
|
||||
select({
|
||||
message: 'What do you want to customise?',
|
||||
options: [
|
||||
{
|
||||
label: 'Docs Layout',
|
||||
value: 'docs',
|
||||
hint: 'main UI of your docs',
|
||||
value: {
|
||||
id: 'docs',
|
||||
target: [`${registry}/layouts/docs`],
|
||||
print() {
|
||||
printLayout(
|
||||
['fumadocs-ui/layouts/docs', '@/layouts/docs'],
|
||||
['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],
|
||||
);
|
||||
},
|
||||
},
|
||||
hint: 'the default docs layout',
|
||||
},
|
||||
{
|
||||
label: 'Notebook Layout',
|
||||
value: {
|
||||
id: 'notebook',
|
||||
target: [`${registry}/layouts/notebook`],
|
||||
print() {
|
||||
printLayout(
|
||||
['fumadocs-ui/layouts/notebook', '@/layouts/notebook'],
|
||||
['fumadocs-ui/layouts/notebook/page', '@/layouts/notebook/page'],
|
||||
);
|
||||
},
|
||||
},
|
||||
hint: 'a more compact version of docs layout',
|
||||
},
|
||||
{
|
||||
label: 'Flux Layout',
|
||||
value: {
|
||||
id: 'flux',
|
||||
target: [`${registry}/layouts/flux`],
|
||||
print() {
|
||||
printLayout(
|
||||
['fumadocs-ui/layouts/flux', '@/layouts/flux'],
|
||||
['fumadocs-ui/layouts/flux/page', '@/layouts/flux/page'],
|
||||
);
|
||||
},
|
||||
},
|
||||
hint: 'the experimental variant of docs layout',
|
||||
},
|
||||
{
|
||||
label: 'Home Layout',
|
||||
value: 'home',
|
||||
hint: 'the navbar for your other pages',
|
||||
value: {
|
||||
id: 'home',
|
||||
target: [`${registry}/layouts/home`],
|
||||
print() {
|
||||
printLayout(['fumadocs-ui/layouts/home', `@/layouts/home`]);
|
||||
},
|
||||
},
|
||||
hint: 'the layout for other non-docs pages',
|
||||
},
|
||||
],
|
||||
}),
|
||||
target: (v): Promise<TargetInfo | symbol> => {
|
||||
if (v.results.layout !== 'docs')
|
||||
return Promise.resolve({
|
||||
target: [`${registry}/layouts/home`],
|
||||
replace: [['fumadocs-ui/layouts/home', `@/components/layout/home`]],
|
||||
});
|
||||
const selected = v.results.layout!;
|
||||
if (selected.id === 'home') return Promise.resolve(selected);
|
||||
|
||||
return select<TargetInfo>({
|
||||
message: 'Which variant do you want to start from?',
|
||||
message: 'Which part do you want to customise?',
|
||||
options: [
|
||||
{
|
||||
label: 'Start from minimal styles',
|
||||
hint: 'for those who want to build their own variant from ground up.',
|
||||
label: 'All',
|
||||
hint: 'install the entire layout',
|
||||
value: selected,
|
||||
},
|
||||
{
|
||||
label: 'Replace & rewrite from minimal styles',
|
||||
hint: 'for those who want to build their own UI from ground up',
|
||||
value: {
|
||||
id: 'docs-min',
|
||||
target: ['layouts/docs-min'],
|
||||
replace: [
|
||||
['fumadocs-ui/layouts/docs', '@/components/layout/docs'],
|
||||
['fumadocs-ui/layouts/docs/page', '@/components/layout/docs/page'],
|
||||
],
|
||||
print() {
|
||||
printLayout(
|
||||
['fumadocs-ui/layouts/docs', '@/layouts/docs'],
|
||||
['fumadocs-ui/layouts/docs/page', '@/layouts/docs/page'],
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Start from default layout',
|
||||
value: {
|
||||
target: [`${registry}/layouts/docs`],
|
||||
replace: [
|
||||
['fumadocs-ui/layouts/docs', '@/components/layout/docs'],
|
||||
['fumadocs-ui/layouts/docs/page', '@/components/layout/docs/page'],
|
||||
],
|
||||
},
|
||||
hint: 'useful for adjusting small details.',
|
||||
},
|
||||
{
|
||||
label: 'Start from Notebook layout',
|
||||
value: {
|
||||
target: [`${registry}/layouts/notebook`],
|
||||
replace: [
|
||||
['fumadocs-ui/layouts/notebook', '@/components/layout/notebook'],
|
||||
['fumadocs-ui/layouts/notebook/page', '@/components/layout/notebook/page'],
|
||||
],
|
||||
},
|
||||
hint: 'useful for adjusting small details.',
|
||||
},
|
||||
{
|
||||
label: 'Start from Flux layout',
|
||||
value: {
|
||||
target: [`${registry}/layouts/flux`],
|
||||
replace: [
|
||||
['fumadocs-ui/layouts/flux', '@/components/layout/flux'],
|
||||
['fumadocs-ui/layouts/flux/page', '@/components/layout/flux/page'],
|
||||
],
|
||||
},
|
||||
hint: 'useful for adjusting small details.',
|
||||
},
|
||||
...info.unlistedIndexes.flatMap((index) => {
|
||||
const prefix = `slots/${selected.id}`;
|
||||
if (!index.name.startsWith(prefix)) return [];
|
||||
let name = index.name.slice(prefix.length + 1);
|
||||
|
||||
if (name.startsWith('page/')) {
|
||||
name = name.slice('page/'.length);
|
||||
|
||||
return {
|
||||
label: `Page: ${name}`,
|
||||
hint: "only replace a part of layout's page, useful for adjusting details",
|
||||
value: {
|
||||
id: index.name,
|
||||
target: [`${registry}/${index.name}`],
|
||||
print() {
|
||||
printSlot({
|
||||
at: `@/layouts/${selected.id}/page/slots/${name}`,
|
||||
layoutId: selected.id,
|
||||
name,
|
||||
isPage: true,
|
||||
});
|
||||
},
|
||||
} as TargetInfo,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: `Layout: ${name}`,
|
||||
hint: 'only replace a part of layout, useful for adjusting details',
|
||||
value: {
|
||||
id: index.name,
|
||||
target: [`${registry}/${index.name}`],
|
||||
print() {
|
||||
printSlot({
|
||||
at: `@/layouts/${selected.id}/slots/${name}`,
|
||||
layoutId: selected.id,
|
||||
name,
|
||||
isPage: false,
|
||||
});
|
||||
},
|
||||
} as TargetInfo,
|
||||
};
|
||||
}),
|
||||
],
|
||||
});
|
||||
},
|
||||
@@ -105,20 +172,209 @@ export async function customise(client: RegistryClient) {
|
||||
|
||||
const target = result.target as TargetInfo;
|
||||
await install(target.target, installer);
|
||||
printNext(...target.replace);
|
||||
target.print?.();
|
||||
|
||||
outro(picocolors.bold('Have fun!'));
|
||||
}
|
||||
|
||||
function printNext(...maps: [from: string, to: string][]) {
|
||||
function printLayout(...maps: [from: string, to: string][]) {
|
||||
intro(picocolors.bold('What is Next?'));
|
||||
|
||||
log.info(
|
||||
[
|
||||
'You can check the installed components in `components`.',
|
||||
'You can check the installed layouts in `layouts` folder.',
|
||||
picocolors.dim('---'),
|
||||
'Open your `layout.tsx` files, replace the imports of components:',
|
||||
...maps.map(([from, to]) => picocolors.greenBright(`"${from}" -> "${to}"`)),
|
||||
].join('\n'),
|
||||
);
|
||||
}
|
||||
|
||||
function printSlot({ at, layoutId, name, isPage }: SlotPrintInfo) {
|
||||
intro(picocolors.bold('What is Next?'));
|
||||
|
||||
log.info(`You can check the installed layout slot in "${at}".`);
|
||||
|
||||
const code = getSlotCode({ at, layoutId, name, isPage });
|
||||
|
||||
if (code) {
|
||||
if (isPage) {
|
||||
log.info(
|
||||
`${picocolors.bold('At your <DocsPage /> component, update your "slots" prop:')}\n\n${code}`,
|
||||
);
|
||||
} else {
|
||||
log.info(
|
||||
`${picocolors.bold('At your <DocsLayout /> component, update your "slots" prop:')}\n\n${code}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSlotCode({ at, layoutId, name, isPage }: SlotPrintInfo): string | undefined {
|
||||
if (isPage) {
|
||||
switch (name) {
|
||||
case 'toc':
|
||||
if (layoutId === 'flux') {
|
||||
return `import { TOCProvider, TOC } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsPage
|
||||
slots={{
|
||||
toc: {
|
||||
provider: TOCProvider,
|
||||
main: TOC,
|
||||
},
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsPage>
|
||||
);`;
|
||||
}
|
||||
|
||||
return `import { TOCProvider, TOC, TOCPopover } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsPage
|
||||
slots={{
|
||||
toc: {
|
||||
provider: TOCProvider,
|
||||
main: TOC,
|
||||
popover: TOCPopover,
|
||||
},
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsPage>
|
||||
);`;
|
||||
case 'container': {
|
||||
return `import { Container } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsPage
|
||||
slots={{
|
||||
container: Container,
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsPage>
|
||||
);`;
|
||||
}
|
||||
case 'footer': {
|
||||
return `import { Footer } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsPage
|
||||
slots={{
|
||||
footer: Footer,
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsPage>
|
||||
);`;
|
||||
}
|
||||
case 'breadcrumb': {
|
||||
return `import { Breadcrumb } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsPage
|
||||
slots={{
|
||||
breadcrumb: Breadcrumb,
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsPage>
|
||||
);`;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case 'sidebar': {
|
||||
if (layoutId === 'notebook') {
|
||||
return `import {
|
||||
SidebarProvider,
|
||||
Sidebar,
|
||||
SidebarTrigger,
|
||||
SidebarCollapseTrigger,
|
||||
useSidebar,
|
||||
} from '${at}';
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
slots={{
|
||||
sidebar: {
|
||||
provider: SidebarProvider,
|
||||
root: Sidebar,
|
||||
trigger: SidebarTrigger,
|
||||
collapseTrigger: SidebarCollapseTrigger,
|
||||
useSidebar: useSidebar,
|
||||
},
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsLayout>
|
||||
);`;
|
||||
}
|
||||
|
||||
return `import { SidebarProvider, Sidebar, SidebarTrigger, useSidebar } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
slots={{
|
||||
sidebar: {
|
||||
provider: SidebarProvider,
|
||||
root: Sidebar,
|
||||
trigger: SidebarTrigger,
|
||||
useSidebar: useSidebar,
|
||||
},
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsLayout>
|
||||
);`;
|
||||
}
|
||||
case 'container': {
|
||||
return `import { Container } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
slots={{
|
||||
container: Container,
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsLayout>
|
||||
);`;
|
||||
}
|
||||
case 'header': {
|
||||
return `import { Header } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
slots={{
|
||||
header: Header,
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsLayout>
|
||||
);`;
|
||||
}
|
||||
case 'tab-dropdown': {
|
||||
return `import { TabDropdown } from '${at}';
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
slots={{
|
||||
tabDropdown: TabDropdown,
|
||||
}}
|
||||
>
|
||||
...
|
||||
</DocsLayout>
|
||||
);`;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ export const registryInfoSchema = z.object({
|
||||
*/
|
||||
env: z.record(z.string(), z.unknown()).optional(),
|
||||
indexes: z.array(indexSchema).default([]),
|
||||
unlistedIndexes: z.array(indexSchema).default([]),
|
||||
|
||||
registries: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user