@fumadocs/cli 1.7.5 → 1.7.6
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/{ai-BoUGzPvv.js → ai-CvrtLYh1.js} +8 -6
- package/dist/ai-CvrtLYh1.js.map +1 -0
- package/dist/all-bAINnjNQ.js +26 -0
- package/dist/{all-DgAfTffR.js.map → all-bAINnjNQ.js.map} +1 -1
- package/dist/codemod/index.d.ts +1 -1
- package/dist/codemod/index.js +1 -2
- package/dist/{codemod-auol-p4R.js → codemod-Wc9nRqrw.js} +60 -39
- package/dist/codemod-Wc9nRqrw.js.map +1 -0
- package/dist/{config-Bw0kmF01.js → config-BpiQyqBs.js} +13 -8
- package/dist/config-BpiQyqBs.js.map +1 -0
- package/dist/{config-Bfg4yG8S.d.ts → config-Z-4b_Y_C.d.ts} +1 -3
- package/dist/config-Z-4b_Y_C.d.ts.map +1 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -2
- package/dist/{docs-qRGwd9Yp.js → docs---dy-BSm.js} +286 -32
- package/dist/docs---dy-BSm.js.map +1 -0
- package/dist/{epub-DbOh5Q2D.js → epub-BKvZ4ppz.js} +12 -13
- package/dist/epub-BKvZ4ppz.js.map +1 -0
- package/dist/features/ai.d.ts +2 -3
- package/dist/features/ai.d.ts.map +1 -1
- package/dist/features/ai.js +1 -2
- package/dist/features/all.d.ts +1 -3
- package/dist/features/all.d.ts.map +1 -1
- package/dist/features/all.js +1 -2
- package/dist/features/docs/index.d.ts +2 -4
- package/dist/features/docs/index.d.ts.map +1 -1
- package/dist/features/docs/index.js +1 -2
- package/dist/features/epub.d.ts +1 -3
- package/dist/features/epub.d.ts.map +1 -1
- package/dist/features/epub.js +1 -2
- package/dist/features/feedback.d.ts +1 -3
- package/dist/features/feedback.d.ts.map +1 -1
- package/dist/features/feedback.js +1 -2
- package/dist/features/index.d.ts +1 -1
- package/dist/features/index.js +1 -2
- package/dist/features/lint.d.ts +1 -3
- package/dist/features/lint.d.ts.map +1 -1
- package/dist/features/lint.js +1 -2
- package/dist/features/llms.d.ts +6 -7
- package/dist/features/llms.d.ts.map +1 -1
- package/dist/features/llms.js +1 -2
- package/dist/features/mcp.d.ts +5 -7
- package/dist/features/mcp.d.ts.map +1 -1
- package/dist/features/mcp.js +1 -2
- package/dist/features/og.d.ts +2 -3
- package/dist/features/og.d.ts.map +1 -1
- package/dist/features/og.js +1 -2
- package/dist/features/search.d.ts +10 -10
- package/dist/features/search.d.ts.map +1 -1
- package/dist/features/search.js +1 -2
- package/dist/features/utils.d.ts +2 -2
- package/dist/features/utils.js +1 -2
- package/dist/features/webmcp.d.ts +2 -3
- package/dist/features/webmcp.d.ts.map +1 -1
- package/dist/features/webmcp.js +1 -2
- package/dist/{features-Do6x9Tsz.js → features-C_FG2WD0.js} +5 -6
- package/dist/{features-Do6x9Tsz.js.map → features-C_FG2WD0.js.map} +1 -1
- package/dist/{feedback-DpXYMKtX.js → feedback-7u1-eklc.js} +6 -7
- package/dist/feedback-7u1-eklc.js.map +1 -0
- package/dist/{fs-D6J9dOEx.js → fs-Dqr1VJSD.js} +2 -3
- package/dist/{fs-D6J9dOEx.js.map → fs-Dqr1VJSD.js.map} +1 -1
- package/dist/{index-Cs7hw2un.d.ts → index-CAIqrY1S.d.ts} +3 -5
- package/dist/index-CAIqrY1S.d.ts.map +1 -0
- package/dist/{index-DI66DoV8.d.ts → index-DCJbgkgf.d.ts} +4 -8
- package/dist/index-DCJbgkgf.d.ts.map +1 -0
- package/dist/{index-Ck0EPdgu.d.ts → index-w9FHKPPs.d.ts} +6 -16
- package/dist/index-w9FHKPPs.d.ts.map +1 -0
- package/dist/index.js +22 -31
- package/dist/index.js.map +1 -1
- package/dist/{installer-C4662Woc.js → installer-CEOV0fBv.js} +3 -5
- package/dist/{installer-C4662Woc.js.map → installer-CEOV0fBv.js.map} +1 -1
- package/dist/{lint-zkXUULgO.js → lint-AC2ClOfa.js} +3 -4
- package/dist/{lint-zkXUULgO.js.map → lint-AC2ClOfa.js.map} +1 -1
- package/dist/{llms-DQzAst6R.js → llms-Cge1uefC.js} +11 -12
- package/dist/llms-Cge1uefC.js.map +1 -0
- package/dist/{mcp-BpFZ_fdV.js → mcp-BVpVFl6G.js} +7 -9
- package/dist/mcp-BVpVFl6G.js.map +1 -0
- package/dist/{og-CY4RS9Un.js → og-DuPd9kNq.js} +10 -11
- package/dist/og-DuPd9kNq.js.map +1 -0
- package/dist/project/index.d.ts +2 -2
- package/dist/project/index.js +1 -2
- package/dist/{project-DwRVTm4p.js → project-D655VGTX.js} +19 -10
- package/dist/project-D655VGTX.js.map +1 -0
- package/dist/registry/installer.d.ts +1 -3
- package/dist/registry/installer.d.ts.map +1 -1
- package/dist/registry/installer.js +1 -2
- package/dist/{search-BCHEW0-K.js → search-B44II4bM.js} +39 -30
- package/dist/search-B44II4bM.js.map +1 -0
- package/dist/{utils-DbMe7h6c.js → utils-Btt-BJ53.js} +6 -12
- package/dist/utils-Btt-BJ53.js.map +1 -0
- package/dist/{utils-CxJ7kw39.d.ts → utils-DlGz_4va.d.ts} +6 -12
- package/dist/utils-DlGz_4va.d.ts.map +1 -0
- package/dist/{webmcp-I9L7xzmw.js → webmcp-lY3_RsR3.js} +6 -8
- package/dist/webmcp-lY3_RsR3.js.map +1 -0
- package/package.json +7 -7
- package/dist/ai-BoUGzPvv.js.map +0 -1
- package/dist/all-DgAfTffR.js +0 -27
- package/dist/codemod-auol-p4R.js.map +0 -1
- package/dist/config-Bfg4yG8S.d.ts.map +0 -1
- package/dist/config-Bw0kmF01.js.map +0 -1
- package/dist/docs-qRGwd9Yp.js.map +0 -1
- package/dist/epub-DbOh5Q2D.js.map +0 -1
- package/dist/feedback-DpXYMKtX.js.map +0 -1
- package/dist/index-Ck0EPdgu.d.ts.map +0 -1
- package/dist/index-Cs7hw2un.d.ts.map +0 -1
- package/dist/index-DI66DoV8.d.ts.map +0 -1
- package/dist/llms-DQzAst6R.js.map +0 -1
- package/dist/mcp-BpFZ_fdV.js.map +0 -1
- package/dist/og-CY4RS9Un.js.map +0 -1
- package/dist/project-DwRVTm4p.js.map +0 -1
- package/dist/search-BCHEW0-K.js.map +0 -1
- package/dist/utils-CxJ7kw39.d.ts.map +0 -1
- package/dist/utils-DbMe7h6c.js.map +0 -1
- package/dist/webmcp-I9L7xzmw.js.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { b as findJsxElement, d as wrapNextConfig, l as addVitePlugin, m as addJsxAttribute, p as addImport } from "./codemod-
|
|
2
|
-
import { t as exists } from "./fs-
|
|
3
|
-
import { a as
|
|
1
|
+
import { b as findJsxElement, d as wrapNextConfig, l as addVitePlugin, m as addJsxAttribute, p as addImport } from "./codemod-Wc9nRqrw.js";
|
|
2
|
+
import { t as exists } from "./fs-Dqr1VJSD.js";
|
|
3
|
+
import { a as registerReactRouterRoutes } from "./utils-Btt-BJ53.js";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
|
|
6
|
+
// src/project/route.ts
|
|
7
|
+
const restParam = (name, catchAll, _optional, suffix) => catchAll ? `[...${name}]${suffix ? `/${suffix}` : ""}` : `[${name}]`;
|
|
7
8
|
const conventions = {
|
|
8
9
|
next: {
|
|
9
10
|
locale: () => "[lang]",
|
|
@@ -25,9 +26,15 @@ const conventions = {
|
|
|
25
26
|
},
|
|
26
27
|
waku: {
|
|
27
28
|
locale: () => "[lang]",
|
|
28
|
-
param:
|
|
29
|
+
param: restParam,
|
|
29
30
|
file: (segments, ext) => `pages/_api/${segments.join("/")}.${ext}`,
|
|
30
31
|
path: (segments) => `/${segments.join("/")}`
|
|
32
|
+
},
|
|
33
|
+
astro: {
|
|
34
|
+
locale: (optional) => optional ? "[...lang]" : "[lang]",
|
|
35
|
+
param: restParam,
|
|
36
|
+
file: (segments) => `pages/${segments.join("/")}.ts`,
|
|
37
|
+
path: (segments) => `/${segments.join("/")}`
|
|
31
38
|
}
|
|
32
39
|
};
|
|
33
40
|
/** the locale segment of route files, with a trailing slash */
|
|
@@ -63,6 +70,12 @@ function routeModule(framework, route, mod) {
|
|
|
63
70
|
const { imports, body, request, methods = ["GET"], revalidate, static: isStatic } = mod;
|
|
64
71
|
const arg = request ? "request: Request" : "";
|
|
65
72
|
const head = `${imports.trim()}\n\n`;
|
|
73
|
+
if (framework === "astro") {
|
|
74
|
+
const out = [];
|
|
75
|
+
if (isStatic) out.push("export const prerender = true;\n");
|
|
76
|
+
for (const method of methods) out.push(`export const ${method}: APIRoute = async (${request ? "{ request }" : ""}) => ${body};\n`);
|
|
77
|
+
return `import type { APIRoute } from 'astro';\n${head}${out.join("\n")}`;
|
|
78
|
+
}
|
|
66
79
|
if (framework === "react-router") {
|
|
67
80
|
const out = [];
|
|
68
81
|
out.push(handler("loader", "Route.LoaderArgs"));
|
|
@@ -102,10 +115,49 @@ ${out.join("\n")}
|
|
|
102
115
|
`;
|
|
103
116
|
}
|
|
104
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* `getStaticPaths()` of an Astro route per docs page, also prerendered with `output: 'server'`.
|
|
120
|
+
* With i18n, the route is under the locale segment, and reads `i18n` of `@/lib/i18n` for an optional locale.
|
|
121
|
+
*/
|
|
122
|
+
function astroPagePaths(i18n) {
|
|
123
|
+
const prerender = "export const prerender = true;\n\n";
|
|
124
|
+
if (!i18n) return `${prerender}export const getStaticPaths = (() => {
|
|
125
|
+
return source.getPages().map((page) => ({
|
|
126
|
+
params: { slug: page.slugs.join('/') || undefined },
|
|
127
|
+
props: { page },
|
|
128
|
+
}));
|
|
129
|
+
}) satisfies GetStaticPaths;`;
|
|
130
|
+
return `${prerender}export const getStaticPaths = (() => {
|
|
131
|
+
const paths = [];
|
|
132
|
+
for (const { language, pages } of source.getLanguages()) {
|
|
133
|
+
for (const page of pages) {
|
|
134
|
+
paths.push({
|
|
135
|
+
params: {
|
|
136
|
+
${i18n.optionalLocale ? "// the default language has no prefix\n lang: language === i18n.defaultLanguage ? undefined : language" : "lang: language"},
|
|
137
|
+
slug: page.slugs.join('/') || undefined,
|
|
138
|
+
},
|
|
139
|
+
props: { lang: language, page },
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return paths;
|
|
144
|
+
}) satisfies GetStaticPaths;`;
|
|
145
|
+
}
|
|
146
|
+
/** an Astro endpoint per docs page, `body` is the response of `page` */
|
|
147
|
+
const astroPageRoute = (i18n, imports, body) => `import type { APIRoute, GetStaticPaths, InferGetStaticPropsType } from 'astro';
|
|
148
|
+
${imports.trim()}
|
|
149
|
+
${i18n?.optionalLocale ? "import { i18n } from '@/lib/i18n';\n" : ""}
|
|
150
|
+
${astroPagePaths(i18n)}
|
|
151
|
+
|
|
152
|
+
export const GET: APIRoute<InferGetStaticPropsType<typeof getStaticPaths>> = async ({
|
|
153
|
+
props: { page },
|
|
154
|
+
}) => {
|
|
155
|
+
return ${body};
|
|
156
|
+
};
|
|
157
|
+
`;
|
|
105
158
|
/** import of the generated types of a React Router route module */
|
|
106
159
|
const reactRouterTypes = (route) => `import type { Route } from './+types/${route.file.split("/").pop().replace(/\.tsx?$/, "")}';`;
|
|
107
|
-
|
|
108
|
-
//#region src/features/docs/templates.ts
|
|
160
|
+
// src/features/docs/templates.ts
|
|
109
161
|
const wrapper = (children) => `<div className="flex flex-col min-h-screen">${children}</div>`;
|
|
110
162
|
const i18nConfig = (optionalLocale) => `import { defineI18n } from 'fumadocs-core/i18n';
|
|
111
163
|
|
|
@@ -131,15 +183,16 @@ export const source = loader({
|
|
|
131
183
|
`;
|
|
132
184
|
const layoutShared = (i18n) => i18n ? `import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
|
133
185
|
import { uiTranslations } from 'fumadocs-ui/i18n';
|
|
186
|
+
import { zhTW } from '@fumadocs/language/zh-tw';
|
|
134
187
|
import { i18n } from '@/lib/i18n';
|
|
135
188
|
|
|
136
|
-
export const translations = i18n.translations().extend(uiTranslations());
|
|
189
|
+
export const translations = i18n.translations().extend(uiTranslations()).preset('cn', zhTW());
|
|
137
190
|
|
|
138
191
|
export function baseOptions(locale: string = i18n.defaultLanguage): BaseLayoutProps {
|
|
139
192
|
return {
|
|
140
193
|
nav: {
|
|
141
194
|
title: 'My App',
|
|
142
|
-
url:
|
|
195
|
+
url: ${i18n.optionalLocale ? "locale === i18n.defaultLanguage ? '/' : `/${locale}`" : "`/${locale}`"},
|
|
143
196
|
},
|
|
144
197
|
};
|
|
145
198
|
}
|
|
@@ -206,7 +259,7 @@ function shared(input, async) {
|
|
|
206
259
|
return {
|
|
207
260
|
"lib/shared.ts": sharedConstants,
|
|
208
261
|
"lib/source.ts": source(async, i18n),
|
|
209
|
-
"lib/layout.shared.tsx": layoutShared(i18n),
|
|
262
|
+
"lib/layout.shared.tsx": layoutShared(input.i18n),
|
|
210
263
|
"components/mdx.tsx": mdx,
|
|
211
264
|
...input.static ? { "components/search.tsx": search(i18n) } : {},
|
|
212
265
|
...input.i18n ? { "lib/i18n.ts": i18nConfig(input.i18n.optionalLocale) } : {}
|
|
@@ -217,8 +270,8 @@ const searchImport = (input) => input.static ? `import SearchDialog from '@/comp
|
|
|
217
270
|
const providerImports = (input) => `import { RootProvider } from '${input.provider}';
|
|
218
271
|
${searchImport(input)}${input.i18n ? `import { i18nProvider } from 'fumadocs-ui/i18n';
|
|
219
272
|
import { translations } from '@/lib/layout.shared';
|
|
220
|
-
import { i18n } from '@/lib/i18n';
|
|
221
273
|
` : ""}`;
|
|
274
|
+
const i18nImport = "import { i18n } from '@/lib/i18n';\n";
|
|
222
275
|
const providerProps = (input) => `${input.i18n ? " i18n={i18nProvider(translations, lang)}" : ""}${input.providerProps}`;
|
|
223
276
|
/** the docs page for React Router & TanStack Start, where MDX content is loaded lazily on client */
|
|
224
277
|
const clientContent = `function Content({ path }: { path: string }) {
|
|
@@ -341,7 +394,7 @@ export async function generateMetadata(props: PageProps<'${route}/docs/[[...slug
|
|
|
341
394
|
return {
|
|
342
395
|
...shared(input, true),
|
|
343
396
|
"routes/docs/layout.tsx": `import { Outlet${i18n ? ", useParams" : ""} } from 'react-router';
|
|
344
|
-
${providerImports(input)}
|
|
397
|
+
${providerImports(input)}${i18n ? i18nImport : ""}
|
|
345
398
|
export default function Layout() {
|
|
346
399
|
${i18n ? " const { lang = i18n.defaultLanguage } = useParams();\n\n" : ""} return (
|
|
347
400
|
<RootProvider${providerProps(input)}>
|
|
@@ -401,7 +454,7 @@ export async function loader(${input.static ? "" : "{ request }: Route.LoaderArg
|
|
|
401
454
|
return {
|
|
402
455
|
...shared(input, true),
|
|
403
456
|
"routes/_docs.tsx": `import { createFileRoute, Outlet${i18n ? ", useParams" : ""} } from '@tanstack/react-router';
|
|
404
|
-
${providerImports(input)}
|
|
457
|
+
${providerImports(input)}${i18n ? i18nImport : ""}
|
|
405
458
|
export const Route = createFileRoute('/_docs')({
|
|
406
459
|
component: Layout,
|
|
407
460
|
});
|
|
@@ -552,7 +605,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|
|
552
605
|
"components/provider.tsx": `'use client';
|
|
553
606
|
import type { ReactNode } from 'react';
|
|
554
607
|
import { useRouter } from 'waku/router/client';
|
|
555
|
-
${providerImports(input)}
|
|
608
|
+
${providerImports(input)}${i18nImport}
|
|
556
609
|
export function Provider({ children }: { children: ReactNode }) {
|
|
557
610
|
const router = useRouter();
|
|
558
611
|
const lang = router.path.split('/')[1] || i18n.defaultLanguage;
|
|
@@ -579,8 +632,160 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|
|
579
632
|
},
|
|
580
633
|
"pages/_api/api/search.ts": searchRoute.waku
|
|
581
634
|
};
|
|
635
|
+
},
|
|
636
|
+
astro: (input) => {
|
|
637
|
+
const i18n = input.i18n !== null;
|
|
638
|
+
return {
|
|
639
|
+
...shared(input, false),
|
|
640
|
+
"lib/source.ts": `import { getCollection } from 'astro:content';
|
|
641
|
+
import { loader } from 'fumadocs-core/source';
|
|
642
|
+
import { toFumadocsSource } from '@fumadocs/astro/source';
|
|
643
|
+
import { docsRoute } from './shared';
|
|
644
|
+
${i18n ? i18nImport : ""}
|
|
645
|
+
export const source = loader({
|
|
646
|
+
source: toFumadocsSource(await getCollection('docs'), await getCollection('meta')),
|
|
647
|
+
baseUrl: docsRoute,${i18n ? "\n i18n," : ""}
|
|
648
|
+
});
|
|
649
|
+
`,
|
|
650
|
+
"components/mdx.tsx": `import defaultMdxComponents from 'fumadocs-ui/mdx';
|
|
651
|
+
${i18n ? "import { DynamicLink } from 'fumadocs-core/dynamic-link';\n" : ""}import { DocsBody } from 'fumadocs-ui/layouts/docs/page';
|
|
652
|
+
import type { MDXComponents } from 'mdx/types';
|
|
653
|
+
${i18n ? "import { ContentProvider, type ProviderProps } from '@/components/provider';\n" : ""}
|
|
654
|
+
export function getMDXComponents(components?: MDXComponents) {
|
|
655
|
+
return {
|
|
656
|
+
...defaultMdxComponents,${i18n ? "\n // links with the locale, like `/[lang]/docs`\n DynamicLink," : ""}
|
|
657
|
+
...components,
|
|
658
|
+
} satisfies MDXComponents;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
${i18n ? `// for the Fumadocs integration, \`wrapper\` receives the props of \`<Content />\`
|
|
662
|
+
export default getMDXComponents({
|
|
663
|
+
wrapper: ({ children, ...props }: ProviderProps) => (
|
|
664
|
+
<ContentProvider {...props}>
|
|
665
|
+
<DocsBody>{children}</DocsBody>
|
|
666
|
+
</ContentProvider>
|
|
667
|
+
),
|
|
668
|
+
});` : `// for the Fumadocs integration, \`wrapper\` gives the content its prose styles
|
|
669
|
+
export default getMDXComponents({ wrapper: DocsBody });`}
|
|
670
|
+
`,
|
|
671
|
+
"components/provider.tsx": `import { navigate } from 'astro:transitions/client';
|
|
672
|
+
import type { ReactNode } from 'react';
|
|
673
|
+
${providerImports(input)}
|
|
674
|
+
export interface ProviderProps {${i18n ? "\n lang: string;" : ""}
|
|
675
|
+
/** \`Astro.url.pathname\` */
|
|
676
|
+
pathname: string;
|
|
677
|
+
/** \`Astro.params\` */
|
|
678
|
+
params: Record<string, string | undefined>;
|
|
679
|
+
children?: ReactNode;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export function Provider({ ${i18n ? "lang, " : ""}pathname, params, children }: ProviderProps) {
|
|
683
|
+
return (
|
|
684
|
+
<RootProvider pathname={pathname} params={params} navigate={navigate}${providerProps(input)}>
|
|
685
|
+
{children}
|
|
686
|
+
</RootProvider>
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
${i18n ? `
|
|
690
|
+
/**
|
|
691
|
+
* For the MDX content, which is an island of its own.
|
|
692
|
+
*/
|
|
693
|
+
export function ContentProvider({ lang, pathname, params, children }: ProviderProps) {
|
|
694
|
+
return (
|
|
695
|
+
<RootProvider
|
|
696
|
+
pathname={pathname}
|
|
697
|
+
params={params}
|
|
698
|
+
navigate={navigate}
|
|
699
|
+
i18n={i18nProvider(translations, lang)}
|
|
700
|
+
theme={{ enabled: false }}
|
|
701
|
+
search={{ enabled: false }}
|
|
702
|
+
>
|
|
703
|
+
{children}
|
|
704
|
+
</RootProvider>
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
` : ""}`,
|
|
708
|
+
"components/docs.tsx": `import { type SerializedPageTree, useFumadocsLoader } from 'fumadocs-core/source/client';
|
|
709
|
+
import type { TOCItemType } from 'fumadocs-core/toc';
|
|
710
|
+
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
|
711
|
+
import { DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
|
|
712
|
+
import { Provider, type ProviderProps } from '@/components/provider';
|
|
713
|
+
import { baseOptions } from '@/lib/layout.shared';
|
|
714
|
+
|
|
715
|
+
export interface DocsProps extends ProviderProps {
|
|
716
|
+
tree: SerializedPageTree;
|
|
717
|
+
title: string;
|
|
718
|
+
description?: string;
|
|
719
|
+
toc: TOCItemType[];
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export function Docs({ tree, title, description, toc, children, ...props }: DocsProps) {
|
|
723
|
+
const data = useFumadocsLoader({ tree });
|
|
724
|
+
|
|
725
|
+
return (
|
|
726
|
+
<Provider {...props}>
|
|
727
|
+
<DocsLayout {...baseOptions(${i18n ? "props.lang" : ""})} tree={data.tree}>
|
|
728
|
+
<DocsPage toc={toc}>
|
|
729
|
+
<DocsTitle>{title}</DocsTitle>
|
|
730
|
+
<DocsDescription>{description}</DocsDescription>
|
|
731
|
+
{children}
|
|
732
|
+
</DocsPage>
|
|
733
|
+
</DocsLayout>
|
|
734
|
+
</Provider>
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
`,
|
|
738
|
+
[`pages/${localeSegment("astro", input.i18n)}docs/[...slug].astro`]: `---
|
|
739
|
+
import type { GetStaticPaths, InferGetStaticPropsType } from 'astro';
|
|
740
|
+
import Layout from '@/layouts/Layout.astro';
|
|
741
|
+
import { Docs } from '@/components/docs';
|
|
742
|
+
${input.i18n?.optionalLocale ? i18nImport : ""}import { source } from '@/lib/source';
|
|
743
|
+
|
|
744
|
+
${astroPagePaths(input.i18n)}
|
|
745
|
+
|
|
746
|
+
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
|
|
747
|
+
|
|
748
|
+
const { ${i18n ? "lang, " : ""}page } = Astro.props;
|
|
749
|
+
const { body: Content, toc } = await page.data.load();
|
|
750
|
+
const pageTree = await source.serializePageTree(source.getPageTree(${i18n ? "lang" : ""}));
|
|
751
|
+
${i18n ? "const provider = { lang, pathname: Astro.url.pathname, params: Astro.params };\n" : ""}---
|
|
752
|
+
|
|
753
|
+
<Layout
|
|
754
|
+
title={page.data.title}
|
|
755
|
+
description={page.data.description}${i18n ? "\n lang={lang}" : ""}
|
|
756
|
+
>
|
|
757
|
+
<Docs
|
|
758
|
+
client:load
|
|
759
|
+
transition:persist="docs"
|
|
760
|
+
${i18n ? "{...provider}" : "pathname={Astro.url.pathname}\n params={Astro.params}"}
|
|
761
|
+
tree={pageTree}
|
|
762
|
+
title={page.data.title}
|
|
763
|
+
description={page.data.description}
|
|
764
|
+
toc={toc}
|
|
765
|
+
>
|
|
766
|
+
<Content${i18n ? " {...provider}" : ""} />
|
|
767
|
+
</Docs>
|
|
768
|
+
</Layout>
|
|
769
|
+
`,
|
|
770
|
+
"pages/api/search.ts": `import type { APIRoute } from 'astro';
|
|
771
|
+
import { createFromSource } from 'fumadocs-core/search/server';
|
|
772
|
+
import { source } from '@/lib/source';
|
|
773
|
+
|
|
774
|
+
const server = createFromSource(source);
|
|
775
|
+
|
|
776
|
+
export const GET: APIRoute = ${input.static ? "() => server.staticGET()" : "({ request }) => server.GET(request)"};
|
|
777
|
+
`
|
|
778
|
+
};
|
|
582
779
|
}
|
|
583
780
|
};
|
|
781
|
+
const astroContentConfig = `import { defineCollection } from 'astro:content';
|
|
782
|
+
import { metaLoader, pageLoader } from '@fumadocs/astro/loaders';
|
|
783
|
+
|
|
784
|
+
export const collections = {
|
|
785
|
+
docs: defineCollection({ loader: pageLoader() }),
|
|
786
|
+
meta: defineCollection({ loader: metaLoader() }),
|
|
787
|
+
};
|
|
788
|
+
`;
|
|
584
789
|
const nextProxy = `import { createI18nMiddleware } from 'fumadocs-core/i18n/middleware';
|
|
585
790
|
import { i18n } from '@/lib/i18n';
|
|
586
791
|
|
|
@@ -611,14 +816,12 @@ description: 你的第一份文檔
|
|
|
611
816
|
|
|
612
817
|
歡迎來到文檔!你可以在 \`content/docs/index.cn.mdx\` 編輯這個頁面。
|
|
613
818
|
`;
|
|
614
|
-
|
|
615
|
-
//#region src/features/docs/index.ts
|
|
819
|
+
// src/features/docs/index.ts
|
|
616
820
|
const cssImports = (preset) => [`@import 'fumadocs-ui/css/${preset}.css';`, `@import 'fumadocs-ui/css/preset.css';`];
|
|
617
821
|
const docs = {
|
|
618
822
|
id: "docs",
|
|
619
823
|
title: "Docs",
|
|
620
824
|
description: "set up Fumadocs on your app, in a dedicated route group",
|
|
621
|
-
supports: reactOnly,
|
|
622
825
|
options: { i18n: {
|
|
623
826
|
message: "Enable internationalization?",
|
|
624
827
|
initialValue: false
|
|
@@ -628,18 +831,34 @@ const docs = {
|
|
|
628
831
|
},
|
|
629
832
|
async apply(ctx, options) {
|
|
630
833
|
const { project } = ctx;
|
|
631
|
-
const { baseDir, packageJson, config } = project;
|
|
632
|
-
const framework = reactFramework(project);
|
|
834
|
+
const { baseDir, packageJson, config, framework } = project;
|
|
633
835
|
const i18n = project.i18n ?? (options.i18n ? { optionalLocale: framework !== "waku" } : null);
|
|
634
836
|
const providerProps = [];
|
|
635
837
|
if (project.static) providerProps.push("search={{ SearchDialog }}");
|
|
636
838
|
if ("next-themes" in (packageJson.dependencies ?? {})) providerProps.push("theme={{ enabled: false }}");
|
|
637
839
|
ctx.addDependencies({
|
|
638
840
|
"fumadocs-core": null,
|
|
639
|
-
"fumadocs-mdx": null,
|
|
640
841
|
"fumadocs-ui": config.uiLibrary === "base-ui" ? "npm:@fumadocs/base-ui" : null
|
|
641
842
|
});
|
|
642
|
-
ctx.addDependencies({ "@
|
|
843
|
+
if (i18n) ctx.addDependencies({ "@fumadocs/language": null });
|
|
844
|
+
if (framework === "astro") {
|
|
845
|
+
ctx.addDependencies({
|
|
846
|
+
"@fumadocs/astro": null,
|
|
847
|
+
"@astrojs/react": null,
|
|
848
|
+
react: null,
|
|
849
|
+
"react-dom": null
|
|
850
|
+
});
|
|
851
|
+
ctx.addDependencies({
|
|
852
|
+
"@types/mdx": null,
|
|
853
|
+
"@types/react": null,
|
|
854
|
+
"@types/react-dom": null,
|
|
855
|
+
tailwindcss: null,
|
|
856
|
+
"@tailwindcss/vite": null
|
|
857
|
+
}, true);
|
|
858
|
+
} else {
|
|
859
|
+
ctx.addDependencies({ "fumadocs-mdx": null });
|
|
860
|
+
ctx.addDependencies({ "@types/mdx": null }, true);
|
|
861
|
+
}
|
|
643
862
|
const files = templates[framework]({
|
|
644
863
|
static: project.static,
|
|
645
864
|
i18n,
|
|
@@ -649,15 +868,21 @@ const docs = {
|
|
|
649
868
|
for (const [file, content] of Object.entries(files)) await ctx.write(path.posix.join(baseDir, file), content);
|
|
650
869
|
await ctx.write("content/docs/index.mdx", sampleContent);
|
|
651
870
|
if (i18n) await ctx.write("content/docs/index.cn.mdx", sampleContentCn);
|
|
652
|
-
|
|
871
|
+
if (framework === "astro") await configureCollections(ctx);
|
|
872
|
+
else await ctx.append(".gitignore", [".source"]);
|
|
653
873
|
if (i18n && framework === "next") await configureProxy(ctx);
|
|
654
874
|
await configureBundler(ctx);
|
|
655
875
|
await configureCss(ctx);
|
|
656
|
-
await suppressHydrationWarning(ctx);
|
|
876
|
+
if (framework !== "astro") await suppressHydrationWarning(ctx);
|
|
657
877
|
if (framework === "react-router") await configureRoutes(ctx, i18n);
|
|
658
878
|
if (!(await fs.readFile(path.join(project.cwd, "tsconfig.json"), "utf-8").catch(() => "")).includes("\"@/*\"")) ctx.note(`Generated files import from the \`@/*\` alias, add it to \`tsconfig.json\`:\n "paths": { "@/*": ["./${baseDir ? `${baseDir}/` : ""}*"] }`);
|
|
659
879
|
if (project.static && framework === "tanstack-start") ctx.note("In SPA mode, prerender the docs pages and search index, see https://fumadocs.dev/docs/manual-installation/tanstack-start.");
|
|
660
880
|
if (i18n && framework === "react-router") ctx.note("When prerendering, list the docs pages with `source.getPages().map((page) => page.url)` instead of globbing content files, see https://fumadocs.dev/docs/internationalization/react-router.");
|
|
881
|
+
if (framework === "astro") {
|
|
882
|
+
const layout = path.join(baseDir, project.info.rootFile);
|
|
883
|
+
if (!(await fs.readFile(path.join(project.cwd, layout), "utf-8").catch(() => "")).includes("Astro.props")) ctx.note(`Docs pages render in \`${layout}\` with \`title\` and \`description\` props, add them to its \`<head>\`.`);
|
|
884
|
+
if (i18n) ctx.note(`Docs pages pass the locale as \`lang\` to \`${layout}\`, set it on \`<html lang>\`.`);
|
|
885
|
+
}
|
|
661
886
|
if (i18n) ctx.note(`Languages are configured in \`lib/i18n.ts\`, translated content uses a locale suffix like \`index.cn.mdx\`.\nUI translations: https://fumadocs.dev/docs/internationalization#translations`);
|
|
662
887
|
ctx.note(`Start the dev server and open ${i18n && !i18n.optionalLocale ? "/en/docs" : "/docs"} to see your docs.`);
|
|
663
888
|
}
|
|
@@ -678,14 +903,44 @@ async function configureProxy(ctx) {
|
|
|
678
903
|
}
|
|
679
904
|
ctx.note(`A proxy already exists at ${existing}, handle the docs locale there. Fumadocs provides \`createI18nMiddleware\` from 'fumadocs-core/i18n/middleware', see https://fumadocs.dev/docs/internationalization/next.`);
|
|
680
905
|
}
|
|
906
|
+
function bundlerEdits({ framework, baseDir }) {
|
|
907
|
+
if (framework === "next") return [wrapNextConfig];
|
|
908
|
+
if (framework === "astro") return [(file) => addVitePlugin(file, {
|
|
909
|
+
name: "fumadocs",
|
|
910
|
+
from: "@fumadocs/astro",
|
|
911
|
+
call: `fumadocs({ components: './${path.posix.join(baseDir, "components/mdx.tsx")}' })`,
|
|
912
|
+
default: true
|
|
913
|
+
}, ["integrations"]), (file) => addVitePlugin(file, {
|
|
914
|
+
name: "tailwindcss",
|
|
915
|
+
from: "@tailwindcss/vite",
|
|
916
|
+
default: true
|
|
917
|
+
}, ["vite", "plugins"])];
|
|
918
|
+
return [(file) => addVitePlugin(file, {
|
|
919
|
+
name: "fumadocsMdx",
|
|
920
|
+
from: "fumadocs-mdx/vite"
|
|
921
|
+
}, framework === "waku" ? ["vite", "plugins"] : ["plugins"])];
|
|
922
|
+
}
|
|
681
923
|
async function configureBundler(ctx) {
|
|
682
924
|
const { framework, configFile } = ctx.project;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
688
|
-
|
|
925
|
+
let edited = false;
|
|
926
|
+
if (configFile) for (const edit of bundlerEdits(ctx.project)) {
|
|
927
|
+
edited = await ctx.source(configFile, (file) => {
|
|
928
|
+
if (!edit(file)) throw new Error(`cannot update ${configFile}`);
|
|
929
|
+
}).catch(() => false);
|
|
930
|
+
if (!edited) break;
|
|
931
|
+
}
|
|
932
|
+
if (edited) return;
|
|
933
|
+
if (framework === "next") ctx.note("Wrap your Next.js config with `createMDX()` from 'fumadocs-mdx/next', see https://fumadocs.dev/docs/mdx/next.");
|
|
934
|
+
else if (framework === "astro") ctx.note("Add the integrations of Fumadocs to your Astro config, see https://fumadocs.dev/docs/manual-installation/astro.");
|
|
935
|
+
else ctx.note("Add `fumadocsMdx()` from 'fumadocs-mdx/vite' to the Vite plugins of your config file.");
|
|
936
|
+
}
|
|
937
|
+
/** Astro content collections of the docs */
|
|
938
|
+
async function configureCollections(ctx) {
|
|
939
|
+
await ctx.write("content/docs/meta.json", "{\n \"pages\": [\"...\"]\n}\n");
|
|
940
|
+
const file = path.join(ctx.project.baseDir, "content.config.ts");
|
|
941
|
+
const content = await fs.readFile(path.join(ctx.project.cwd, file), "utf-8").catch(() => null);
|
|
942
|
+
if (content === null) await ctx.write(file, astroContentConfig);
|
|
943
|
+
else if (!content.includes("pageLoader(")) ctx.note(`A content config already exists at ${file}, add the collections of docs to it:\n${astroContentConfig}`);
|
|
689
944
|
}
|
|
690
945
|
/** resolve the global CSS file imported by the root file */
|
|
691
946
|
async function findCssEntry({ cwd, baseDir, info }) {
|
|
@@ -729,7 +984,6 @@ async function configureRoutes(ctx, i18n) {
|
|
|
729
984
|
});
|
|
730
985
|
});
|
|
731
986
|
}
|
|
732
|
-
|
|
733
|
-
export { routeModule as a, reactRouterTypes as i, findNextProxy as n, formatRoute as r, docs as t };
|
|
987
|
+
export { reactRouterTypes as a, formatRoute as i, findNextProxy as n, routeModule as o, astroPageRoute as r, docs as t };
|
|
734
988
|
|
|
735
|
-
//# sourceMappingURL=docs-
|
|
989
|
+
//# sourceMappingURL=docs---dy-BSm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs---dy-BSm.js","names":[],"sources":["../src/project/route.ts","../src/features/docs/templates.ts","../src/features/docs/index.ts"],"sourcesContent":["import type { Framework, I18nInfo } from '@/project';\n\nexport type RouteSegment =\n /** static path, can contain multiple segments like `/llms.mdx/docs` */\n | string\n | {\n param: string;\n catchAll?: boolean;\n /** the catch-all also matches zero segments */\n optional?: boolean;\n /** static file name after the catch-all like `content.md`, part of the parameter when the framework can't express it */\n suffix?: string;\n };\n\nexport interface RouteDescriptor {\n segments: RouteSegment[];\n /** under the locale parameter when the project has i18n */\n locale?: boolean;\n}\n\nexport interface FormattedRoute {\n /** route file, relative to the base dir */\n file: string;\n /** route string of the framework, e.g. for `RouteContext<'...'>`, `createFileRoute('...')`, `route('...')` or `ApiContext<'...'>` */\n path: string;\n /** path-to-regexp pattern, for the Next.js proxy matcher and rewrites */\n pattern: string;\n}\n\ninterface Convention {\n locale: (optional: boolean) => string;\n param: (name: string, catchAll: boolean, optional: boolean, suffix?: string) => string;\n file: (segments: string[], ext: string) => string;\n path: (segments: string[]) => string;\n}\n\nconst restParam: Convention['param'] = (name, catchAll, _optional, suffix) =>\n catchAll ? `[...${name}]${suffix ? `/${suffix}` : ''}` : `[${name}]`;\n\nconst conventions: Record<Framework, Convention> = {\n next: {\n locale: () => '[lang]',\n param: (name, catchAll, optional) =>\n catchAll ? (optional ? `[[...${name}]]` : `[...${name}]`) : `[${name}]`,\n file: (segments, ext) => `app/${segments.join('/')}/route.${ext}`,\n path: (segments) => `/${segments.join('/')}`,\n },\n 'react-router': {\n locale: (optional) => (optional ? ':lang?' : ':lang'),\n param: (name, catchAll) => (catchAll ? '*' : `:${name}`),\n // `routes.ts` maps paths to files, name files after the static segments like flat routes\n file: (segments, ext) =>\n `routes/${segments.filter((segment) => !/^[:*]/.test(segment)).join('.')}.${ext}`,\n path: (segments) => segments.join('/'),\n },\n 'tanstack-start': {\n locale: (optional) => (optional ? '{-$lang}' : '$lang'),\n param: (name, catchAll) => (catchAll ? '$' : `$${name}`),\n file: (segments, ext) => `routes/${segments.join('/').replaceAll('.', '[.]')}.${ext}`,\n path: (segments) => `/${segments.join('/')}`,\n },\n waku: {\n locale: () => '[lang]',\n param: restParam,\n file: (segments, ext) => `pages/_api/${segments.join('/')}.${ext}`,\n path: (segments) => `/${segments.join('/')}`,\n },\n astro: {\n // the rest parameter is `undefined` for the default locale\n locale: (optional) => (optional ? '[...lang]' : '[lang]'),\n param: restParam,\n // endpoints can't be `.tsx`\n file: (segments) => `pages/${segments.join('/')}.ts`,\n path: (segments) => `/${segments.join('/')}`,\n },\n};\n\n/** the locale segment of route files, with a trailing slash */\nexport function localeSegment(framework: Framework, i18n: I18nInfo | null) {\n return i18n ? `${conventions[framework].locale(i18n.optionalLocale)}/` : '';\n}\n\nexport function formatRoute(\n route: RouteDescriptor,\n framework: Framework,\n i18n: I18nInfo | null,\n ext = 'ts',\n): FormattedRoute {\n const convention = conventions[framework];\n const segments: string[] = [];\n const patterns: string[] = [];\n if (route.locale && i18n) {\n segments.push(convention.locale(i18n.optionalLocale));\n patterns.push(':lang');\n }\n\n for (const segment of route.segments) {\n if (typeof segment === 'string') {\n for (const part of segment.split('/')) {\n if (part.length === 0) continue;\n segments.push(part);\n patterns.push(part);\n }\n } else {\n const { param, catchAll = false, optional = false, suffix } = segment;\n segments.push(convention.param(param, catchAll, optional, suffix));\n patterns.push(catchAll ? `:${param}*` : `:${param}`);\n }\n }\n\n return {\n file: convention.file(segments, ext),\n path: convention.path(segments),\n pattern: `/${patterns.join('/')}`,\n };\n}\n\nexport interface RouteModule {\n /** import statements of the handler */\n imports: string;\n /** expression returning a `Response` */\n body: string;\n /** the handler reads `request` */\n request?: boolean;\n /** @defaultValue `['GET']` */\n methods?: string[];\n /** Next.js: never revalidate the route */\n revalidate?: boolean;\n /** Waku & Astro: prerender the route */\n static?: boolean;\n}\n\n/** the route module of a handler that only reads the request, per framework */\nexport function routeModule(framework: Framework, route: FormattedRoute, mod: RouteModule): string {\n const { imports, body, request, methods = ['GET'], revalidate, static: isStatic } = mod;\n const arg = request ? 'request: Request' : '';\n const head = `${imports.trim()}\\n\\n`;\n\n if (framework === 'astro') {\n const out: string[] = [];\n if (isStatic) out.push('export const prerender = true;\\n');\n for (const method of methods)\n out.push(\n `export const ${method}: APIRoute = async (${request ? '{ request }' : ''}) => ${body};\\n`,\n );\n\n return `import type { APIRoute } from 'astro';\\n${head}${out.join('\\n')}`;\n }\n\n if (framework === 'react-router') {\n const out: string[] = [];\n // GET is the loader, every other method is the action\n out.push(handler('loader', 'Route.LoaderArgs'));\n if (methods.length > 1) out.push(handler('action', 'Route.ActionArgs'));\n\n return `${request ? `${reactRouterTypes(route)}\\n` : ''}${head}${out.join('\\n')}`;\n }\n\n if (framework === 'tanstack-start') {\n const out: string[] = [];\n for (const method of methods)\n out.push(` ${method}: async (${request ? '{ request }' : ''}) => ${body},`);\n\n return `import { createFileRoute } from '@tanstack/react-router';\n${head}export const Route = createFileRoute('${route.path}')({\n server: {\n handlers: {\n${out.join('\\n')}\n },\n },\n});\n`;\n }\n\n const out: string[] = [];\n if (framework === 'next' && revalidate) out.push('export const revalidate = false;\\n');\n for (const method of methods)\n out.push(`export async function ${method}(${arg}) {\n return ${body};\n}\n`);\n if (framework === 'waku' && isStatic)\n out.push(`export async function getConfig() {\n return {\n render: 'static' as const,\n } as const;\n}\n`);\n\n return `${head}${out.join('\\n')}`;\n\n function handler(name: string, type: string) {\n return `export async function ${name}(${request ? `{ request }: ${type}` : ''}) {\n return ${body};\n}\n`;\n }\n}\n\n/**\n * `getStaticPaths()` of an Astro route per docs page, also prerendered with `output: 'server'`.\n * With i18n, the route is under the locale segment, and reads `i18n` of `@/lib/i18n` for an optional locale.\n */\nexport function astroPagePaths(i18n: I18nInfo | null) {\n const prerender = 'export const prerender = true;\\n\\n';\n if (!i18n)\n return `${prerender}export const getStaticPaths = (() => {\n return source.getPages().map((page) => ({\n params: { slug: page.slugs.join('/') || undefined },\n props: { page },\n }));\n}) satisfies GetStaticPaths;`;\n\n return `${prerender}export const getStaticPaths = (() => {\n const paths = [];\n for (const { language, pages } of source.getLanguages()) {\n for (const page of pages) {\n paths.push({\n params: {\n ${i18n.optionalLocale ? '// the default language has no prefix\\n lang: language === i18n.defaultLanguage ? undefined : language' : 'lang: language'},\n slug: page.slugs.join('/') || undefined,\n },\n props: { lang: language, page },\n });\n }\n }\n return paths;\n}) satisfies GetStaticPaths;`;\n}\n\n/** an Astro endpoint per docs page, `body` is the response of `page` */\nexport const astroPageRoute = (i18n: I18nInfo | null, imports: string, body: string) =>\n `import type { APIRoute, GetStaticPaths, InferGetStaticPropsType } from 'astro';\n${imports.trim()}\n${i18n?.optionalLocale ? \"import { i18n } from '@/lib/i18n';\\n\" : ''}\n${astroPagePaths(i18n)}\n\nexport const GET: APIRoute<InferGetStaticPropsType<typeof getStaticPaths>> = async ({\n props: { page },\n}) => {\n return ${body};\n};\n`;\n\n/** import of the generated types of a React Router route module */\nexport const reactRouterTypes = (route: FormattedRoute) =>\n `import type { Route } from './+types/${route.file\n .split('/')\n .pop()!\n .replace(/\\.tsx?$/, '')}';`;\n","import type { Framework, I18nInfo } from '@/project';\nimport { astroPagePaths, localeSegment } from '@/project/route';\n\nexport interface TemplateInput {\n static: boolean;\n i18n: I18nInfo | null;\n /** import specifier of `RootProvider` */\n provider: string;\n /** extra props of `RootProvider` */\n providerProps: string;\n}\n\n/** files relative to the base dir */\nexport type Template = (input: TemplateInput) => Record<string, string>;\n\nconst wrapper = (children: string) =>\n `<div className=\"flex flex-col min-h-screen\">${children}</div>`;\n\nconst i18nConfig = (optionalLocale: boolean) => `import { defineI18n } from 'fumadocs-core/i18n';\n\nexport const i18n = defineI18n({\n defaultLanguage: 'en',\n languages: ['en', 'cn'],${optionalLocale ? \"\\n hideLocale: 'default-locale',\" : ''}\n});\n`;\n\nconst sharedConstants = `export const docsRoute = '/docs';\n`;\n\nconst source = (async: boolean, i18n: boolean) => `import { loader } from 'fumadocs-core/source';\nimport { defineDocs } from 'fumadocs-mdx/macro';\nimport { docsRoute } from './shared';\n${i18n ? \"import { i18n } from '@/lib/i18n';\\n\" : ''}\nexport const docs = defineDocs({\n dir: 'content/docs',${async ? '\\n docs: {\\n async: true,\\n },' : ''}\n});\n\nexport const source = loader({\n baseUrl: docsRoute,\n source: docs.toFumadocsSource(),${i18n ? '\\n i18n,' : ''}\n});\n`;\n\nconst layoutShared = (i18n: I18nInfo | null) =>\n i18n\n ? `import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';\nimport { uiTranslations } from 'fumadocs-ui/i18n';\nimport { zhTW } from '@fumadocs/language/zh-tw';\nimport { i18n } from '@/lib/i18n';\n\nexport const translations = i18n.translations().extend(uiTranslations()).preset('cn', zhTW());\n\nexport function baseOptions(locale: string = i18n.defaultLanguage): BaseLayoutProps {\n return {\n nav: {\n title: 'My App',\n url: ${i18n.optionalLocale ? \"locale === i18n.defaultLanguage ? '/' : `/${locale}`\" : '`/${locale}`'},\n },\n };\n}\n`\n : `import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';\n\nexport function baseOptions(): BaseLayoutProps {\n return {\n nav: {\n title: 'My App',\n },\n };\n}\n`;\n\nconst mdx = `import defaultMdxComponents from 'fumadocs-ui/mdx';\nimport type { MDXComponents } from 'mdx/types';\n\nexport function getMDXComponents(components?: MDXComponents) {\n return {\n ...defaultMdxComponents,\n ...components,\n } satisfies MDXComponents;\n}\n\nexport const useMDXComponents = getMDXComponents;\n\ndeclare global {\n type MDXProvidedComponents = ReturnType<typeof getMDXComponents>;\n}\n`;\n\nconst search = (i18n: boolean) => `'use client';\nimport {\n SearchDialog,\n SearchDialogClose,\n SearchDialogContent,\n SearchDialogHeader,\n SearchDialogIcon,\n SearchDialogInput,\n SearchDialogList,\n SearchDialogOverlay,\n type SharedProps,\n} from 'fumadocs-ui/components/dialog/search';\nimport { useStaticSearch } from 'fumadocs-core/search/client';\n${i18n ? \"import { useI18n } from 'fumadocs-ui/contexts/i18n';\\n\" : ''}\nexport default function DefaultSearchDialog(props: SharedProps) {\n${i18n ? ' const { locale } = useI18n();\\n' : ''} const search = useStaticSearch(${i18n ? '{ locale }' : ''});\n\n return (\n <SearchDialog {...search} {...props}>\n <SearchDialogOverlay />\n <SearchDialogContent>\n <SearchDialogHeader>\n <SearchDialogIcon />\n <SearchDialogInput />\n <SearchDialogClose />\n </SearchDialogHeader>\n <SearchDialogList />\n </SearchDialogContent>\n </SearchDialog>\n );\n}\n`;\n\nfunction shared(input: TemplateInput, async: boolean): Record<string, string> {\n const i18n = input.i18n !== null;\n return {\n 'lib/shared.ts': sharedConstants,\n 'lib/source.ts': source(async, i18n),\n 'lib/layout.shared.tsx': layoutShared(input.i18n),\n 'components/mdx.tsx': mdx,\n ...(input.static ? { 'components/search.tsx': search(i18n) } : {}),\n ...(input.i18n ? { 'lib/i18n.ts': i18nConfig(input.i18n.optionalLocale) } : {}),\n };\n}\n\nconst searchImport = (input: TemplateInput) =>\n input.static ? `import SearchDialog from '@/components/search';\\n` : '';\n\n/** imports of the root provider, with the i18n provider */\nconst providerImports = (input: TemplateInput) =>\n `import { RootProvider } from '${input.provider}';\n${searchImport(input)}${\n input.i18n\n ? `import { i18nProvider } from 'fumadocs-ui/i18n';\nimport { translations } from '@/lib/layout.shared';\n`\n : ''\n }`;\n\nconst i18nImport = \"import { i18n } from '@/lib/i18n';\\n\";\n\nconst providerProps = (input: TemplateInput) =>\n `${input.i18n ? ' i18n={i18nProvider(translations, lang)}' : ''}${input.providerProps}`;\n\n/** the docs page for React Router & TanStack Start, where MDX content is loaded lazily on client */\nconst clientContent = `function Content({ path }: { path: string }) {\n const page = docs.getPage(path);\n if (!page) throw new Error(\\`unknown page: \\${path}\\`);\n\n const { toc } = use(page.load());\n const MDX = page.body;\n\n return (\n <DocsPage toc={toc}>\n <DocsTitle>{page.title}</DocsTitle>\n <DocsDescription>{page.description}</DocsDescription>\n <DocsBody>\n <MDX components={useMDXComponents()} />\n </DocsBody>\n </DocsPage>\n );\n}`;\n\nconst searchRoute = {\n next: (input: TemplateInput) => `import { source } from '@/lib/source';\nimport { createFromSource } from 'fumadocs-core/search/server';\n${input.static ? '\\nexport const revalidate = false;\\n' : ''}\nexport const { ${input.static ? 'staticGET: GET' : 'GET'} } = createFromSource(source);\n`,\n waku: `import { createFromSource } from 'fumadocs-core/search/server';\nimport { source } from '@/lib/source';\n\nexport const { GET } = createFromSource(source);\n`,\n};\n\nexport const templates: Record<Framework, Template> = {\n next: (input) => {\n const i18n = input.i18n !== null;\n // route group with `[lang]` segment for i18n\n const dir = i18n ? 'app/(docs)/[lang]' : 'app/(docs)';\n const route = i18n ? '/[lang]' : '';\n const lang = i18n ? ', params.lang' : '';\n\n return {\n ...shared(input, false),\n [`${dir}/layout.tsx`]: input.static\n ? `'use client';\n${providerImports(input)}${i18n ? \"import { use } from 'react';\\n\" : ''}\nexport default function Layout({ ${i18n ? 'params, ' : ''}children }: LayoutProps<'${route || '/'}'>) {\n${i18n ? ' const { lang } = use(params);\\n\\n' : ''} return (\n <RootProvider${providerProps(input)}>\n ${wrapper('{children}')}\n </RootProvider>\n );\n}\n`\n : `${providerImports(input)}\nexport default ${i18n ? 'async ' : ''}function Layout({ ${i18n ? 'params, ' : ''}children }: LayoutProps<'${route || '/'}'>) {\n${i18n ? ' const { lang } = await params;\\n\\n' : ''} return (\n <RootProvider${providerProps(input)}>\n ${wrapper('{children}')}\n </RootProvider>\n );\n}\n`,\n [`${dir}/docs/layout.tsx`]: `import { source } from '@/lib/source';\nimport { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { baseOptions } from '@/lib/layout.shared';\n\nexport default ${i18n ? 'async ' : ''}function Layout({ ${i18n ? 'params, ' : ''}children }: LayoutProps<'${route}/docs'>) {\n${i18n ? ' const { lang } = await params;\\n\\n' : ''} return (\n <DocsLayout tree={source.getPageTree(${i18n ? 'lang' : ''})} {...baseOptions(${i18n ? 'lang' : ''})}>\n {children}\n </DocsLayout>\n );\n}\n`,\n [`${dir}/docs/[[...slug]]/page.tsx`]: `import { source } from '@/lib/source';\nimport { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';\nimport { notFound } from 'next/navigation';\nimport { getMDXComponents } from '@/components/mdx';\nimport type { Metadata } from 'next';\nimport { createRelativeLink } from 'fumadocs-ui/mdx';\n\nexport default async function Page(props: PageProps<'${route}/docs/[[...slug]]'>) {\n const params = await props.params;\n const page = source.getPage(params.slug${lang});\n if (!page) notFound();\n\n const MDX = page.data.body;\n\n return (\n <DocsPage toc={page.data.toc} full={page.data.full}>\n <DocsTitle>{page.data.title}</DocsTitle>\n <DocsDescription>{page.data.description}</DocsDescription>\n <DocsBody>\n <MDX\n components={getMDXComponents({\n // this allows you to link to other pages with relative file paths\n a: createRelativeLink(source, page),\n })}\n />\n </DocsBody>\n </DocsPage>\n );\n}\n\nexport async function generateStaticParams() {\n return source.generateParams();\n}\n\nexport async function generateMetadata(props: PageProps<'${route}/docs/[[...slug]]'>): Promise<Metadata> {\n const params = await props.params;\n const page = source.getPage(params.slug${lang});\n if (!page) notFound();\n\n return {\n title: page.data.title,\n description: page.data.description,\n };\n}\n`,\n 'app/api/search/route.ts': searchRoute.next(input),\n };\n },\n\n 'react-router': (input) => {\n const i18n = input.i18n !== null;\n return {\n ...shared(input, true),\n 'routes/docs/layout.tsx': `import { Outlet${i18n ? ', useParams' : ''} } from 'react-router';\n${providerImports(input)}${i18n ? i18nImport : ''}\nexport default function Layout() {\n${i18n ? ' const { lang = i18n.defaultLanguage } = useParams();\\n\\n' : ''} return (\n <RootProvider${providerProps(input)}>\n ${wrapper('\\n <Outlet />\\n ')}\n </RootProvider>\n );\n}\n`,\n 'routes/docs/page.tsx': `import type { Route } from './+types/page';\nimport { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';\nimport { docs, source } from '@/lib/source';\nimport { baseOptions } from '@/lib/layout.shared';\nimport { useFumadocsLoader } from 'fumadocs-core/source/client';\nimport { useMDXComponents } from '@/components/mdx';\nimport { use } from 'react';\n\nexport async function loader({ params }: Route.LoaderArgs) {\n const slugs = params['*'].split('/').filter((v) => v.length > 0);\n const page = source.getPage(slugs${i18n ? ', params.lang' : ''});\n if (!page) throw new Response('Not found', { status: 404 });\n\n return {\n path: page.path,\n url: page.url,\n pageTree: await source.serializePageTree(source.getPageTree(${i18n ? 'params.lang' : ''})),\n };\n}\n\n${clientContent.replace('<DocsPage toc={toc}>', '<DocsPage toc={toc}>\\n <title>{page.title}</title>\\n <meta name=\"description\" content={page.description} />')}\n\nexport default function Page({ loaderData${i18n ? ', params' : ''} }: Route.ComponentProps) {\n const { path, pageTree } = useFumadocsLoader(loaderData);\n\n return (\n <DocsLayout {...baseOptions(${i18n ? 'params.lang' : ''})} tree={pageTree}>\n <Content path={path} />\n </DocsLayout>\n );\n}\n`,\n 'routes/docs/search.ts': `import type { Route } from './+types/search';\nimport { createFromSource } from 'fumadocs-core/search/server';\nimport { source } from '@/lib/source';\n\nconst server = createFromSource(source);\n\nexport async function loader(${input.static ? '' : '{ request }: Route.LoaderArgs'}) {\n return server.${input.static ? 'staticGET()' : 'GET(request)'};\n}\n`,\n };\n },\n\n 'tanstack-start': (input) => {\n const seg = localeSegment('tanstack-start', input.i18n);\n const i18n = input.i18n !== null;\n return {\n ...shared(input, true),\n 'routes/_docs.tsx': `import { createFileRoute, Outlet${i18n ? ', useParams' : ''} } from '@tanstack/react-router';\n${providerImports(input)}${i18n ? i18nImport : ''}\nexport const Route = createFileRoute('/_docs')({\n component: Layout,\n});\n\nfunction Layout() {\n${i18n ? ' const { lang = i18n.defaultLanguage } = useParams({ strict: false });\\n\\n' : ''} return (\n <RootProvider${providerProps(input)}>\n ${wrapper('\\n <Outlet />\\n ')}\n </RootProvider>\n );\n}\n`,\n [`routes/_docs/${seg}docs/$.tsx`]: `import { createFileRoute, notFound } from '@tanstack/react-router';\nimport { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { createServerFn } from '@tanstack/react-start';\nimport { docs, source } from '@/lib/source';\nimport { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';\nimport { baseOptions } from '@/lib/layout.shared';\nimport { useFumadocsLoader } from 'fumadocs-core/source/client';\nimport { Suspense, use } from 'react';\nimport { useMDXComponents } from '@/components/mdx';\n\nexport const Route = createFileRoute('/_docs/${seg}docs/$')({\n component: Page,\n loader: async ({ params }) => {\n const data = await serverLoader({\n data: {\n slugs: params._splat?.split('/') ?? [],${i18n ? '\\n lang: params.lang,' : ''}\n },\n });\n await docs.getPage(data.path)?.preload();\n return data;\n },\n});\n\nconst serverLoader = createServerFn({\n method: 'GET',\n})\n .validator((params: { slugs: string[]${i18n ? '; lang?: string' : ''} }) => params)\n .handler(async ({ data: { slugs${i18n ? ', lang' : ''} } }) => {\n const page = source.getPage(slugs${i18n ? ', lang' : ''});\n if (!page) throw notFound();\n\n return {\n path: page.path,\n pageTree: await source.serializePageTree(source.getPageTree(${i18n ? 'lang' : ''})),\n };\n });\n\n${clientContent}\n\nfunction Page() {\n${i18n ? ' const { lang } = Route.useParams();\\n' : ''} const data = useFumadocsLoader(Route.useLoaderData());\n\n return (\n <DocsLayout {...baseOptions(${i18n ? 'lang' : ''})} tree={data.pageTree}>\n <Suspense>\n <Content path={data.path} />\n </Suspense>\n </DocsLayout>\n );\n}\n`,\n 'routes/api/search.ts': `import { createFileRoute } from '@tanstack/react-router';\nimport { source } from '@/lib/source';\nimport { createFromSource } from 'fumadocs-core/search/server';\n\nconst server = createFromSource(source);\n\nexport const Route = createFileRoute('/api/search')({\n server: {\n handlers: {\n GET: ${input.static ? '() => server.staticGET()' : 'async ({ request }) => server.GET(request)'},\n },\n },\n});\n`,\n };\n },\n\n waku: (input) => {\n const i18n = input.i18n !== null;\n const page = `import { source } from '@/lib/source';\nimport type { PageProps } from 'waku/router';\nimport { unstable_notFound } from 'waku/router/server';\nimport { createRelativeLink } from 'fumadocs-ui/mdx';\nimport { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';\nimport { getMDXComponents } from '@/components/mdx';\n${i18n ? \"import { DocsLayout } from 'fumadocs-ui/layouts/docs';\\nimport { baseOptions } from '@/lib/layout.shared';\\n\" : ''}\nexport default function Page({ slug${i18n ? ', lang' : ''} }: PageProps<'${i18n ? '/[lang]' : ''}/docs/[...slug]'>) {\n const page = source.getPage(slug${i18n ? ', lang' : ''});\n if (!page) unstable_notFound();\n\n const MDX = page.data.body;\n const content = (\n <DocsPage toc={page.data.toc}>\n <DocsTitle>{page.data.title}</DocsTitle>\n <DocsDescription>{page.data.description}</DocsDescription>\n <DocsBody>\n <MDX\n components={getMDXComponents({\n // this allows you to link to other pages with relative file paths\n a: createRelativeLink(source, page),\n })}\n />\n </DocsBody>\n </DocsPage>\n );\n${\n i18n\n ? `\n // layouts don't receive the locale, render the docs layout here\n return (\n <DocsLayout {...baseOptions(lang)} tree={source.getPageTree(lang)}>\n {content}\n </DocsLayout>\n );`\n : `\n return content;`\n}\n}\n\nexport async function getConfig() {\n return {\n render: 'static',\n staticPaths: source.generateParams().map((item) => ${i18n ? '[item.lang, ...item.slug]' : 'item.slug'}),\n } as const;\n}\n`;\n\n return {\n ...shared(input, false),\n 'pages/(docs)/_layout.tsx': i18n\n ? `import type { ReactNode } from 'react';\nimport { Provider } from '@/components/provider';\n\nexport default function Layout({ children }: { children: ReactNode }) {\n return (\n <Provider>\n ${wrapper('{children}')}\n </Provider>\n );\n}\n`\n : `import type { ReactNode } from 'react';\n${providerImports(input)}\nexport default function Layout({ children }: { children: ReactNode }) {\n return (\n <RootProvider${providerProps(input)}>\n ${wrapper('{children}')}\n </RootProvider>\n );\n}\n`,\n ...(i18n\n ? {\n 'components/provider.tsx': `'use client';\nimport type { ReactNode } from 'react';\nimport { useRouter } from 'waku/router/client';\n${providerImports(input)}${i18nImport}\nexport function Provider({ children }: { children: ReactNode }) {\n const router = useRouter();\n const lang = router.path.split('/')[1] || i18n.defaultLanguage;\n\n return <RootProvider${providerProps(input)}>{children}</RootProvider>;\n}\n`,\n 'pages/(docs)/[lang]/docs/[...slug].tsx': page,\n }\n : {\n 'pages/(docs)/docs/_layout.tsx': `import type { ReactNode } from 'react';\nimport { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { source } from '@/lib/source';\nimport { baseOptions } from '@/lib/layout.shared';\n\nexport default function Layout({ children }: { children: ReactNode }) {\n return (\n <DocsLayout {...baseOptions()} tree={source.getPageTree()}>\n {children}\n </DocsLayout>\n );\n}\n`,\n 'pages/(docs)/docs/[...slug].tsx': page,\n }),\n 'pages/_api/api/search.ts': searchRoute.waku,\n };\n },\n\n astro: (input) => {\n const i18n = input.i18n !== null;\n return {\n ...shared(input, false),\n 'lib/source.ts': `import { getCollection } from 'astro:content';\nimport { loader } from 'fumadocs-core/source';\nimport { toFumadocsSource } from '@fumadocs/astro/source';\nimport { docsRoute } from './shared';\n${i18n ? i18nImport : ''}\nexport const source = loader({\n source: toFumadocsSource(await getCollection('docs'), await getCollection('meta')),\n baseUrl: docsRoute,${i18n ? '\\n i18n,' : ''}\n});\n`,\n 'components/mdx.tsx': `import defaultMdxComponents from 'fumadocs-ui/mdx';\n${i18n ? \"import { DynamicLink } from 'fumadocs-core/dynamic-link';\\n\" : ''}import { DocsBody } from 'fumadocs-ui/layouts/docs/page';\nimport type { MDXComponents } from 'mdx/types';\n${i18n ? \"import { ContentProvider, type ProviderProps } from '@/components/provider';\\n\" : ''}\nexport function getMDXComponents(components?: MDXComponents) {\n return {\n ...defaultMdxComponents,${i18n ? '\\n // links with the locale, like `/[lang]/docs`\\n DynamicLink,' : ''}\n ...components,\n } satisfies MDXComponents;\n}\n\n${\n i18n\n ? `// for the Fumadocs integration, \\`wrapper\\` receives the props of \\`<Content />\\`\nexport default getMDXComponents({\n wrapper: ({ children, ...props }: ProviderProps) => (\n <ContentProvider {...props}>\n <DocsBody>{children}</DocsBody>\n </ContentProvider>\n ),\n});`\n : `// for the Fumadocs integration, \\`wrapper\\` gives the content its prose styles\nexport default getMDXComponents({ wrapper: DocsBody });`\n}\n`,\n 'components/provider.tsx': `import { navigate } from 'astro:transitions/client';\nimport type { ReactNode } from 'react';\n${providerImports(input)}\nexport interface ProviderProps {${i18n ? '\\n lang: string;' : ''}\n /** \\`Astro.url.pathname\\` */\n pathname: string;\n /** \\`Astro.params\\` */\n params: Record<string, string | undefined>;\n children?: ReactNode;\n}\n\nexport function Provider({ ${i18n ? 'lang, ' : ''}pathname, params, children }: ProviderProps) {\n return (\n <RootProvider pathname={pathname} params={params} navigate={navigate}${providerProps(input)}>\n {children}\n </RootProvider>\n );\n}\n${\n i18n\n ? `\n/**\n * For the MDX content, which is an island of its own.\n */\nexport function ContentProvider({ lang, pathname, params, children }: ProviderProps) {\n return (\n <RootProvider\n pathname={pathname}\n params={params}\n navigate={navigate}\n i18n={i18nProvider(translations, lang)}\n theme={{ enabled: false }}\n search={{ enabled: false }}\n >\n {children}\n </RootProvider>\n );\n}\n`\n : ''\n}`,\n 'components/docs.tsx': `import { type SerializedPageTree, useFumadocsLoader } from 'fumadocs-core/source/client';\nimport type { TOCItemType } from 'fumadocs-core/toc';\nimport { DocsLayout } from 'fumadocs-ui/layouts/docs';\nimport { DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';\nimport { Provider, type ProviderProps } from '@/components/provider';\nimport { baseOptions } from '@/lib/layout.shared';\n\nexport interface DocsProps extends ProviderProps {\n tree: SerializedPageTree;\n title: string;\n description?: string;\n toc: TOCItemType[];\n}\n\nexport function Docs({ tree, title, description, toc, children, ...props }: DocsProps) {\n const data = useFumadocsLoader({ tree });\n\n return (\n <Provider {...props}>\n <DocsLayout {...baseOptions(${i18n ? 'props.lang' : ''})} tree={data.tree}>\n <DocsPage toc={toc}>\n <DocsTitle>{title}</DocsTitle>\n <DocsDescription>{description}</DocsDescription>\n {children}\n </DocsPage>\n </DocsLayout>\n </Provider>\n );\n}\n`,\n [`pages/${localeSegment('astro', input.i18n)}docs/[...slug].astro`]: `---\nimport type { GetStaticPaths, InferGetStaticPropsType } from 'astro';\nimport Layout from '@/layouts/Layout.astro';\nimport { Docs } from '@/components/docs';\n${input.i18n?.optionalLocale ? i18nImport : ''}import { source } from '@/lib/source';\n\n${astroPagePaths(input.i18n)}\n\ntype Props = InferGetStaticPropsType<typeof getStaticPaths>;\n\nconst { ${i18n ? 'lang, ' : ''}page } = Astro.props;\nconst { body: Content, toc } = await page.data.load();\nconst pageTree = await source.serializePageTree(source.getPageTree(${i18n ? 'lang' : ''}));\n${i18n ? 'const provider = { lang, pathname: Astro.url.pathname, params: Astro.params };\\n' : ''}---\n\n<Layout\n title={page.data.title}\n description={page.data.description}${i18n ? '\\n lang={lang}' : ''}\n>\n <Docs\n client:load\n transition:persist=\"docs\"\n ${i18n ? '{...provider}' : 'pathname={Astro.url.pathname}\\n params={Astro.params}'}\n tree={pageTree}\n title={page.data.title}\n description={page.data.description}\n toc={toc}\n >\n <Content${i18n ? ' {...provider}' : ''} />\n </Docs>\n</Layout>\n`,\n 'pages/api/search.ts': `import type { APIRoute } from 'astro';\nimport { createFromSource } from 'fumadocs-core/search/server';\nimport { source } from '@/lib/source';\n\nconst server = createFromSource(source);\n\nexport const GET: APIRoute = ${input.static ? '() => server.staticGET()' : '({ request }) => server.GET(request)'};\n`,\n };\n },\n};\n\nexport const astroContentConfig = `import { defineCollection } from 'astro:content';\nimport { metaLoader, pageLoader } from '@fumadocs/astro/loaders';\n\nexport const collections = {\n docs: defineCollection({ loader: pageLoader() }),\n meta: defineCollection({ loader: metaLoader() }),\n};\n`;\n\nexport const nextProxy = `import { createI18nMiddleware } from 'fumadocs-core/i18n/middleware';\nimport { i18n } from '@/lib/i18n';\n\nexport default createI18nMiddleware(i18n);\n\nexport const config = {\n // only the docs routes, add the ones of other features here\n matcher: ['/docs/:path*', '/:lang/docs/:path*'],\n};\n`;\n\nexport const sampleContent = `---\ntitle: Hello World\ndescription: Your first document\n---\n\nWelcome to the docs! You can edit this page in \\`content/docs/index.mdx\\`.\n\n## Heading\n\n<Cards>\n <Card title=\"Learn more about Fumadocs\" href=\"https://fumadocs.dev\" />\n</Cards>\n`;\n\nexport const sampleContentCn = `---\ntitle: 你好,世界\ndescription: 你的第一份文檔\n---\n\n歡迎來到文檔!你可以在 \\`content/docs/index.cn.mdx\\` 編輯這個頁面。\n`;\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Feature, FeatureContext } from '@/features';\nimport type { I18nInfo, Project } from '@/project';\nimport {\n addImport,\n addJsxAttribute,\n addVitePlugin,\n findJsxElement,\n type SourceFile,\n wrapNextConfig,\n} from '@/codemod';\nimport { exists } from '@/utils/fs';\nimport { localeSegment } from '@/project/route';\nimport {\n astroContentConfig,\n nextProxy,\n sampleContent,\n sampleContentCn,\n templates,\n} from './templates';\nimport { registerReactRouterRoutes } from '../utils';\n\nconst cssImports = (preset: string) => [\n `@import 'fumadocs-ui/css/${preset}.css';`,\n `@import 'fumadocs-ui/css/preset.css';`,\n];\n\nexport const docs: Feature<{ i18n: boolean }> = {\n id: 'docs',\n title: 'Docs',\n description: 'set up Fumadocs on your app, in a dedicated route group',\n options: {\n i18n: { message: 'Enable internationalization?', initialValue: false },\n },\n async detect(project) {\n return exists(path.join(project.cwd, project.baseDir, 'lib/source.ts'));\n },\n async apply(ctx, options) {\n const { project } = ctx;\n const { baseDir, packageJson, config, framework } = project;\n // Waku has no optional route segments, the locale is always in the URL\n const i18n = project.i18n ?? (options.i18n ? { optionalLocale: framework !== 'waku' } : null);\n const providerProps: string[] = [];\n if (project.static) providerProps.push('search={{ SearchDialog }}');\n if ('next-themes' in (packageJson.dependencies ?? {}))\n providerProps.push('theme={{ enabled: false }}');\n\n ctx.addDependencies({\n 'fumadocs-core': null,\n 'fumadocs-ui': config.uiLibrary === 'base-ui' ? 'npm:@fumadocs/base-ui' : null,\n });\n // the language pack of `cn`\n if (i18n) ctx.addDependencies({ '@fumadocs/language': null });\n if (framework === 'astro') {\n // the integrations of Fumadocs and React, Tailwind CSS\n ctx.addDependencies({\n '@fumadocs/astro': null,\n '@astrojs/react': null,\n react: null,\n 'react-dom': null,\n });\n ctx.addDependencies(\n {\n '@types/mdx': null,\n '@types/react': null,\n '@types/react-dom': null,\n tailwindcss: null,\n '@tailwindcss/vite': null,\n },\n true,\n );\n } else {\n ctx.addDependencies({ 'fumadocs-mdx': null });\n ctx.addDependencies({ '@types/mdx': null }, true);\n }\n\n const files = templates[framework]({\n static: project.static,\n i18n,\n provider: project.info.provider,\n providerProps: providerProps.map((prop) => ` ${prop}`).join(''),\n });\n for (const [file, content] of Object.entries(files)) {\n await ctx.write(path.posix.join(baseDir, file), content);\n }\n await ctx.write('content/docs/index.mdx', sampleContent);\n if (i18n) await ctx.write('content/docs/index.cn.mdx', sampleContentCn);\n if (framework === 'astro') await configureCollections(ctx);\n else await ctx.append('.gitignore', ['.source']);\n if (i18n && framework === 'next') await configureProxy(ctx);\n\n await configureBundler(ctx);\n await configureCss(ctx);\n // Astro renders `<html>` without React\n if (framework !== 'astro') await suppressHydrationWarning(ctx);\n if (framework === 'react-router') await configureRoutes(ctx, i18n);\n\n const tsconfig = await fs\n .readFile(path.join(project.cwd, 'tsconfig.json'), 'utf-8')\n .catch(() => '');\n if (!tsconfig.includes('\"@/*\"')) {\n ctx.note(\n `Generated files import from the \\`@/*\\` alias, add it to \\`tsconfig.json\\`:\\n \"paths\": { \"@/*\": [\"./${baseDir ? `${baseDir}/` : ''}*\"] }`,\n );\n }\n if (project.static && framework === 'tanstack-start') {\n ctx.note(\n 'In SPA mode, prerender the docs pages and search index, see https://fumadocs.dev/docs/manual-installation/tanstack-start.',\n );\n }\n if (i18n && framework === 'react-router') {\n ctx.note(\n 'When prerendering, list the docs pages with `source.getPages().map((page) => page.url)` instead of globbing content files, see https://fumadocs.dev/docs/internationalization/react-router.',\n );\n }\n if (framework === 'astro') {\n const layout = path.join(baseDir, project.info.rootFile);\n const content = await fs.readFile(path.join(project.cwd, layout), 'utf-8').catch(() => '');\n if (!content.includes('Astro.props'))\n ctx.note(\n `Docs pages render in \\`${layout}\\` with \\`title\\` and \\`description\\` props, add them to its \\`<head>\\`.`,\n );\n if (i18n)\n ctx.note(\n `Docs pages pass the locale as \\`lang\\` to \\`${layout}\\`, set it on \\`<html lang>\\`.`,\n );\n }\n if (i18n) {\n ctx.note(\n `Languages are configured in \\`lib/i18n.ts\\`, translated content uses a locale suffix like \\`index.cn.mdx\\`.\\nUI translations: https://fumadocs.dev/docs/internationalization#translations`,\n );\n }\n ctx.note(\n `Start the dev server and open ${i18n && !i18n.optionalLocale ? '/en/docs' : '/docs'} to see your docs.`,\n );\n },\n};\n\n/** the Next.js proxy (middleware) file of the project, relative to cwd */\nexport async function findNextProxy({ cwd, baseDir }: Project) {\n for (const file of ['proxy.ts', 'middleware.ts']) {\n for (const dir of new Set([baseDir, ''])) {\n const target = path.join(dir, file);\n if (await exists(path.join(cwd, target))) return target;\n }\n }\n}\n\n/** the i18n middleware redirects & rewrites docs URLs without locale */\nasync function configureProxy(ctx: FeatureContext) {\n const existing = await findNextProxy(ctx.project);\n if (!existing) {\n await ctx.write(path.join(ctx.project.baseDir, 'proxy.ts'), nextProxy);\n return;\n }\n\n ctx.note(\n `A proxy already exists at ${existing}, handle the docs locale there. Fumadocs provides \\`createI18nMiddleware\\` from 'fumadocs-core/i18n/middleware', see https://fumadocs.dev/docs/internationalization/next.`,\n );\n}\n\nfunction bundlerEdits({ framework, baseDir }: Project): ((file: SourceFile) => boolean)[] {\n if (framework === 'next') return [wrapNextConfig];\n if (framework === 'astro') {\n return [\n // it adds the React and MDX integrations\n (file) =>\n addVitePlugin(\n file,\n {\n name: 'fumadocs',\n from: '@fumadocs/astro',\n call: `fumadocs({ components: './${path.posix.join(baseDir, 'components/mdx.tsx')}' })`,\n default: true,\n },\n ['integrations'],\n ),\n (file) =>\n addVitePlugin(file, { name: 'tailwindcss', from: '@tailwindcss/vite', default: true }, [\n 'vite',\n 'plugins',\n ]),\n ];\n }\n\n return [\n (file) =>\n addVitePlugin(\n file,\n { name: 'fumadocsMdx', from: 'fumadocs-mdx/vite' },\n framework === 'waku' ? ['vite', 'plugins'] : ['plugins'],\n ),\n ];\n}\n\nasync function configureBundler(ctx: FeatureContext) {\n const { framework, configFile } = ctx.project;\n let edited = false;\n if (configFile) {\n // parse the file for each edit, so it sees the edits before it\n for (const edit of bundlerEdits(ctx.project)) {\n edited = await ctx\n .source(configFile, (file) => {\n if (!edit(file)) throw new Error(`cannot update ${configFile}`);\n })\n .catch(() => false);\n if (!edited) break;\n }\n }\n if (edited) return;\n\n if (framework === 'next') {\n ctx.note(\n \"Wrap your Next.js config with `createMDX()` from 'fumadocs-mdx/next', see https://fumadocs.dev/docs/mdx/next.\",\n );\n } else if (framework === 'astro') {\n ctx.note(\n 'Add the integrations of Fumadocs to your Astro config, see https://fumadocs.dev/docs/manual-installation/astro.',\n );\n } else {\n ctx.note(\n \"Add `fumadocsMdx()` from 'fumadocs-mdx/vite' to the Vite plugins of your config file.\",\n );\n }\n}\n\n/** Astro content collections of the docs */\nasync function configureCollections(ctx: FeatureContext) {\n // Astro warns about empty collections\n await ctx.write('content/docs/meta.json', '{\\n \"pages\": [\"...\"]\\n}\\n');\n const file = path.join(ctx.project.baseDir, 'content.config.ts');\n const content = await fs.readFile(path.join(ctx.project.cwd, file), 'utf-8').catch(() => null);\n if (content === null) {\n await ctx.write(file, astroContentConfig);\n } else if (!content.includes('pageLoader(')) {\n ctx.note(\n `A content config already exists at ${file}, add the collections of docs to it:\\n${astroContentConfig}`,\n );\n }\n}\n\n/** resolve the global CSS file imported by the root file */\nasync function findCssEntry({ cwd, baseDir, info }: Project): Promise<string | undefined> {\n const rootFile = path.join(baseDir, info.rootFile);\n const content = await fs.readFile(path.join(cwd, rootFile), 'utf-8').catch(() => '');\n const match = /import\\s+(?:\\w+\\s+from\\s+)?['\"]([^'\"]+\\.css)(?:\\?url)?['\"]/.exec(content);\n if (!match) return;\n const specifier = match[1];\n\n if (specifier.startsWith('@/')) return path.join(baseDir, specifier.slice(2));\n if (specifier.startsWith('.')) return path.join(path.dirname(rootFile), specifier);\n}\n\nasync function configureCss(ctx: FeatureContext) {\n const { cwd } = ctx.project;\n const preset = (await exists(path.join(cwd, 'components.json'))) ? 'shadcn' : 'neutral';\n const entry = await findCssEntry(ctx.project);\n if (entry && (await exists(path.join(cwd, entry)))) {\n await ctx.append(entry, cssImports(preset));\n return;\n }\n\n ctx.note(\n `Add the styles to your global CSS file (after \\`@import 'tailwindcss'\\`):\\n ${cssImports(preset).join('\\n ')}`,\n );\n}\n\n/** `next-themes` sets the theme class on `<html>`, React warns about the mismatch without this attribute */\nasync function suppressHydrationWarning(ctx: FeatureContext) {\n const { baseDir, info } = ctx.project;\n const edited = await ctx.source(path.join(baseDir, info.rootFile), (file) => {\n const html = findJsxElement(file, 'html')?.openingElement;\n if (!html || file.code.slice(html.start, html.end).includes('suppressHydrationWarning')) return;\n addJsxAttribute(file, html, 'suppressHydrationWarning');\n });\n\n if (!edited)\n ctx.note('Add `suppressHydrationWarning` to the `<html>` element of your root layout.');\n}\n\nasync function configureRoutes(ctx: FeatureContext, i18n: I18nInfo | null) {\n const added = await registerReactRouterRoutes(ctx, [\n `layout('routes/docs/layout.tsx', [route('${localeSegment('react-router', i18n)}docs/*', 'routes/docs/page.tsx')])`,\n { path: 'api/search', file: 'routes/docs/search.ts', pattern: '/api/search' },\n ]);\n if (!added) return;\n await ctx.source(path.join(ctx.project.baseDir, 'routes.ts'), (file) => {\n addImport(file, { from: '@react-router/dev/routes', named: ['layout', 'route'] });\n });\n}\n"],"mappings":"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"}
|