@fumadocs/cli 1.7.4 → 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-BmTB7dab.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-m0CwRlRz.js → og-DuPd9kNq.js} +12 -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-BmTB7dab.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-m0CwRlRz.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,7 +1,7 @@
|
|
|
1
|
-
import { a as addReactRouterRoute, p as addImport } from "./codemod-
|
|
1
|
+
import { a as addReactRouterRoute, p as addImport } from "./codemod-Wc9nRqrw.js";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
|
|
4
|
+
// src/features/utils.ts
|
|
5
5
|
/** the shallowest `.ts`/`.tsx` file under `dir` whose content includes `needle`, relative to `cwd` */
|
|
6
6
|
async function findSource(cwd, dir, needle, match = () => true) {
|
|
7
7
|
const entries = await fs.readdir(path.join(cwd, dir), {
|
|
@@ -20,11 +20,6 @@ async function findSource(cwd, dir, needle, match = () => true) {
|
|
|
20
20
|
function scripts(json, scripts) {
|
|
21
21
|
Object.assign(json.scripts ??= {}, scripts);
|
|
22
22
|
}
|
|
23
|
-
const reactOnly = (project) => project.framework !== "astro" || "Astro is not supported by this feature";
|
|
24
|
-
function reactFramework(project) {
|
|
25
|
-
if (project.framework === "astro") throw new Error("Astro is not supported by this feature");
|
|
26
|
-
return project.framework;
|
|
27
|
-
}
|
|
28
23
|
/** POSIX path for generated code and route configs */
|
|
29
24
|
const posix = (file) => file.split(path.sep).join("/");
|
|
30
25
|
/** add an export to a source file, unless a declaration with the same name exists */
|
|
@@ -79,8 +74,8 @@ async function registerReactRouterRoutes(ctx, routes) {
|
|
|
79
74
|
}
|
|
80
75
|
return added;
|
|
81
76
|
}
|
|
82
|
-
/** features reading page Markdown need Fumadocs MDX
|
|
83
|
-
const requiresMarkdown = (project) => project.
|
|
77
|
+
/** features reading page Markdown need the collections of Fumadocs MDX or `@fumadocs/astro`, or a runtime source exposing `page.data.content` */
|
|
78
|
+
const requiresMarkdown = (project) => project.source.collections !== null || project.source.dynamic || "requires Fumadocs MDX, `@fumadocs/astro` or a runtime content source";
|
|
84
79
|
function sourceRef(dynamic) {
|
|
85
80
|
return dynamic ? {
|
|
86
81
|
dynamic: true,
|
|
@@ -92,7 +87,6 @@ function sourceRef(dynamic) {
|
|
|
92
87
|
resolved: "source"
|
|
93
88
|
};
|
|
94
89
|
}
|
|
95
|
-
|
|
96
|
-
export { reactFramework as a, requiresMarkdown as c, sourceRef as d, posix as i, scripts as l, addPageUrl as n, reactOnly as o, findSource as r, registerReactRouterRoutes as s, addExport as t, sharedRoute as u };
|
|
90
|
+
export { registerReactRouterRoutes as a, sharedRoute as c, posix as i, sourceRef as l, addPageUrl as n, requiresMarkdown as o, findSource as r, scripts as s, addExport as t };
|
|
97
91
|
|
|
98
|
-
//# sourceMappingURL=utils-
|
|
92
|
+
//# sourceMappingURL=utils-Btt-BJ53.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-Btt-BJ53.js","names":[],"sources":["../src/features/utils.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Project } from '@/project';\nimport type { FormattedRoute } from '@/project/route';\nimport type { FeatureContext, PackageJson } from '@/features';\nimport { addImport, addReactRouterRoute } from '@/codemod';\n\n/** the shallowest `.ts`/`.tsx` file under `dir` whose content includes `needle`, relative to `cwd` */\nexport async function findSource(\n cwd: string,\n dir: string,\n needle: string,\n match: (file: string) => boolean = () => true,\n) {\n const entries = await fs\n .readdir(path.join(cwd, dir), { recursive: true, withFileTypes: true })\n .catch(() => []);\n const depth = (dir: string) => dir.split(path.sep).length;\n entries.sort((a, b) => depth(a.parentPath) - depth(b.parentPath));\n\n for (const entry of entries) {\n if (!entry.isFile() || !/\\.tsx?$/.test(entry.name) || entry.parentPath.includes('node_modules'))\n continue;\n const file = path.join(entry.parentPath, entry.name);\n if (!match(file)) continue;\n if ((await fs.readFile(file, 'utf-8')).includes(needle)) return path.relative(cwd, file);\n }\n}\n\nexport function scripts(json: PackageJson, scripts: Record<string, string>) {\n Object.assign((json.scripts ??= {}), scripts);\n}\n\n/** POSIX path for generated code and route configs */\nexport const posix = (file: string) => file.split(path.sep).join('/');\n\n/** add an export to a source file, unless a declaration with the same name exists */\nexport async function addExport(ctx: FeatureContext, file: string, name: string, code: string) {\n const current = await fs.readFile(path.join(ctx.project.cwd, file), 'utf-8').catch(() => '');\n if (new RegExp(`export (async )?(const|function) ${name}\\\\b`).test(current)) return false;\n await ctx.append(file, [code]);\n return true;\n}\n\n/** add a page URL helper to `lib/shared.ts`, where the route constants live */\nexport async function addPageUrl(ctx: FeatureContext, name: string, code: string) {\n const file = path.join(ctx.project.baseDir, 'lib/shared.ts');\n if (!(await addExport(ctx, file, name, code))) return false;\n\n await ctx.source(file, (source) =>\n addImport(source, { from: 'fumadocs-core/source', named: ['createGetUrl'] }),\n );\n // a second pass, so the import lands after the one above instead of before it\n if (ctx.project.i18n) {\n await ctx.source(file, (source) => addImport(source, { from: './i18n', named: ['i18n'] }));\n }\n return true;\n}\n\n/** the value of a route constant in `lib/shared.ts`, defined with `value` when missing */\nexport async function sharedRoute(ctx: FeatureContext, name: string, value: string) {\n const file = path.join(ctx.project.baseDir, 'lib/shared.ts');\n const current = await fs.readFile(path.join(ctx.project.cwd, file), 'utf-8').catch(() => '');\n const existing = new RegExp(`export const ${name}\\\\s*=\\\\s*['\"]([^'\"]*)['\"]`).exec(current)?.[1];\n if (existing !== undefined) return existing;\n // keep a blank line after the declarations already in the file\n const separator = current.trimEnd().endsWith(';') || current.length === 0 ? '' : '\\n';\n await ctx.append(file, [`${separator}export const ${name} = '${value}';`]);\n return value;\n}\n\n/** add routes to `routes.ts`, or tell the user when the config isn't an array (e.g. file-based routes) */\nexport async function registerReactRouterRoutes(\n ctx: FeatureContext,\n routes: (FormattedRoute | string)[],\n): Promise<boolean> {\n const { baseDir } = ctx.project;\n const entries = routes.map((item) =>\n typeof item === 'string' ? item : { path: item.path, entry: item.file },\n );\n let added = false;\n const edited = await ctx.source(path.join(baseDir, 'routes.ts'), (file) => {\n const first = entries[0];\n if (file.code.includes(typeof first === 'string' ? first : first.entry)) {\n added = true;\n return;\n }\n added = addReactRouterRoute(file, entries);\n });\n\n if (!edited || !added) {\n const list = entries.map((item) =>\n typeof item === 'string' ? item : `route('${item.path}', '${item.entry}')`,\n );\n ctx.note(`Register the routes in your route config:\\n ${list.join('\\n ')}`);\n }\n return added;\n}\n\n/** features reading page Markdown need the collections of Fumadocs MDX or `@fumadocs/astro`, or a runtime source exposing `page.data.content` */\nexport const requiresMarkdown = (project: Project) =>\n project.source.collections !== null ||\n project.source.dynamic ||\n 'requires Fumadocs MDX, `@fumadocs/astro` or a runtime content source';\n\n/** how generated code refers to the loader of `lib/source.ts` */\nexport interface SourceRef {\n /** resolved on demand, `lib/source.ts` exports `getSource()` instead of `source` */\n dynamic: boolean;\n /** passed to Fumadocs APIs, which accept a loader or a function resolving one */\n ref: string;\n /** reads the loader inside an async function */\n resolved: string;\n}\n\nexport function sourceRef(dynamic: boolean): SourceRef {\n return dynamic\n ? { dynamic: true, ref: 'getSource', resolved: '(await getSource())' }\n : { dynamic: false, ref: 'source', resolved: 'source' };\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as Project
|
|
2
|
-
import { i as FeatureContext, s as PackageJson } from "./index-
|
|
3
|
-
//#region src/project/route.d.ts
|
|
1
|
+
import { a as Project } from "./index-DCJbgkgf.js";
|
|
2
|
+
import { i as FeatureContext, s as PackageJson } from "./index-CAIqrY1S.js";
|
|
4
3
|
interface FormattedRoute {
|
|
5
4
|
/** route file, relative to the base dir */
|
|
6
5
|
file: string;
|
|
@@ -9,13 +8,9 @@ interface FormattedRoute {
|
|
|
9
8
|
/** path-to-regexp pattern, for the Next.js proxy matcher and rewrites */
|
|
10
9
|
pattern: string;
|
|
11
10
|
}
|
|
12
|
-
//#endregion
|
|
13
|
-
//#region src/features/utils.d.ts
|
|
14
11
|
/** the shallowest `.ts`/`.tsx` file under `dir` whose content includes `needle`, relative to `cwd` */
|
|
15
12
|
declare function findSource(cwd: string, dir: string, needle: string, match?: (file: string) => boolean): Promise<string | undefined>;
|
|
16
13
|
declare function scripts(json: PackageJson, scripts: Record<string, string>): void;
|
|
17
|
-
declare const reactOnly: (project: Project) => "Astro is not supported by this feature" | true;
|
|
18
|
-
declare function reactFramework(project: Project): ReactFramework;
|
|
19
14
|
/** POSIX path for generated code and route configs */
|
|
20
15
|
declare const posix: (file: string) => string;
|
|
21
16
|
/** add an export to a source file, unless a declaration with the same name exists */
|
|
@@ -26,8 +21,8 @@ declare function addPageUrl(ctx: FeatureContext, name: string, code: string): Pr
|
|
|
26
21
|
declare function sharedRoute(ctx: FeatureContext, name: string, value: string): Promise<string>;
|
|
27
22
|
/** add routes to `routes.ts`, or tell the user when the config isn't an array (e.g. file-based routes) */
|
|
28
23
|
declare function registerReactRouterRoutes(ctx: FeatureContext, routes: (FormattedRoute | string)[]): Promise<boolean>;
|
|
29
|
-
/** features reading page Markdown need Fumadocs MDX
|
|
30
|
-
declare const requiresMarkdown: (project: Project) => "
|
|
24
|
+
/** features reading page Markdown need the collections of Fumadocs MDX or `@fumadocs/astro`, or a runtime source exposing `page.data.content` */
|
|
25
|
+
declare const requiresMarkdown: (project: Project) => "requires Fumadocs MDX, `@fumadocs/astro` or a runtime content source" | true;
|
|
31
26
|
/** how generated code refers to the loader of `lib/source.ts` */
|
|
32
27
|
interface SourceRef {
|
|
33
28
|
/** resolved on demand, `lib/source.ts` exports `getSource()` instead of `source` */
|
|
@@ -38,6 +33,5 @@ interface SourceRef {
|
|
|
38
33
|
resolved: string;
|
|
39
34
|
}
|
|
40
35
|
declare function sourceRef(dynamic: boolean): SourceRef;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//# sourceMappingURL=utils-CxJ7kw39.d.ts.map
|
|
36
|
+
export { posix as a, scripts as c, FormattedRoute as d, findSource as i, sharedRoute as l, addExport as n, registerReactRouterRoutes as o, addPageUrl as r, requiresMarkdown as s, SourceRef as t, sourceRef as u };
|
|
37
|
+
//# sourceMappingURL=utils-DlGz_4va.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-DlGz_4va.d.ts","names":[],"sources":["../src/project/route.ts","../src/features/utils.ts"],"mappings":";;UAoBiB;;EAEf;;EAEA;;EAEA;;;iBClBoB,WACpB,aACA,aACA,gBACA,SAAQ,2BAAqC;iBAiB/B,QAAQ,MAAM,aAAa,SAAS;;cAKvC,QAAK;;iBAGI,UAAU,KAAK,gBAAgB,cAAc,cAAc,eAAY;;iBAQvE,WAAW,KAAK,gBAAgB,cAAc,eAAY;;iBAe1D,YAAY,KAAK,gBAAgB,cAAc,gBAAa;;iBAY5D,0BACpB,KAAK,gBACL,SAAS,6BACR;;cAyBU,mBAAgB,SAAa;;UAMzB;;EAEf;;EAEA;;EAEA;;iBAGc,UAAU,mBAAmB"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { b as findJsxElement, h as appendJsxChildren, p as addImport } from "./codemod-
|
|
2
|
-
import {
|
|
3
|
-
import { r as llms } from "./llms-
|
|
1
|
+
import { b as findJsxElement, h as appendJsxChildren, p as addImport } from "./codemod-Wc9nRqrw.js";
|
|
2
|
+
import { o as requiresMarkdown, r as findSource } from "./utils-Btt-BJ53.js";
|
|
3
|
+
import { r as llms } from "./llms-Cge1uefC.js";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
|
|
5
|
+
// src/features/webmcp.ts
|
|
6
6
|
const component = ({ static: isStatic, i18n, tanstack }) => `'use client';
|
|
7
7
|
import { useEffect } from 'react';
|
|
8
8
|
import { useRouter } from 'fumadocs-core/framework';
|
|
@@ -108,8 +108,7 @@ const webmcp = {
|
|
|
108
108
|
requires: [llms],
|
|
109
109
|
supports: requiresMarkdown,
|
|
110
110
|
async apply(ctx) {
|
|
111
|
-
const { cwd, baseDir, i18n } = ctx.project;
|
|
112
|
-
const framework = reactFramework(ctx.project);
|
|
111
|
+
const { cwd, baseDir, i18n, framework } = ctx.project;
|
|
113
112
|
await ctx.write(path.join(baseDir, "components/webmcp.tsx"), component({
|
|
114
113
|
static: ctx.project.static,
|
|
115
114
|
i18n: i18n !== null,
|
|
@@ -129,7 +128,6 @@ const webmcp = {
|
|
|
129
128
|
ctx.note("WebMCP is experimental, test it with the `#enable-webmcp-testing` flag of Chrome. The `search_docs` tool uses the search API route, see https://fumadocs.dev/docs/integrations/llms#webmcp.");
|
|
130
129
|
}
|
|
131
130
|
};
|
|
132
|
-
//#endregion
|
|
133
131
|
export { webmcp as n, component as t };
|
|
134
132
|
|
|
135
|
-
//# sourceMappingURL=webmcp-
|
|
133
|
+
//# sourceMappingURL=webmcp-lY3_RsR3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webmcp-lY3_RsR3.js","names":[],"sources":["../src/features/webmcp.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature } from '@/features';\nimport { addImport, appendJsxChildren, findJsxElement } from '@/codemod';\nimport { llms } from './llms';\nimport { findSource, requiresMarkdown } from './utils';\n\ninterface Input {\n static: boolean;\n i18n: boolean;\n /** the Markdown route of TanStack Start is `/docs/*.md` */\n tanstack: boolean;\n}\n\nexport const component = ({ static: isStatic, i18n, tanstack }: Input) => `'use client';\nimport { useEffect } from 'react';\nimport { useRouter } from 'fumadocs-core/framework';\nimport { ${isStatic ? 'staticClient' : 'fetchClient'} } from 'fumadocs-core/search/client/${isStatic ? 'orama-static' : 'fetch'}';\n${i18n ? \"import { useI18n } from 'fumadocs-ui/contexts/i18n';\\n\" : ''}import { ${tanstack ? 'docsRoute' : 'docsContentRoute, docsRoute'} } from '@/lib/shared';\n\ninterface ModelContextTool<Input> {\n name: string;\n description: string;\n inputSchema?: object;\n execute: (input: Input) => Promise<unknown>;\n}\n\ndeclare global {\n interface Document {\n /** WebMCP, see https://webmachinelearning.github.io/webmcp/ */\n modelContext?: {\n registerTool: <Input>(\n tool: ModelContextTool<Input>,\n options?: { signal?: AbortSignal },\n ) => Promise<void>;\n };\n }\n}\n\n/** the Markdown URL of a page, e.g. ${tanstack ? '`/docs/page` -> `/docs/page.md`' : '`/docs/page` -> `/llms.mdx/docs/page/content.md`'} */\nfunction markdownUrl(url: string) {\n${\n tanstack\n ? ' return url.endsWith(docsRoute) ? `${url}/index.md` : `${url}.md`;'\n : ` const start = url.indexOf(docsRoute);\n const slugs = url.slice(start + docsRoute.length).replace(/^\\\\//, '');\n return [url.slice(0, start) + docsContentRoute, slugs, 'content.md'].filter(Boolean).join('/');`\n}\n}\n\n/** expose the docs to AI agents in the browser */\nexport function WebMCP() {\n const router = useRouter();\n${i18n ? ' const { locale } = useI18n();\\n' : ''}\n useEffect(() => {\n const context = document.modelContext;\n if (!context) return;\n const controller = new AbortController();\n const options = { signal: controller.signal };\n const client = ${isStatic ? 'staticClient' : 'fetchClient'}(${i18n ? '{ locale }' : ''});\n\n void context.registerTool(\n {\n name: 'search_docs',\n description: 'Search the docs, returns matching pages and headings with their URLs',\n inputSchema: {\n type: 'object',\n properties: { query: { type: 'string' } },\n required: ['query'],\n },\n async execute({ query }: { query: string }) {\n return client.search(query);\n },\n },\n options,\n );\n void context.registerTool(\n {\n name: 'read_page',\n description: 'Read a docs page as Markdown by its URL',\n inputSchema: {\n type: 'object',\n properties: { url: { type: 'string' } },\n required: ['url'],\n },\n async execute({ url }: { url: string }) {\n const res = await fetch(markdownUrl(url));\n if (!res.ok) throw new Error(\\`page not found: \\${url}\\`);\n return res.text();\n },\n },\n options,\n );\n void context.registerTool(\n {\n name: 'open_page',\n description: 'Navigate to a docs page by its URL',\n inputSchema: {\n type: 'object',\n properties: { url: { type: 'string' } },\n required: ['url'],\n },\n async execute({ url }: { url: string }) {\n await router.push(url);\n return \\`opened \\${url}\\`;\n },\n },\n options,\n );\n\n return () => controller.abort();\n }, [router${i18n ? ', locale' : ''}]);\n\n return null;\n}\n`;\n\nexport const webmcp: Feature = {\n id: 'webmcp',\n title: 'WebMCP',\n description:\n 'expose tools to AI agents in the browser (experimental): search, read and open docs pages',\n requires: [llms],\n supports: requiresMarkdown,\n async apply(ctx) {\n const { cwd, baseDir, i18n, framework } = ctx.project;\n await ctx.write(\n path.join(baseDir, 'components/webmcp.tsx'),\n component({\n static: ctx.project.static,\n i18n: i18n !== null,\n tanstack: framework === 'tanstack-start',\n }),\n );\n\n const provider = await findSource(cwd, baseDir, '<RootProvider');\n const edited =\n provider !== undefined &&\n (await ctx.source(provider, (file) => {\n if (file.code.includes('<WebMCP')) return;\n const element = findJsxElement(file, 'RootProvider');\n if (!element) return;\n appendJsxChildren(file, element, '<WebMCP />');\n addImport(file, { from: '@/components/webmcp', named: ['WebMCP'] });\n }));\n if (!edited)\n ctx.note('Render `<WebMCP />` from `@/components/webmcp` inside `<RootProvider />`.');\n\n ctx.note(\n 'WebMCP is experimental, test it with the `#enable-webmcp-testing` flag of Chrome. The `search_docs` tool uses the search API route, see https://fumadocs.dev/docs/integrations/llms#webmcp.',\n );\n },\n};\n"],"mappings":";;;;;AAaA,MAAa,aAAa,EAAE,QAAQ,UAAU,MAAM,eAAsB;;;WAG/D,WAAW,iBAAiB,cAAc,uCAAuC,WAAW,iBAAiB,QAAQ;EAC9H,OAAO,2DAA2D,GAAG,WAAW,WAAW,cAAc,8BAA8B;;;;;;;;;;;;;;;;;;;;;uCAqBlG,WAAW,oCAAoC,mDAAmD;;EAGvI,WACI,wEACA;;mGAGL;;;;;;EAMC,OAAO,sCAAsC,GAAG;;;;;;qBAM7B,WAAW,iBAAiB,cAAc,GAAG,OAAO,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoD7E,OAAO,aAAa,GAAG;;;;;AAMrC,MAAa,SAAkB;CAC7B,IAAI;CACJ,OAAO;CACP,aACE;CACF,UAAU,CAAC,IAAI;CACf,UAAU;CACV,MAAM,MAAM,KAAK;EACf,MAAM,EAAE,KAAK,SAAS,MAAM,cAAc,IAAI;EAC9C,MAAM,IAAI,MACR,KAAK,KAAK,SAAS,uBAAuB,GAC1C,UAAU;GACR,QAAQ,IAAI,QAAQ;GACpB,MAAM,SAAS;GACf,UAAU,cAAc;EAC1B,CAAC,CACH;EAEA,MAAM,WAAW,MAAM,WAAW,KAAK,SAAS,eAAe;EAU/D,IAAI,EARF,aAAa,KAAA,KACZ,MAAM,IAAI,OAAO,WAAW,SAAS;GACpC,IAAI,KAAK,KAAK,SAAS,SAAS,GAAG;GACnC,MAAM,UAAU,eAAe,MAAM,cAAc;GACnD,IAAI,CAAC,SAAS;GACd,kBAAkB,MAAM,SAAS,YAAY;GAC7C,UAAU,MAAM;IAAE,MAAM;IAAuB,OAAO,CAAC,QAAQ;GAAE,CAAC;EACpE,CAAC,IAED,IAAI,KAAK,2EAA2E;EAEtF,IAAI,KACF,6LACF;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fumadocs/cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.6",
|
|
4
4
|
"description": "The CLI tool for Fumadocs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Docs",
|
|
@@ -47,18 +47,18 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@clack/prompts": "^1.8.1",
|
|
49
49
|
"@fuma-cli/interactive": "^0.1.2",
|
|
50
|
-
"cac": "^7.
|
|
50
|
+
"cac": "^7.1.0",
|
|
51
51
|
"fuma-cli": "^0.3.1",
|
|
52
|
-
"magic-string": "^1.4.
|
|
53
|
-
"oxc-parser": "^0.
|
|
52
|
+
"magic-string": "^1.4.3",
|
|
53
|
+
"oxc-parser": "^0.155.0",
|
|
54
54
|
"picocolors": "^1.1.1",
|
|
55
55
|
"tinyexec": "^1.3.1",
|
|
56
|
-
"zod": "^4.6.
|
|
56
|
+
"zod": "^4.6.7"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@types/node": "26.6.
|
|
59
|
+
"@types/node": "26.6.5",
|
|
60
60
|
"tsconfig": "0.0.0",
|
|
61
|
-
"tsdown": "0.23.
|
|
61
|
+
"tsdown": "0.23.1"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=20"
|
package/dist/ai-BoUGzPvv.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ai-BoUGzPvv.js","names":[],"sources":["../src/features/ai.ts"],"sourcesContent":["import path from 'node:path';\nimport type { ImportDeclaration } from 'oxc-parser';\nimport type { Feature, FeatureContext } from '@/features';\nimport { findSource } from './utils';\nimport { docs } from './docs';\n\nconst providers = {\n openrouter: { label: 'AI SDK', hint: 'default to OpenRouter', env: 'OPENROUTER_API_KEY' },\n llmgateway: {\n label: 'LLMGateway',\n hint: 'open-source LLM gateway, API key required',\n env: 'LLM_GATEWAY_API_KEY',\n },\n inkeep: { label: 'Inkeep AI', hint: 'API key required', env: 'INKEEP_API_KEY' },\n};\n\nexport type AIProvider = keyof typeof providers;\n\nexport const ai: Feature<{ provider: AIProvider }> = {\n id: 'ai',\n title: 'AI Chat',\n description: 'Ask AI dialog for your docs',\n requires: [docs],\n options: {\n provider: {\n message: 'Choose an AI provider',\n choices: Object.entries(providers).map(([value, { label, hint }]) => ({\n value: value as AIProvider,\n label,\n hint,\n })),\n },\n },\n supports: (project) =>\n project.static ? 'AI chat requires a server to run the chat route handler.' : true,\n async apply(ctx, { provider }) {\n await ctx.install(`ai/${provider}`);\n ctx.env(providers[provider].env, '');\n if (ctx.project.source.dynamic) await readDynamicSource(ctx);\n\n const { cwd, baseDir, info, config } = ctx.project;\n // e.g. `./components` -> `@/components/ai/layout`\n const clientLayoutImport = `@/${path.posix.join(config.aliases.componentsDir, 'ai/layout')}`;\n const layout = await findSource(cwd, path.join(baseDir, info.routesDir), '<DocsLayout');\n let layoutModule: string | undefined;\n let wired = false;\n\n if (layout !== undefined) {\n await ctx.source(layout, (file) => {\n wired = file.code.includes(`'${clientLayoutImport}'`);\n // `aiChat` takes client state, render the layout from a client component\n const declaration =\n !wired &&\n file.program.body.find(\n (node): node is ImportDeclaration =>\n node.type === 'ImportDeclaration' &&\n node.importKind !== 'type' &&\n /^fumadocs-ui\\/layouts\\/(docs|notebook|glass|spacious)$/.test(node.source.value) &&\n node.specifiers.length === 1 &&\n node.specifiers[0].local.name === 'DocsLayout',\n );\n if (!declaration) return;\n\n layoutModule = declaration.source.value;\n file.s.overwrite(\n declaration.source.start,\n declaration.source.end,\n `'${clientLayoutImport}'`,\n );\n });\n }\n\n if (layoutModule) {\n await ctx.write(\n path.join(baseDir, config.aliases.componentsDir, 'ai/layout.tsx'),\n clientLayout(layoutModule),\n );\n } else if (!wired) {\n ctx.note(\n 'Pass the AI chat to your docs layout, see https://fumadocs.dev/docs/integrations/llms#ask-ai.',\n );\n }\n ctx.note(`Set ${providers[provider].env} in \\`.env.local\\`.`);\n },\n};\n\n/** a client component rendering the docs layout with the installed chat */\nfunction clientLayout(layoutModule: string) {\n // Glass and Spacious layouts have their own trigger\n const trigger = /\\/(docs|notebook)$/.test(layoutModule);\n\n return `'use client';\nimport { DocsLayout as Layout, type DocsLayoutProps } from '${layoutModule}';\nimport { AIChat, AIChatPanel,${trigger ? ' AIChatTrigger,' : ''} useAIChat } from './search';\n\nexport function DocsLayout(props: DocsLayoutProps) {\n return (\n <AIChat>\n <ChatLayout {...props} />${trigger ? '\\n <AIChatTrigger />' : ''}\n </AIChat>\n );\n}\n\nfunction ChatLayout(props: DocsLayoutProps) {\n const { open, setOpen } = useAIChat();\n\n return <Layout {...props} aiChat={{ open, onOpenChange: setOpen, panel: <AIChatPanel /> }} />;\n}\n`;\n}\n\n/** the installed chat route indexes pages of a static `source`, runtime sources resolve on demand */\nasync function readDynamicSource(ctx: FeatureContext) {\n const { cwd, baseDir } = ctx.project;\n const route = await findSource(cwd, baseDir, 'createSearchServer');\n const edited =\n route !== undefined &&\n (await ctx.source(route, (file) => {\n file.s.replaceAll(\n \"import { source } from '@/lib/source';\",\n \"import { getSource } from '@/lib/source';\",\n );\n file.s.replaceAll('source.getPages()', '(await getSource()).getPages()');\n // every page of a runtime source carries its Markdown\n file.s.replaceAll(\" if (!('getText' in page.data)) return null;\\n\\n\", '');\n file.s.replaceAll(\"await page.data.getText('processed')\", 'page.data.content');\n }));\n\n if (!edited)\n ctx.note(\n 'Read pages from `getSource()` in the chat route, the search index is built from `page.data.content`.',\n );\n}\n"],"mappings":";;;;AAMA,MAAM,YAAY;CAChB,YAAY;EAAE,OAAO;EAAU,MAAM;EAAyB,KAAK;CAAqB;CACxF,YAAY;EACV,OAAO;EACP,MAAM;EACN,KAAK;CACP;CACA,QAAQ;EAAE,OAAO;EAAa,MAAM;EAAoB,KAAK;CAAiB;AAChF;AAIA,MAAa,KAAwC;CACnD,IAAI;CACJ,OAAO;CACP,aAAa;CACb,UAAU,CAAC,IAAI;CACf,SAAS,EACP,UAAU;EACR,SAAS;EACT,SAAS,OAAO,QAAQ,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,aAAa;GAC7D;GACP;GACA;EACF,EAAE;CACJ,EACF;CACA,WAAW,YACT,QAAQ,SAAS,6DAA6D;CAChF,MAAM,MAAM,KAAK,EAAE,YAAY;EAC7B,MAAM,IAAI,QAAQ,MAAM,UAAU;EAClC,IAAI,IAAI,UAAU,SAAS,CAAC,KAAK,EAAE;EACnC,IAAI,IAAI,QAAQ,OAAO,SAAS,MAAM,kBAAkB,GAAG;EAE3D,MAAM,EAAE,KAAK,SAAS,MAAM,WAAW,IAAI;EAE3C,MAAM,qBAAqB,KAAK,KAAK,MAAM,KAAK,OAAO,QAAQ,eAAe,WAAW;EACzF,MAAM,SAAS,MAAM,WAAW,KAAK,KAAK,KAAK,SAAS,KAAK,SAAS,GAAG,aAAa;EACtF,IAAI;EACJ,IAAI,QAAQ;EAEZ,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,OAAO,SAAS,SAAS;GACjC,QAAQ,KAAK,KAAK,SAAS,IAAI,mBAAmB,EAAE;GAEpD,MAAM,cACJ,CAAC,SACD,KAAK,QAAQ,KAAK,MACf,SACC,KAAK,SAAS,uBACd,KAAK,eAAe,UACpB,yDAAyD,KAAK,KAAK,OAAO,KAAK,KAC/E,KAAK,WAAW,WAAW,KAC3B,KAAK,WAAW,EAAE,CAAC,MAAM,SAAS,YACtC;GACF,IAAI,CAAC,aAAa;GAElB,eAAe,YAAY,OAAO;GAClC,KAAK,EAAE,UACL,YAAY,OAAO,OACnB,YAAY,OAAO,KACnB,IAAI,mBAAmB,EACzB;EACF,CAAC;EAGH,IAAI,cACF,MAAM,IAAI,MACR,KAAK,KAAK,SAAS,OAAO,QAAQ,eAAe,eAAe,GAChE,aAAa,YAAY,CAC3B;OACK,IAAI,CAAC,OACV,IAAI,KACF,+FACF;EAEF,IAAI,KAAK,OAAO,UAAU,SAAS,CAAC,IAAI,oBAAoB;CAC9D;AACF;;AAGA,SAAS,aAAa,cAAsB;CAE1C,MAAM,UAAU,qBAAqB,KAAK,YAAY;CAEtD,OAAO;8DACqD,aAAa;+BAC5C,UAAU,oBAAoB,GAAG;;;;;iCAK/B,UAAU,8BAA8B,GAAG;;;;;;;;;;;AAW5E;;AAGA,eAAe,kBAAkB,KAAqB;CACpD,MAAM,EAAE,KAAK,YAAY,IAAI;CAC7B,MAAM,QAAQ,MAAM,WAAW,KAAK,SAAS,oBAAoB;CAcjE,IAAI,EAZF,UAAU,KAAA,KACT,MAAM,IAAI,OAAO,QAAQ,SAAS;EACjC,KAAK,EAAE,WACL,0CACA,2CACF;EACA,KAAK,EAAE,WAAW,qBAAqB,gCAAgC;EAEvE,KAAK,EAAE,WAAW,yDAAyD,EAAE;EAC7E,KAAK,EAAE,WAAW,wCAAwC,mBAAmB;CAC/E,CAAC,IAGD,IAAI,KACF,sGACF;AACJ"}
|
package/dist/all-BmTB7dab.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { t as docs } from "./docs-qRGwd9Yp.js";
|
|
2
|
-
import { t as ai } from "./ai-BoUGzPvv.js";
|
|
3
|
-
import { r as llms } from "./llms-DQzAst6R.js";
|
|
4
|
-
import { t as og } from "./og-m0CwRlRz.js";
|
|
5
|
-
import { t as search } from "./search-BCHEW0-K.js";
|
|
6
|
-
import { t as mcp } from "./mcp-BpFZ_fdV.js";
|
|
7
|
-
import { n as webmcp } from "./webmcp-I9L7xzmw.js";
|
|
8
|
-
import { t as feedback } from "./feedback-DpXYMKtX.js";
|
|
9
|
-
import { t as epub } from "./epub-DbOh5Q2D.js";
|
|
10
|
-
import { t as lint } from "./lint-zkXUULgO.js";
|
|
11
|
-
//#region src/features/all.ts
|
|
12
|
-
const features = [
|
|
13
|
-
docs,
|
|
14
|
-
ai,
|
|
15
|
-
llms,
|
|
16
|
-
mcp,
|
|
17
|
-
webmcp,
|
|
18
|
-
og,
|
|
19
|
-
search,
|
|
20
|
-
feedback,
|
|
21
|
-
epub,
|
|
22
|
-
lint
|
|
23
|
-
];
|
|
24
|
-
//#endregion
|
|
25
|
-
export { features as t };
|
|
26
|
-
|
|
27
|
-
//# sourceMappingURL=all-BmTB7dab.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codemod-auol-p4R.js","names":[],"sources":["../src/codemod/shared.ts","../src/codemod/config.ts","../src/codemod/react-router.ts","../src/codemod/tanstack-start.ts","../src/codemod/source.ts","../src/codemod/next-proxy.ts"],"sourcesContent":["import {\n parseSync,\n type ArrayExpression,\n type ExportDefaultDeclaration,\n type ImportDeclaration,\n type JSXElement,\n type JSXOpeningElement,\n type Node,\n type ObjectExpression,\n type ObjectProperty,\n type Program,\n type Span,\n} from 'oxc-parser';\nimport MagicString from 'magic-string';\nimport fs from 'node:fs/promises';\n\nexport type NodeOfType<T extends Node['type']> = Extract<Node, { type: T }>;\n\nexport interface SourceFile {\n path: string;\n /** the original content */\n code: string;\n program: Program;\n /** the edited content */\n s: MagicString;\n save(): Promise<void>;\n}\n\nexport function parseSourceFile(path: string, code: string): SourceFile {\n const { program, errors } = parseSync(path, code);\n if (errors.length > 0) throw new Error(`Failed to parse ${path}: ${errors[0].message}`);\n const s = new MagicString(code);\n\n return {\n path,\n code,\n program,\n s,\n save: () => fs.writeFile(path, s.toString()),\n };\n}\n\nexport async function createSourceFile(path: string): Promise<SourceFile> {\n return parseSourceFile(path, await fs.readFile(path, 'utf-8'));\n}\n\n/** the value of a string literal, or a template literal without expressions */\nexport function getStringValue(node: Node): string | undefined {\n if (node.type === 'Literal' && typeof node.value === 'string') return node.value;\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0)\n return node.quasis[0].value.cooked ?? undefined;\n}\n\nexport function* descendants(node: Node): Generator<Node> {\n for (const key in node) {\n if (key === 'parent') continue;\n const value = (node as unknown as Record<string, unknown>)[key];\n for (const child of Array.isArray(value) ? value : [value]) {\n if (child && typeof child === 'object' && 'type' in child) {\n yield child as Node;\n yield* descendants(child as Node);\n }\n }\n }\n}\n\nexport function find<T extends Node['type']>(\n node: Node,\n type: T,\n predicate?: (node: NodeOfType<T>) => boolean,\n): NodeOfType<T> | undefined {\n for (const child of descendants(node)) {\n if (child.type === type && (!predicate || predicate(child as NodeOfType<T>)))\n return child as NodeOfType<T>;\n }\n}\n\nexport function getDefaultExport(file: SourceFile): ExportDefaultDeclaration | undefined {\n return file.program.body.find((node) => node.type === 'ExportDefaultDeclaration');\n}\n\nexport function findJsxElement(file: SourceFile, tagName: string): JSXElement | undefined {\n return find(file.program, 'JSXElement', ({ openingElement: { name } }) => {\n return name.type === 'JSXIdentifier' && name.name === tagName;\n });\n}\n\nexport function addJsxAttribute(file: SourceFile, element: JSXOpeningElement, attribute: string) {\n const { code, s } = file;\n const anchor = element.attributes.at(-1) ?? element.name;\n const multiline = code.slice(element.start, element.end).includes('\\n');\n s.appendLeft(\n anchor.end,\n `${multiline ? `\\n${lineIndent(code, anchor.start)}` : ' '}${attribute}`,\n );\n}\n\n/**\n * Insert JSX before the children of the element, re-indenting the children\n */\nexport function prependJsxChildren(file: SourceFile, element: JSXElement, jsx: string) {\n const { closingElement, openingElement } = element;\n if (!closingElement) return;\n const { code, s } = file;\n const indent = lineIndent(code, element.start);\n const prior = dedent(code.slice(openingElement.end, closingElement.start));\n const body = `${jsx}\\n\\n${prior}`.replaceAll(/^(?=.)/gm, `${indent} `);\n s.overwrite(openingElement.end, closingElement.start, `\\n${body}\\n${indent}`);\n}\n\n/**\n * Insert JSX after the children of the element\n */\nexport function appendJsxChildren(file: SourceFile, element: JSXElement, jsx: string) {\n const { closingElement } = element;\n if (!closingElement) return;\n const { code, s } = file;\n const indent = lineIndent(code, element.start);\n const body = jsx.replaceAll(/^(?=.)/gm, `${indent} `);\n // children end with the indentation of the closing tag\n s.appendLeft(closingElement.start - indent.length, `${body}\\n`);\n}\n\n/** trim, and remove the common indentation */\nfunction dedent(text: string): string {\n const lines = text.trim().split('\\n');\n let indent: string | undefined;\n for (const line of lines.slice(1)) {\n if (line.trim().length === 0) continue;\n const current = /^[ \\t]*/.exec(line)![0];\n if (indent === undefined || current.length < indent.length) indent = current;\n }\n if (!indent) return lines.join('\\n');\n return lines\n .map((line, i) => (i > 0 && line.startsWith(indent) ? line.slice(indent.length) : line))\n .join('\\n');\n}\n\nexport function getProperty(object: ObjectExpression, name: string): ObjectProperty | undefined {\n for (const property of object.properties) {\n if (property.type !== 'Property') continue;\n const { key } = property;\n if (\n key.type === 'Identifier' ? key.name === name : key.type === 'Literal' && key.value === name\n )\n return property;\n }\n}\n\n/** indentation of the line containing `index` */\nexport function lineIndent(code: string, index: number): string {\n const start = code.lastIndexOf('\\n', index - 1) + 1;\n return /^[ \\t]*/.exec(code.slice(start, index))![0];\n}\n\nexport function addImport(\n file: SourceFile,\n declaration: { from: string; default?: string; named?: string[] },\n) {\n const { s, program } = file;\n const existing = program.body.find(\n (node): node is ImportDeclaration =>\n node.type === 'ImportDeclaration' &&\n node.importKind !== 'type' &&\n node.source.value === declaration.from,\n );\n\n if (existing && !declaration.default && declaration.named) {\n const imported = new Set<string>();\n let last: Span | undefined;\n for (const spec of existing.specifiers) {\n if (spec.type !== 'ImportSpecifier') continue;\n imported.add(spec.local.name);\n last = spec;\n }\n\n const missing = declaration.named.filter((name) => !imported.has(name));\n if (last && missing.length > 0) s.appendLeft(last.end, `, ${missing.join(', ')}`);\n if (last) return;\n }\n\n const parts: string[] = [];\n if (declaration.default) parts.push(declaration.default);\n if (declaration.named?.length) parts.push(`{ ${declaration.named.join(', ')} }`);\n const statement = `import ${parts.join(', ')} from '${declaration.from}';`;\n\n const last = program.body.findLast((node) => node.type === 'ImportDeclaration');\n if (last) s.appendLeft(last.end, `\\n${statement}`);\n else s.prepend(`${statement}\\n\\n`);\n}\n\ntype Container = ArrayExpression | ObjectExpression;\ntype ElementOf<C extends Container> = C extends ArrayExpression\n ? NonNullable<ArrayExpression['elements'][number]>\n : ObjectExpression['properties'][number];\n\nfunction getElements<C extends Container>(container: C): ElementOf<C>[] {\n const elements = container.type === 'ArrayExpression' ? container.elements : container.properties;\n return elements.filter((element) => element !== null) as ElementOf<C>[];\n}\n\n/**\n * Append items to an array or object literal, following its formatting\n */\nexport function addElements(file: SourceFile, container: Container, items: string[]) {\n if (items.length === 0) return;\n const { code, s } = file;\n const last = getElements(container).at(-1);\n const multiline = code.slice(container.start, container.end).includes('\\n');\n\n if (!last) {\n // `[]` is a zero-length range which cannot be overwritten\n if (container.end - container.start === 2) s.appendLeft(container.end - 1, items.join(', '));\n else s.overwrite(container.start + 1, container.end - 1, items.join(', '));\n return;\n }\n\n if (!multiline) {\n s.appendLeft(last.end, items.map((item) => `, ${item}`).join(''));\n return;\n }\n\n const indent = lineIndent(code, last.start);\n s.appendLeft(\n last.end,\n items.map((item) => `,\\n${indent}${item.replaceAll('\\n', `\\n${indent}`)}`).join(''),\n );\n}\n\n/**\n * Remove the items of an array or object literal that don't pass the filter\n */\nexport function filterElements<C extends Container>(\n file: SourceFile,\n container: C,\n keep: (element: ElementOf<C>) => boolean,\n) {\n const { s } = file;\n const elements = getElements(container);\n const keeps = elements.map(keep);\n let kept = 0;\n for (const keep of keeps) if (keep) kept++;\n if (kept === elements.length) return;\n if (kept === 0) {\n s.remove(container.start + 1, container.end - 1);\n return;\n }\n\n let lastKept: ElementOf<C> | undefined;\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (keeps[i]) {\n lastKept = element;\n continue;\n }\n const next = elements[i + 1];\n // remove along with the separator: up to the next item, or from the last kept item\n if (next) s.remove(element.start, next.start);\n else s.remove(lastKept!.end, element.end);\n }\n}\n","import type { Expression, ObjectExpression } from 'oxc-parser';\nimport {\n addElements,\n addImport,\n find,\n getDefaultExport,\n getProperty,\n type SourceFile,\n} from './shared';\n\n/**\n * The options object of a config file, e.g. the argument of `defineConfig({ ... })`, or the object in `export default { ... }`\n */\nexport function getConfigObject(file: SourceFile): ObjectExpression | undefined {\n const exported = getDefaultExport(file);\n if (!exported) return;\n let expr = exported.declaration as Expression;\n\n while (true) {\n if (expr.type === 'ObjectExpression') return expr;\n if (expr.type === 'TSSatisfiesExpression' || expr.type === 'TSAsExpression') {\n expr = expr.expression;\n } else if (expr.type === 'CallExpression' && expr.arguments.length > 0) {\n const arg = expr.arguments[0];\n if (arg.type === 'SpreadElement') return;\n expr = arg;\n } else if (expr.type === 'Identifier') {\n const { name } = expr;\n const init = find(\n file.program,\n 'VariableDeclarator',\n (node) => node.id.type === 'Identifier' && node.id.name === name,\n )?.init;\n if (!init) return;\n expr = init;\n } else {\n return;\n }\n }\n}\n\n/**\n * Add a plugin call to the `plugins` array of a Vite config, skip if the same plugin is already added.\n *\n * @param path - property path to the plugins array, e.g. `['vite', 'plugins']` for Waku\n */\nexport function addVitePlugin(\n file: SourceFile,\n plugin: { name: string; from: string; call?: string },\n path: string[] = ['plugins'],\n): boolean {\n let object = getConfigObject(file);\n for (let i = 0; object && i < path.length - 1; i++) {\n let value = getProperty(object, path[i])?.value;\n // e.g. `vite: { ... } satisfies UserConfig as Config['vite']`\n while (value?.type === 'TSSatisfiesExpression' || value?.type === 'TSAsExpression')\n value = value.expression;\n object = value?.type === 'ObjectExpression' ? value : undefined;\n }\n if (!object) return false;\n\n const call = plugin.call ?? `${plugin.name}()`;\n const plugins = getProperty(object, path[path.length - 1])?.value;\n if (!plugins) {\n addElements(file, object, [`${path[path.length - 1]}: [${call}]`]);\n } else if (plugins.type === 'ArrayExpression') {\n for (const element of plugins.elements) {\n if (\n element?.type === 'CallExpression' &&\n element.callee.type === 'Identifier' &&\n element.callee.name === plugin.name\n )\n return true;\n }\n addElements(file, plugins, [call]);\n } else {\n return false;\n }\n\n addImport(file, { from: plugin.from, named: [plugin.name] });\n return true;\n}\n\n/**\n * Wrap the default export of `next.config.*` with `createMDX()`\n */\nexport function wrapNextConfig(file: SourceFile): boolean {\n if (file.code.includes('fumadocs-mdx/next')) return true;\n const exported = getDefaultExport(file);\n if (!exported) return false;\n const { declaration, start } = exported;\n if (declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration')\n return false;\n\n file.s.prependLeft(start, `const withMDX = createMDX();\\n\\n`);\n file.s.appendLeft(declaration.start, 'withMDX(');\n file.s.appendRight(declaration.end, ')');\n addImport(file, { from: 'fumadocs-mdx/next', named: ['createMDX'] });\n return true;\n}\n\n/**\n * Add rewrites to a Next.js config, skip if `rewrites` is already defined\n */\nexport function addNextRewrites(\n file: SourceFile,\n rewrites: { source: string; destination: string }[],\n): boolean {\n const config = getConfigObject(file);\n if (!config) return false;\n if (getProperty(config, 'rewrites')) return false;\n\n const items = rewrites.map(\n ({ source, destination }) =>\n ` {\\n source: '${source}',\\n destination: '${destination}',\\n },`,\n );\n addElements(file, config, [`async rewrites() {\\n return [\\n${items.join('\\n')}\\n ];\\n}`]);\n return true;\n}\n","import type { ArrayExpression, ObjectProperty } from 'oxc-parser';\nimport {\n addElements,\n filterElements,\n find,\n getStringValue,\n getDefaultExport,\n getProperty,\n type SourceFile,\n} from '@/codemod/shared';\n\n/**\n * filter items in a specific array initializer in the prerender function\n */\nexport function filterReactRouterPrerenderArray(\n file: SourceFile,\n array: 'paths' | 'excluded',\n filter: (item: string) => boolean,\n) {\n const initializer = getPrerenderArray(file, array);\n if (!initializer) return;\n\n filterElements(file, initializer, (element) => {\n const value = getStringValue(element);\n return value === undefined || filter(value);\n });\n}\n\n/**\n * add items to a specific array initializer in the prerender function, skip existing ones\n */\nexport function addReactRouterPrerenderArray(\n file: SourceFile,\n array: 'paths' | 'excluded',\n items: string[],\n) {\n const initializer = getPrerenderArray(file, array);\n if (!initializer) return;\n\n const existing = new Set<string>();\n for (const element of initializer.elements) {\n const value = element ? getStringValue(element) : undefined;\n if (value !== undefined) existing.add(value);\n }\n addElements(\n file,\n initializer,\n items.filter((item) => !existing.has(item)).map((item) => `'${item}'`),\n );\n}\n\nfunction getPrerenderArray(file: SourceFile, array: string): ArrayExpression | undefined {\n const method = getPrerenderMethod(file);\n if (!method) return;\n\n const initializer = find(\n method.value,\n 'VariableDeclarator',\n (item) => item.id.type === 'Identifier' && item.id.name === array,\n )?.init;\n if (initializer?.type === 'ArrayExpression') return initializer;\n}\n\n/**\n * Add new routes to route config, an item can be raw code (e.g. `layout(...)`)\n */\nexport function addReactRouterRoute(\n file: SourceFile,\n routes: ({ path: string; entry: string } | string)[],\n): boolean {\n return modifyReactRouterRoutes(file, (arr) => {\n addElements(\n file,\n arr,\n routes.map((item) =>\n typeof item === 'string' ? item : `route('${item.path}', '${item.entry}')`,\n ),\n );\n });\n}\n\n/**\n * Remove routes from route config (root level only)\n */\nexport function filterReactRouterRoute(\n file: SourceFile,\n filter: (item: { path: string; entry: string }) => boolean,\n) {\n modifyReactRouterRoutes(file, (arr) => {\n filterElements(file, arr, (element) => {\n if (element.type !== 'CallExpression') return true;\n const { callee, arguments: args } = element;\n if (callee.type !== 'Identifier' || callee.name !== 'route') return true;\n\n const path = getStringValue(args[0]);\n const entry = getStringValue(args[1]);\n return path === undefined || entry === undefined || filter({ path, entry });\n });\n });\n}\n\n/** @returns `false` if the route config isn't an array literal (e.g. file-based routes) */\nfunction modifyReactRouterRoutes(file: SourceFile, mod: (array: ArrayExpression) => void): boolean {\n const exported = getDefaultExport(file);\n const initializer = exported && find(exported, 'ArrayExpression');\n if (!initializer) return false;\n mod(initializer);\n return true;\n}\n\n/**\n * Find the prerender method from the config\n */\nfunction getPrerenderMethod(file: SourceFile): ObjectProperty | undefined {\n const exported = getDefaultExport(file);\n const options = exported && find(exported, 'ObjectExpression');\n const property = options && getProperty(options, 'prerender');\n if (property?.method) return property;\n}\n","import type { CallExpression } from 'oxc-parser';\nimport {\n addElements,\n find,\n getStringValue,\n getDefaultExport,\n getProperty,\n type SourceFile,\n} from '@/codemod/shared';\n\n/**\n * Add path to the `pages` array in tanstack start vite config.\n *\n * If the `pages` property doesn't exist, create one.\n */\nexport function addTanstackPrerender(file: SourceFile, paths: string[]) {\n const options = getTanstackStartCall(file)?.arguments[0];\n if (options?.type !== 'ObjectExpression') return;\n\n const toItem = (path: string) => `{ path: '${path}' }`;\n const pages = getProperty(options, 'pages')?.value;\n if (!pages) {\n addElements(file, options, [\n `pages: [\\n${paths.map((path) => ` ${toItem(path)}`).join(',\\n')}\\n]`,\n ]);\n return;\n }\n if (pages.type !== 'ArrayExpression') return;\n\n const existingPaths = new Set<string>();\n for (const element of pages.elements) {\n const value = element?.type === 'ObjectExpression' && getProperty(element, 'path')?.value;\n const path = value ? getStringValue(value) : undefined;\n if (path !== undefined) existingPaths.add(path);\n }\n addElements(file, pages, paths.filter((path) => !existingPaths.has(path)).map(toItem));\n}\n\n/**\n * Find the tanstackStart call expression\n */\nfunction getTanstackStartCall(file: SourceFile): CallExpression | undefined {\n const exported = getDefaultExport(file);\n const options = exported && find(exported, 'ObjectExpression');\n const plugins = options && getProperty(options, 'plugins')?.value;\n if (plugins?.type !== 'ArrayExpression') return;\n\n for (const element of plugins.elements) {\n if (\n element?.type === 'CallExpression' &&\n element.callee.type === 'Identifier' &&\n element.callee.name === 'tanstackStart'\n )\n return element;\n }\n}\n","import { addElements, find, getProperty, type SourceFile } from './shared';\n\n/**\n * Enable `includeProcessedMarkdown` on the `defineDocs()` call\n *\n * @returns `false` if the call cannot be found\n */\nexport function enableProcessedMarkdown(file: SourceFile): boolean {\n const call = find(\n file.program,\n 'CallExpression',\n (node) => node.callee.type === 'Identifier' && node.callee.name === 'defineDocs',\n );\n const options = call?.arguments[0];\n if (options?.type !== 'ObjectExpression') return false;\n const flag = 'includeProcessedMarkdown: true';\n\n const docs = getProperty(options, 'docs')?.value;\n if (!docs) {\n addElements(file, options, [`docs: {\\n postprocess: {\\n ${flag},\\n },\\n}`]);\n return true;\n }\n if (docs.type !== 'ObjectExpression') return false;\n\n const postprocess = getProperty(docs, 'postprocess')?.value;\n if (!postprocess) {\n addElements(file, docs, [`postprocess: {\\n ${flag},\\n}`]);\n return true;\n }\n if (postprocess.type !== 'ObjectExpression') return false;\n\n const existing = getProperty(postprocess, 'includeProcessedMarkdown')?.value;\n if (!existing) addElements(file, postprocess, [flag]);\n else if (existing.type === 'Literal' && existing.value === false)\n file.s.overwrite(existing.start, existing.end, 'true');\n return true;\n}\n","import { addElements, find, getProperty, type SourceFile } from './shared';\n\n/**\n * Add patterns to the `matcher` of `export const config` in a Next.js proxy (middleware) file\n */\nexport function addProxyMatcher(file: SourceFile, patterns: string[]): boolean {\n const config = find(\n file.program,\n 'VariableDeclarator',\n (node) => node.id.type === 'Identifier' && node.id.name === 'config',\n )?.init;\n if (config?.type !== 'ObjectExpression') return false;\n const matcher = getProperty(config, 'matcher')?.value;\n if (matcher?.type !== 'ArrayExpression') return false;\n\n const existing = new Set<string>();\n for (const element of matcher.elements) {\n if (element?.type === 'Literal' && typeof element.value === 'string')\n existing.add(element.value);\n }\n addElements(\n file,\n matcher,\n patterns.filter((pattern) => !existing.has(pattern)).map((pattern) => `'${pattern}'`),\n );\n return true;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-Bfg4yG8S.d.ts","names":[],"sources":["../src/config.ts"],"mappings":";;cAMM;KACM,oBAAoB;iBAwBV,mBAAmB,eAAmB,QAAA,EAAA;;;;;;;;;;;;;;;;;;;GAgD3D,EAAA,KAAA;KAEI,eAAe,QAAQ,kBAAkB;KAElC,cAAc,EAAE,MAAM;KACtB,eAAe,EAAE,OAAO;iBAEd,mBAAmB,gBAAsB,QAAQ;;;;;;iBAejD,WAAW,gBAAsB,QAAQ;iBAezC,iBAAiB,eAAY"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-Bw0kmF01.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { z } from 'zod';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { detectFramework } from 'fuma-cli/detect';\n\nconst frameworks = ['next', 'astro', 'waku', 'react-router', 'tanstack-start'] as const;\nexport type Framework = (typeof frameworks)[number];\n\nfunction isSupportedFramework(v: string): v is Framework {\n return frameworks.includes(v as Framework);\n}\n\n/** directories from shadcn's `components.json`, so components are installed at the same place */\nasync function readShadcnAliases(cwd: string): Promise<Record<string, string>> {\n const content = await fs.readFile(path.join(cwd, 'components.json'), 'utf-8').catch(() => null);\n if (!content) return {};\n const out: Record<string, string> = {};\n let aliases: Record<string, unknown> = {};\n try {\n aliases = JSON.parse(content).aliases ?? {};\n } catch {\n return out;\n }\n for (const [key, alias] of Object.entries(aliases)) {\n // e.g. `@/components/ui` -> `./components/ui`\n if (typeof alias === 'string') out[key] = `.${alias.slice(alias.indexOf('/'))}`;\n }\n return out;\n}\n\nexport async function createConfigSchema(cwd = process.cwd()) {\n const shadcn = await readShadcnAliases(cwd);\n const defaultAliases = {\n uiDir: shadcn.ui ?? './components/ui',\n componentsDir: shadcn.components ?? './components',\n layoutDir: './layouts',\n cssDir: './styles',\n libDir: shadcn.lib ?? './lib',\n utils: shadcn.utils,\n };\n\n let framework = await detectFramework(cwd);\n if (!isSupportedFramework(framework)) framework = 'next';\n\n return z.object({\n $schema: z.string().default('node_modules/@fumadocs/cli/dist/schema.json').optional(),\n aliases: z\n .object({\n uiDir: z.string().default(defaultAliases.uiDir),\n componentsDir: z.string().default(defaultAliases.componentsDir),\n layoutDir: z.string().default(defaultAliases.layoutDir),\n cssDir: z.string().default(defaultAliases.cssDir),\n libDir: z.string().default(defaultAliases.libDir),\n /** module exporting `cn`, installed components import it instead of `lib/cn.ts` */\n utils: shadcn.utils ? z.string().default(shadcn.utils) : z.string().optional(),\n })\n .default(defaultAliases),\n\n baseDir: z.string().default(() => {\n if (framework === 'react-router') return 'app';\n // the routes directory of the framework decides whether app code lives in `src`\n const routes = { next: 'app', waku: 'pages', 'tanstack-start': 'routes' }[framework] ?? '';\n if (existsSync(path.resolve(cwd, 'src', routes))) return 'src';\n if (routes && existsSync(path.resolve(cwd, routes))) return '';\n return existsSync(path.resolve(cwd, 'src')) ? 'src' : '';\n }),\n uiLibrary: z.enum(['radix-ui', 'base-ui']).default('base-ui'),\n framework: z.literal(frameworks).default(framework),\n\n commands: z\n .object({\n /**\n * command to format output code automatically\n */\n format: z.string().optional(),\n })\n .default({}),\n });\n}\n\ntype ConfigSchema = Awaited<ReturnType<typeof createConfigSchema>>;\n\nexport type ConfigInput = z.input<ConfigSchema>;\nexport type LoadedConfig = z.output<ConfigSchema>;\n\nexport async function createOrLoadConfig(file = './cli.json'): Promise<LoadedConfig> {\n const inited = await initConfig(file);\n if (inited) return inited;\n\n const content = await fs.readFile(file, 'utf-8');\n const configSchema = await createConfigSchema();\n\n return configSchema.parse(JSON.parse(content));\n}\n\n/**\n * Write new config, skip if a config already exists\n *\n * @returns the created config, `undefined` if not created\n */\nexport async function initConfig(file = './cli.json'): Promise<LoadedConfig | undefined> {\n if (\n await fs\n .stat(file)\n .then(() => true)\n .catch(() => false)\n ) {\n return;\n }\n\n const defaultConfig = await getDefaultConfig();\n await fs.writeFile(file, JSON.stringify(defaultConfig, null, 2));\n return defaultConfig;\n}\n\nexport async function getDefaultConfig(cwd?: string) {\n const schema = await createConfigSchema(cwd);\n return schema.parse({} satisfies ConfigInput);\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"docs-qRGwd9Yp.js","names":[],"sources":["../src/project/route.ts","../src/features/docs/templates.ts","../src/features/docs/index.ts"],"sourcesContent":["import type { I18nInfo, ReactFramework } 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 conventions: Record<ReactFramework, 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: (name, catchAll, _optional, suffix) =>\n catchAll ? `[...${name}]${suffix ? `/${suffix}` : ''}` : `[${name}]`,\n file: (segments, ext) => `pages/_api/${segments.join('/')}.${ext}`,\n path: (segments) => `/${segments.join('/')}`,\n },\n};\n\n/** the locale segment of route files, with a trailing slash */\nexport function localeSegment(framework: ReactFramework, i18n: I18nInfo | null) {\n return i18n ? `${conventions[framework].locale(i18n.optionalLocale)}/` : '';\n}\n\nexport function formatRoute(\n route: RouteDescriptor,\n framework: ReactFramework,\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: 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(\n framework: ReactFramework,\n route: FormattedRoute,\n mod: RouteModule,\n): 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 === '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/** 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 { I18nInfo, ReactFramework } from '@/project';\nimport { 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: boolean) =>\n i18n\n ? `import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';\nimport { uiTranslations } from 'fumadocs-ui/i18n';\nimport { i18n } from '@/lib/i18n';\n\nexport const translations = i18n.translations().extend(uiTranslations());\n\nexport function baseOptions(locale: string = i18n.defaultLanguage): BaseLayoutProps {\n return {\n nav: {\n title: 'My App',\n url: \\`/\\${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(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';\nimport { i18n } from '@/lib/i18n';\n`\n : ''\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<ReactFramework, 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)}\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)}\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)}\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\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 wrapNextConfig,\n} from '@/codemod';\nimport { exists } from '@/utils/fs';\nimport { localeSegment } from '@/project/route';\nimport { nextProxy, sampleContent, sampleContentCn, templates } from './templates';\nimport { reactFramework, reactOnly, 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 supports: reactOnly,\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 } = project;\n const framework = reactFramework(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-mdx': null,\n 'fumadocs-ui': config.uiLibrary === 'base-ui' ? 'npm:@fumadocs/base-ui' : null,\n });\n ctx.addDependencies({ '@types/mdx': null }, true);\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 await ctx.append('.gitignore', ['.source']);\n if (i18n && framework === 'next') await configureProxy(ctx);\n\n await configureBundler(ctx);\n await configureCss(ctx);\n 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 (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\nasync function configureBundler(ctx: FeatureContext) {\n const { framework, configFile } = ctx.project;\n const edited =\n configFile !== undefined &&\n (await ctx\n .source(configFile, (file) => {\n const ok =\n framework === 'next'\n ? wrapNextConfig(file)\n : addVitePlugin(\n file,\n { name: 'fumadocsMdx', from: 'fumadocs-mdx/vite' },\n framework === 'waku' ? ['vite', 'plugins'] : ['plugins'],\n );\n if (!ok) throw new Error(`cannot update ${configFile}`);\n })\n .catch(() => false));\n\n if (!edited) {\n ctx.note(\n framework === 'next'\n ? \"Wrap your Next.js config with `createMDX()` from 'fumadocs-mdx/next', see https://fumadocs.dev/docs/mdx/next.\"\n : \"Add `fumadocsMdx()` from 'fumadocs-mdx/vite' to the Vite plugins of your config file.\",\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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"epub-DbOh5Q2D.js","names":[],"sources":["../src/features/epub.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature } from '@/features';\nimport type { ReactFramework } from '@/project';\nimport { type FormattedRoute, formatRoute, routeModule } from '@/project/route';\nimport { addReactRouterPrerenderArray, enableProcessedMarkdown } from '@/codemod';\nimport { docs } from './docs';\nimport {\n reactFramework,\n registerReactRouterRoutes,\n requiresMarkdown,\n type SourceRef,\n sourceRef,\n} from './utils';\n\nconst api = ({ dynamic, ref }: SourceRef) => `import { ${ref} } from '@/lib/source';\nimport { createEpubExportAPI } from 'fumadocs-epub';\n\nconst api = createEpubExportAPI({\n ${ref === 'source' ? 'source' : `source: ${ref}`},${dynamic ? '\\n getMarkdown: (page) => page.data.content,' : ''}\n title: 'Documentation',\n author: 'Your Team',\n description: 'Exported documentation',\n // guards the route, requests must send \\`Authorization: Bearer <secret>\\`\n secret: process.env.EXPORT_SECRET,\n});\n`;\n\nconst templates = (framework: ReactFramework, route: FormattedRoute, src: SourceRef) =>\n routeModule(framework, route, {\n imports: api(src),\n body: 'api.GET(request)',\n request: true,\n revalidate: true,\n });\n\nexport const epub: Feature = {\n id: 'epub',\n title: 'EPUB Export',\n description: 'a route to export docs as an EPUB file, protected by EXPORT_SECRET',\n requires: [docs],\n supports: (project) =>\n project.static ? 'the export route needs a server at runtime' : requiresMarkdown(project),\n async apply(ctx) {\n const { baseDir, source } = ctx.project;\n const framework = reactFramework(ctx.project);\n ctx.addDependencies({ 'fumadocs-epub': null });\n\n if (source.collections) {\n await ctx.source(source.collections, (file) => {\n if (!enableProcessedMarkdown(file))\n throw new Error(`cannot find \\`defineDocs()\\` in ${source.collections}`);\n });\n }\n\n const route = formatRoute({ segments: ['export/epub'] }, framework, null);\n await ctx.write(\n path.join(baseDir, route.file),\n templates(framework, route, sourceRef(ctx.project.source.dynamic)),\n );\n if (framework === 'react-router') {\n await registerReactRouterRoutes(ctx, [route]);\n // the route needs a secret at runtime\n await ctx.source('react-router.config.ts', (file) => {\n addReactRouterPrerenderArray(file, 'excluded', [`/${route.path}`]);\n });\n }\n\n ctx.env('EXPORT_SECRET', '');\n ctx.note(\n 'Set EXPORT_SECRET in `.env.local`, then export with `fumadocs export epub` after a production build.',\n );\n },\n};\n"],"mappings":";;;;;AAcA,MAAM,OAAO,EAAE,SAAS,UAAqB,YAAY,IAAI;;;;IAIzD,QAAQ,WAAW,WAAW,WAAW,MAAM,GAAG,UAAU,kDAAkD,GAAG;;;;;;;;AASrH,MAAM,aAAa,WAA2B,OAAuB,QACnE,YAAY,WAAW,OAAO;CAC5B,SAAS,IAAI,GAAG;CAChB,MAAM;CACN,SAAS;CACT,YAAY;AACd,CAAC;AAEH,MAAa,OAAgB;CAC3B,IAAI;CACJ,OAAO;CACP,aAAa;CACb,UAAU,CAAC,IAAI;CACf,WAAW,YACT,QAAQ,SAAS,+CAA+C,iBAAiB,OAAO;CAC1F,MAAM,MAAM,KAAK;EACf,MAAM,EAAE,SAAS,WAAW,IAAI;EAChC,MAAM,YAAY,eAAe,IAAI,OAAO;EAC5C,IAAI,gBAAgB,EAAE,iBAAiB,KAAK,CAAC;EAE7C,IAAI,OAAO,aACT,MAAM,IAAI,OAAO,OAAO,cAAc,SAAS;GAC7C,IAAI,CAAC,wBAAwB,IAAI,GAC/B,MAAM,IAAI,MAAM,mCAAmC,OAAO,aAAa;EAC3E,CAAC;EAGH,MAAM,QAAQ,YAAY,EAAE,UAAU,CAAC,aAAa,EAAE,GAAG,WAAW,IAAI;EACxE,MAAM,IAAI,MACR,KAAK,KAAK,SAAS,MAAM,IAAI,GAC7B,UAAU,WAAW,OAAO,UAAU,IAAI,QAAQ,OAAO,OAAO,CAAC,CACnE;EACA,IAAI,cAAc,gBAAgB;GAChC,MAAM,0BAA0B,KAAK,CAAC,KAAK,CAAC;GAE5C,MAAM,IAAI,OAAO,2BAA2B,SAAS;IACnD,6BAA6B,MAAM,YAAY,CAAC,IAAI,MAAM,MAAM,CAAC;GACnE,CAAC;EACH;EAEA,IAAI,IAAI,iBAAiB,EAAE;EAC3B,IAAI,KACF,sGACF;CACF;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"feedback-DpXYMKtX.js","names":[],"sources":["../src/features/feedback.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature } from '@/features';\nimport { addImport, appendJsxChildren, findJsxElement } from '@/codemod';\nimport { docs } from './docs';\nimport { findSource } from './utils';\n\nexport const feedback: Feature = {\n id: 'feedback',\n title: 'Feedback',\n description: 'let readers rate docs pages, with a component to send feedback',\n requires: [docs],\n async apply(ctx) {\n const { cwd, baseDir, info, framework, static: isStatic } = ctx.project;\n await ctx.install('feedback');\n\n // server actions are only available on RSC frameworks, static export has none\n const serverAction = framework === 'next' || framework === 'waku';\n const page = await findSource(cwd, path.join(baseDir, info.routesDir), '<DocsPage');\n const edited =\n page !== undefined &&\n !(serverAction && isStatic) &&\n (await ctx.source(page, (file) => {\n if (file.code.includes('<Feedback')) return;\n const element = findJsxElement(file, 'DocsPage');\n if (!element) return;\n\n appendJsxChildren(\n file,\n element,\n `<Feedback\n onSendAction={async (feedback) => {${serverAction ? \"\\n 'use server';\\n\" : ''}\n console.log(feedback);\n return {};\n }}\n/>`,\n );\n addImport(file, { from: '@/components/feedback/client', named: ['Feedback'] });\n }));\n\n if (!edited) {\n ctx.note(\n serverAction && isStatic\n ? 'Static export has no server actions, render `<Feedback />` from `@/components/feedback/client` in a client component and send the feedback to your API in `onSendAction`.'\n : 'Add `<Feedback />` from `@/components/feedback/client` to the bottom of your docs page.',\n );\n }\n ctx.note(\n 'Handle the feedback in `onSendAction`, e.g. report it to GitHub Discussions: https://fumadocs.dev/docs/integrations/feedback.',\n );\n },\n};\n"],"mappings":";;;;;AAMA,MAAa,WAAoB;CAC/B,IAAI;CACJ,OAAO;CACP,aAAa;CACb,UAAU,CAAC,IAAI;CACf,MAAM,MAAM,KAAK;EACf,MAAM,EAAE,KAAK,SAAS,MAAM,WAAW,QAAQ,aAAa,IAAI;EAChE,MAAM,IAAI,QAAQ,UAAU;EAG5B,MAAM,eAAe,cAAc,UAAU,cAAc;EAC3D,MAAM,OAAO,MAAM,WAAW,KAAK,KAAK,KAAK,SAAS,KAAK,SAAS,GAAG,WAAW;EAsBlF,IAAI,EApBF,SAAS,KAAA,KACT,EAAE,gBAAgB,aACjB,MAAM,IAAI,OAAO,OAAO,SAAS;GAChC,IAAI,KAAK,KAAK,SAAS,WAAW,GAAG;GACrC,MAAM,UAAU,eAAe,MAAM,UAAU;GAC/C,IAAI,CAAC,SAAS;GAEd,kBACE,MACA,SACA;uCAC6B,eAAe,0BAA0B,GAAG;;;;GAK3E;GACA,UAAU,MAAM;IAAE,MAAM;IAAgC,OAAO,CAAC,UAAU;GAAE,CAAC;EAC/E,CAAC,IAGD,IAAI,KACF,gBAAgB,WACZ,8KACA,yFACN;EAEF,IAAI,KACF,+HACF;CACF;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-Ck0EPdgu.d.ts","names":[],"sources":["../src/codemod/shared.ts","../src/codemod/config.ts","../src/codemod/react-router.ts","../src/codemod/tanstack-start.ts","../src/codemod/source.ts","../src/codemod/next-proxy.ts"],"mappings":";;;KAgBY,WAAW,UAAU,gBAAgB,QAAQ;EAAQ,MAAM;;UAEtD;EACf;;EAEA;EACA,SAAS;;EAET,GAAG;EACH,QAAQ;;iBAGM,gBAAgB,cAAc,eAAe;iBAcvC,iBAAiB,eAAe,QAAQ;;iBAK9C,eAAe,MAAM;iBAMpB,YAAY,MAAM,OAAO,UAAU;iBAapC,KAAK,UAAU,cAC7B,MAAM,MACN,MAAM,GACN,aAAa,MAAM,WAAW,iBAC7B,WAAW;iBAOE,iBAAiB,MAAM,aAAa;iBAIpC,eAAe,MAAM,YAAY,kBAAkB;iBAMnD,gBAAgB,MAAM,YAAY,SAAS,mBAAmB;;;;iBAa9D,mBAAmB,MAAM,YAAY,SAAS,YAAY;;;;iBAa1D,kBAAkB,MAAM,YAAY,SAAS,YAAY;iBAyBzD,YAAY,QAAQ,kBAAkB,eAAe;;iBAYrD,WAAW,cAAc;iBAKzB,UACd,MAAM,YACN;EAAe;EAAc;EAAkB;;KAkC5C,YAAY,kBAAkB;KAC9B,UAAU,UAAU,aAAa,UAAU,kBAC5C,YAAY,uCACZ;;;;iBAUY,YAAY,MAAM,YAAY,WAAW,WAAW;;;;iBA4BpD,eAAe,UAAU,WACvC,MAAM,YACN,WAAW,GACX,OAAO,SAAS,UAAU;;;;;;iBC9NZ,gBAAgB,MAAM,aAAa;;;;;;iBAiCnC,cACd,MAAM,YACN;EAAU;EAAc;EAAc;GACtC;;;;iBAqCc,eAAe,MAAM;;;;iBAkBrB,gBACd,MAAM,YACN;EAAY;EAAgB;;;;;;;iBC5Fd,gCACd,MAAM,YACN,6BACA,SAAS;;;;iBAcK,6BACd,MAAM,YACN,6BACA;;;;iBAgCc,oBACd,MAAM,YACN;EAAW;EAAc;;;;;iBAgBX,uBACd,MAAM,YACN,SAAS;EAAQ;EAAc;;;;;;;;;iBCvEjB,qBAAqB,MAAM,YAAY;;;;;;;;iBCRvC,wBAAwB,MAAM;;;;;;iBCF9B,gBAAgB,MAAM,YAAY"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-Cs7hw2un.d.ts","names":[],"sources":["../src/features/index.ts"],"mappings":";;;;UASiB,aAAa;EAC5B;EACA;IAAW,OAAO;IAAG;IAAe;;;UAGrB;EACf;EACA;;KAGU,cAAc,MAAM,uBAAuB,gBAAgB,aAAa;UAEnE,QAAQ,yBAAyB;EAChD;EACA;EACA;EACA,aAAa,WAAW,UAAU,cAAc,QAAQ;;EAExD,WAAW;;EAEX,UAAU,SAAS,YAAY;;EAE/B,YAAY,SAAS;EACrB,MAAM,KAAK,gBAAgB,SAAS,UAAU;;KAGpC,aAAa,KAAK,QAAQ;EACpC,UAAU,eAAe,wBAAwB;;UAGlC;EACf,UAAU;EACV,eAAe;EACf,kBAAkB;;UAGH;EACf,SAAS;;EAET,QAAQ,cAAc,oBAAoB;;EAE1C,SAAS,cAAc,OAAO,MAAM,wBAAwB;;EAE5D,SAAS,cAAc,oBAAoB;EAC3C,SAAS,iBAAiB;EAC1B,cAAc,OAAO,MAAM,yBAAyB;;EAEpD,UAAU,cAAc,yBAAyB;;EAEjD,kBAAkB,MAAM,+BAA+B;EACvD,MAAM,aAAa;;EAEnB,OAAO;EACP,MAAM;;UAGS;EACf,MAAM;EACN,mBAAmB,iBAAiB;;EAEpC;;UAGe;EACf,SAAS;EACT,WAAW;EACX,IAAI;;EAEJ,WAAW;;iBAGS,WAAW,kBAC/B,SAAS,KAAK,QAAQ,gBACtB,SAAS,KACP,SAAS,WAAW,IAAI,YAAY,aACrC;EAAU"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DI66DoV8.d.ts","names":[],"sources":["../src/project/index.ts"],"mappings":";;;KAMY,iBAAiB,YAAY,QAAQ,kBAAkB;KAEvD;;KAEA,iBAAiB,QAAQ;UAEpB;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA,YAAY;;cAGD,YAAY,OAAO,WAAW;;UAoC1B;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;UAGe;;EAEf;;UAGe;EACf;EACA,WAAW;EACX,MAAM;;EAEN;;EAEA,MAAM;EACN,QAAQ;EACR,gBAAgB;;EAEhB;EACA,QAAQ;;EAER;EACA;IACE,eAAe;IACf,kBAAkB;;;iBAIN,qBAAqB,YAAY,KAAK;iBAIhC,YACpB,QAAQ,cACR,cACA,YAAW,YACV,QAAQ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"llms-DQzAst6R.js","names":[],"sources":["../src/features/llms.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature, FeatureContext } from '@/features';\nimport type { I18nInfo, ReactFramework } from '@/project';\nimport {\n type FormattedRoute,\n formatRoute,\n reactRouterTypes,\n routeModule,\n type RouteDescriptor,\n} from '@/project/route';\nimport {\n addImport,\n addNextRewrites,\n addProxyMatcher,\n addTanstackPrerender,\n enableProcessedMarkdown,\n} from '@/codemod';\nimport { docs, findNextProxy } from './docs';\nimport {\n addExport,\n addPageUrl,\n reactFramework,\n type SourceRef,\n sourceRef,\n registerReactRouterRoutes,\n requiresMarkdown,\n sharedRoute,\n} from './utils';\n\n/** runtime content sources expose raw Markdown as `page.data.content`, Fumadocs MDX processes it */\nexport const docsLlms = ({ dynamic, ref }: SourceRef) => `\nexport const docsLlms = llms(${ref}, {\n renderPage: ${dynamic ? '(page)' : 'async (page)'} => \\`# \\${page.data.title} (\\${page.url})\n\n\\${${dynamic ? 'page.data.content' : \"await page.data.getText('processed')\"}}\\`,\n});`;\n\n/** the Markdown URL of a page, `/llms.mdx/docs/<slugs>/content.md` */\nexport const markdownUrl = (i18n: boolean) => `\nconst getContentUrl = createGetUrl(docsContentRoute${i18n ? ', i18n' : ''});\n\nexport function getPageMarkdownUrl(page: { slugs: string[]; locale?: string }) {\n const segments = [...page.slugs, 'content.md'];\n\n return { segments, url: getContentUrl(segments, page.locale) };\n}`;\n\n/** TanStack Start serves Markdown with a `.md` suffix under the docs route */\nexport const tanstackMarkdownUrl = (i18n: boolean) => `\nconst getDocsUrl = createGetUrl(docsRoute${i18n ? ', i18n' : ''});\n\nexport function getPageMarkdownUrl(page: { slugs: string[]; locale?: string }) {\n const segments = [...page.slugs];\n if (segments.length === 0) {\n segments.push('index.md');\n } else {\n segments[segments.length - 1] += '.md';\n }\n\n return { segments, url: getDocsUrl(segments, page.locale) };\n}\n\n/** @returns page slugs */\nexport function decodeMarkdownUrl(segments: string[]) {\n if (segments.length === 0) return [];\n\n const out = [...segments];\n out[out.length - 1] = out[out.length - 1].replace(/\\\\.md$/, '');\n if (out.length === 1 && out[0] === 'index') out.pop();\n return out;\n}`;\n\nconst imports = `import { docsLlms } from '@/lib/source';`;\nconst pageImports = ({ ref }: SourceRef) => `import { docsLlms, ${ref} } from '@/lib/source';`;\nconst markdown = `new Response(await docsLlms.page(page), {\n headers: {\n 'Content-Type': 'text/markdown',\n },\n })`;\n\ninterface Routes {\n index: FormattedRoute;\n full: FormattedRoute;\n markdown: FormattedRoute;\n}\n\ntype Template = (route: FormattedRoute, i18n: boolean, src: SourceRef) => string;\n\n/** the per-page Markdown route, the only one reading route params */\nconst markdownTemplates: Record<ReactFramework, Template> = {\n next: (route, i18n, src) => `${pageImports(src)}\nimport { notFound } from 'next/navigation';\n\nexport const revalidate = false;\n\nexport async function GET(_req: Request, { params }: RouteContext<'${route.path}'>) {\n const { slug${i18n ? ', lang' : ''} } = await params;\n // remove the appended \"content.md\", \\`/docs/index.md\\` is rewritten to the root page\n const slugs = slug?.slice(0, -1) ?? [];\n if (slugs.at(-1) === 'index') slugs.pop();\n const page = ${src.resolved}.getPage(slugs${i18n ? ', lang' : ''});\n if (!page) notFound();\n\n return ${markdown};\n}\n\nexport ${src.dynamic ? 'async ' : ''}function generateStaticParams() {\n return ${src.resolved}.generateParams().map((item) => ({\n ...item,\n slug: [...item.slug, 'content.md'],\n }));\n}\n`,\n 'react-router': (route, i18n, src) => `${reactRouterTypes(route)}\n${pageImports(src)}\n\nexport async function loader({ params }: Route.LoaderArgs) {\n const slugs = params['*'].split('/').filter((v) => v.length > 0);\n // remove the appended \"content.md\"\n slugs.pop();\n const page = ${src.resolved}.getPage(slugs${i18n ? ', params.lang' : ''});\n if (!page) return new Response('not found', { status: 404 });\n\n return ${markdown};\n}\n`,\n 'tanstack-start': (\n route,\n i18n,\n src,\n ) => `import { createFileRoute, notFound } from '@tanstack/react-router';\nimport { decodeMarkdownUrl } from '@/lib/shared';\n${pageImports(src)}\n\nexport const Route = createFileRoute('${route.path}')({\n server: {\n handlers: {\n GET: async ({ params }) => {\n const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);\n const page = ${src.resolved}.getPage(slugs${i18n ? ', params.lang' : ''});\n if (!page) throw notFound();\n\n return ${markdown.replaceAll('\\n ', '\\n ')};\n },\n },\n },\n});\n`,\n waku: (route, i18n, src) => `${pageImports(src)}\nimport type { ApiContext } from 'waku/router';\nimport { unstable_notFound } from 'waku/router/server';\n\nexport async function GET(_: Request, { params }: ApiContext<'${route.path}'>) {\n const page = ${src.resolved}.getPage(params.slug${i18n ? ', params.lang' : ''});\n if (!page) unstable_notFound();\n\n return ${markdown};\n}\n\nexport async function getConfig() {\n return {\n render: 'static' as const,\n staticPaths: ${src.resolved}.generateParams().map((item) => ${i18n ? '[item.lang, ...item.slug]' : 'item.slug'}),\n } as const;\n}\n`,\n};\n\nexport function templates(\n framework: ReactFramework,\n routes: Routes,\n i18n: boolean,\n src: SourceRef,\n): [route: FormattedRoute, content: string][] {\n const index = (body: string) => ({ imports, body, revalidate: true, static: true });\n\n return [\n [\n routes.index,\n routeModule(framework, routes.index, index('new Response(await docsLlms.index())')),\n ],\n [\n routes.full,\n routeModule(framework, routes.full, index('new Response(await docsLlms.full())')),\n ],\n [routes.markdown, markdownTemplates[framework](routes.markdown, i18n, src)],\n ];\n}\n\nexport function llmsRoutes(\n framework: ReactFramework,\n i18n: I18nInfo | null,\n docsRoute: string,\n contentRoute: string,\n): Routes {\n const format = (route: RouteDescriptor) => formatRoute(route, framework, i18n);\n return {\n index: format({ segments: ['llms.txt'] }),\n full: format({ segments: ['llms-full.txt'] }),\n markdown: format(\n framework === 'tanstack-start'\n ? { segments: [docsRoute, '{$}.md'], locale: true }\n : {\n segments: [\n contentRoute,\n { param: 'slug', catchAll: true, optional: true, suffix: 'content.md' },\n ],\n locale: true,\n },\n ),\n };\n}\n\nexport const llms: Feature = {\n id: 'llms',\n title: 'LLM Routes',\n description: 'serve docs as Markdown for LLMs: llms.txt, llms-full.txt and per-page Markdown',\n requires: [docs],\n supports: requiresMarkdown,\n async apply(ctx) {\n const { baseDir, i18n, source, configFile } = ctx.project;\n const framework = reactFramework(ctx.project);\n const sourceFile = path.join(baseDir, 'lib/source.ts');\n const src = sourceRef(ctx.project.source.dynamic);\n // runtime content sources already give raw Markdown\n if (source.collections) {\n await ctx.source(source.collections, (file) => {\n if (!enableProcessedMarkdown(file))\n throw new Error(`cannot find \\`defineDocs()\\` in ${source.collections}`);\n });\n }\n if (await addExport(ctx, sourceFile, 'docsLlms', docsLlms(src))) {\n await ctx.source(sourceFile, (file) =>\n addImport(file, { from: 'fumadocs-core/source', named: ['llms'] }),\n );\n }\n\n const docsRoute = source.baseUrl.replace(/\\/$/, '');\n let contentRoute = `/llms.mdx${docsRoute}`;\n if (framework === 'tanstack-start') {\n await sharedRoute(ctx, 'docsRoute', source.baseUrl);\n await addPageUrl(ctx, 'getPageMarkdownUrl', tanstackMarkdownUrl(i18n !== null));\n } else {\n contentRoute = await sharedRoute(ctx, 'docsContentRoute', contentRoute);\n await addPageUrl(ctx, 'getPageMarkdownUrl', markdownUrl(i18n !== null));\n }\n const routes = llmsRoutes(framework, i18n, docsRoute, contentRoute);\n for (const [route, content] of templates(framework, routes, i18n !== null, src))\n await ctx.write(path.join(baseDir, route.file), content);\n\n if (framework === 'react-router') {\n await registerReactRouterRoutes(ctx, [routes.index, routes.full, routes.markdown]);\n } else if (framework === 'tanstack-start' && ctx.project.static && configFile) {\n // SPA mode only serves prerendered routes\n await ctx.source(configFile, (file) =>\n addTanstackPrerender(file, [routes.index.path, routes.full.path]),\n );\n } else if (framework === 'next') {\n if (i18n) await extendNextProxy(ctx, routes.markdown);\n await configureMarkdownUrl(ctx, docsRoute, routes.markdown);\n }\n if (ctx.project.static && framework !== 'next') {\n ctx.note(\n 'Per-page Markdown routes are dynamic, add the pages to your prerender config to serve them statically.',\n );\n }\n if (framework === 'react-router' || framework === 'waku') {\n ctx.note(\n 'To serve pages with a `.md` suffix, rewrite `/docs/*.md` to the `llms.mdx` route in a middleware, see https://fumadocs.dev/docs/integrations/llms#md-extension.',\n );\n }\n ctx.note(\n \"Add page actions to your docs page with `MarkdownCopyButton` and `ViewOptionsPopover` from 'fumadocs-ui/layouts/docs/page', the Markdown URL of a page is `getPageMarkdownUrl(page).url` from '@/lib/shared'.\",\n );\n },\n};\n\n/** `/docs/*.md` serves the Markdown of a page, like TanStack Start's `.md` route */\nexport const markdownRewrite = (docsRoute: string, markdown: FormattedRoute, i18n: boolean) => ({\n source: `${i18n ? '/:lang' : ''}${docsRoute}/:slug*.md`,\n destination: `${markdown.pattern}/content.md`,\n});\n\nasync function configureMarkdownUrl(\n ctx: FeatureContext,\n docsRoute: string,\n markdown: FormattedRoute,\n) {\n const { configFile, i18n } = ctx.project;\n const rewrite = markdownRewrite(docsRoute, markdown, i18n !== null);\n const edited =\n configFile !== undefined &&\n (await ctx\n .source(configFile, (file) => {\n if (file.code.includes('llms.mdx')) return;\n if (!addNextRewrites(file, [rewrite])) throw new Error('cannot add rewrites');\n })\n .catch(() => false));\n\n if (!edited) {\n ctx.note(\n `Rewrite \\`${rewrite.source}\\` to \\`${rewrite.destination}\\` in your Next.js config to serve pages with a \\`.md\\` suffix.`,\n );\n }\n}\n\n/** routes under `[lang]` need the i18n middleware to resolve the locale */\nexport async function extendNextProxy(ctx: FeatureContext, route: FormattedRoute) {\n const patterns = [route.pattern.replace('/:lang', ''), route.pattern];\n const proxy = await findNextProxy(ctx.project);\n const edited =\n proxy !== undefined &&\n (await ctx\n .source(proxy, (file) => {\n if (!addProxyMatcher(file, patterns)) throw new Error('cannot find the matcher');\n })\n .catch(() => false));\n if (!edited)\n ctx.note(\n `Add ${patterns.map((p) => `\\`${p}\\``).join(', ')} to the matcher of your i18n middleware.`,\n );\n}\n"],"mappings":"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"}
|
package/dist/mcp-BpFZ_fdV.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-BpFZ_fdV.js","names":[],"sources":["../src/features/mcp.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature } from '@/features';\nimport type { ReactFramework } from '@/project';\nimport { type FormattedRoute, formatRoute, routeModule } from '@/project/route';\nimport { addReactRouterPrerenderArray } from '@/codemod';\nimport { llms } from './llms';\nimport {\n reactFramework,\n registerReactRouterRoutes,\n requiresMarkdown,\n type SourceRef,\n sourceRef,\n} from './utils';\n\nconst server = ({\n ref,\n}: SourceRef) => `import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';\nimport { registerSearchTool, registerSourceTools } from 'fumadocs-core/mcp';\nimport { createFromSource } from 'fumadocs-core/search/server';\nimport { docsLlms, ${ref} } from '@/lib/source';\n\nconst handler = createMcpHandler(() => {\n const mcp = new McpServer({\n name: 'docs',\n version: '1.0.0',\n });\n\n registerSourceTools(mcp, ${ref}, docsLlms);\n registerSearchTool(mcp, createFromSource(${ref}));\n\n return mcp;\n});\n`;\n\nexport const templates = (framework: ReactFramework, route: FormattedRoute, src: SourceRef) =>\n routeModule(framework, route, {\n imports: server(src),\n body: 'handler.fetch(request)',\n request: true,\n methods: ['GET', 'POST', 'DELETE'],\n });\n\nexport const mcpRoute = (framework: ReactFramework) =>\n formatRoute({ segments: ['api/mcp'] }, framework, null);\n\nexport const mcp: Feature = {\n id: 'mcp',\n title: 'MCP Server',\n description: 'a MCP server for AI agents to search and read your docs, at /api/mcp',\n requires: [llms],\n supports: (project) =>\n project.static ? 'MCP requires a server at runtime' : requiresMarkdown(project),\n async apply(ctx) {\n const { baseDir } = ctx.project;\n const framework = reactFramework(ctx.project);\n ctx.addDependencies({ '@modelcontextprotocol/server': null, zod: null });\n\n const route = mcpRoute(framework);\n await ctx.write(\n path.join(baseDir, route.file),\n templates(framework, route, sourceRef(ctx.project.source.dynamic)),\n );\n\n if (framework === 'react-router') {\n await registerReactRouterRoutes(ctx, [route]);\n await ctx.source('react-router.config.ts', (file) => {\n addReactRouterPrerenderArray(file, 'excluded', [`/${route.path}`]);\n });\n }\n\n ctx.note(`Connect AI agents to your docs with the MCP server:\n{\n \"mcpServers\": {\n \"docs\": { \"url\": \"https://your-site.com/api/mcp\" }\n }\n}`);\n },\n};\n"],"mappings":";;;;;;AAcA,MAAM,UAAU,EACd,UACe;;;qBAGI,IAAI;;;;;;;;6BAQI,IAAI;6CACY,IAAI;;;;;AAMjD,MAAa,aAAa,WAA2B,OAAuB,QAC1E,YAAY,WAAW,OAAO;CAC5B,SAAS,OAAO,GAAG;CACnB,MAAM;CACN,SAAS;CACT,SAAS;EAAC;EAAO;EAAQ;CAAQ;AACnC,CAAC;AAEH,MAAa,YAAY,cACvB,YAAY,EAAE,UAAU,CAAC,SAAS,EAAE,GAAG,WAAW,IAAI;AAExD,MAAa,MAAe;CAC1B,IAAI;CACJ,OAAO;CACP,aAAa;CACb,UAAU,CAAC,IAAI;CACf,WAAW,YACT,QAAQ,SAAS,qCAAqC,iBAAiB,OAAO;CAChF,MAAM,MAAM,KAAK;EACf,MAAM,EAAE,YAAY,IAAI;EACxB,MAAM,YAAY,eAAe,IAAI,OAAO;EAC5C,IAAI,gBAAgB;GAAE,gCAAgC;GAAM,KAAK;EAAK,CAAC;EAEvE,MAAM,QAAQ,SAAS,SAAS;EAChC,MAAM,IAAI,MACR,KAAK,KAAK,SAAS,MAAM,IAAI,GAC7B,UAAU,WAAW,OAAO,UAAU,IAAI,QAAQ,OAAO,OAAO,CAAC,CACnE;EAEA,IAAI,cAAc,gBAAgB;GAChC,MAAM,0BAA0B,KAAK,CAAC,KAAK,CAAC;GAC5C,MAAM,IAAI,OAAO,2BAA2B,SAAS;IACnD,6BAA6B,MAAM,YAAY,CAAC,IAAI,MAAM,MAAM,CAAC;GACnE,CAAC;EACH;EAEA,IAAI,KAAK;;;;;EAKX;CACA;AACF"}
|
package/dist/og-m0CwRlRz.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"og-m0CwRlRz.js","names":[],"sources":["../src/features/og.ts"],"sourcesContent":["import path from 'node:path';\nimport type { Feature } from '@/features';\nimport type { ReactFramework } from '@/project';\nimport { type FormattedRoute, formatRoute, reactRouterTypes } from '@/project/route';\nimport { addElements, getConfigObject, getProperty } from '@/codemod';\nimport { docs } from './docs';\nimport { extendNextProxy } from './llms';\nimport {\n addPageUrl,\n reactFramework,\n registerReactRouterRoutes,\n sharedRoute,\n type SourceRef,\n sourceRef,\n} from './utils';\n\ntype Engine = 'takumi' | 'next-og';\n\n/** the image URL of a page, `/og/docs/<slugs>/image.png` */\nconst imageUrl = (ext: string, i18n: boolean) => `\nconst getImageUrl = createGetUrl(docsImageRoute${i18n ? ', i18n' : ''});\n\nexport function getPageImageUrl(page: { slugs: string[]; locale?: string }) {\n const segments = [...page.slugs, 'image.${ext}'];\n\n return { segments, url: getImageUrl(segments, page.locale) };\n}`;\n\nconst imports = (engine: Engine) =>\n `import { generateOGImage } from 'fumadocs-ui/og${engine === 'takumi' ? '/takumi' : ''}';`;\n\nconst image = (engine: Engine) => `generateOGImage({\n title: page.data.title,\n description: page.data.description,\n site: 'My App',${engine === 'takumi' ? \"\\n format: 'webp',\" : ''}\n })`;\n\nconst ext = (engine: Engine) => (engine === 'takumi' ? 'webp' : 'png');\n\ntype Template = (route: FormattedRoute, engine: Engine, i18n: boolean, src: SourceRef) => string;\n\nconst templates: Record<ReactFramework, Template> = {\n next: (route, engine, i18n, src) => `import { ${src.ref} } from '@/lib/source';\nimport { notFound } from 'next/navigation';\n${imports(engine)}\n\nexport const revalidate = false;\n\nexport async function GET(_req: Request, { params }: RouteContext<'${route.path}'>) {\n const { slug${i18n ? ', lang' : ''} } = await params;\n const page = ${src.resolved}.getPage(slug.slice(0, -1)${i18n ? ', lang' : ''});\n if (!page) notFound();\n\n return ${image(engine)};\n}\n\nexport ${src.dynamic ? 'async ' : ''}function generateStaticParams() {\n return ${src.resolved}.generateParams().map((item) => ({\n ...item,\n slug: [...item.slug, 'image.${ext(engine)}'],\n }));\n}\n`,\n 'react-router': (route, engine, i18n, src) => `${reactRouterTypes(route)}\nimport { ${src.ref} } from '@/lib/source';\n${imports(engine)}\n\nexport async function loader({ params }: Route.LoaderArgs) {\n const slugs = params['*'].split('/').filter((v) => v.length > 0);\n const page = ${src.resolved}.getPage(slugs.slice(0, -1)${i18n ? ', params.lang' : ''});\n if (!page) throw new Response(undefined, { status: 404 });\n\n return ${image(engine)};\n}\n`,\n 'tanstack-start': (\n route,\n engine,\n i18n,\n src,\n ) => `import { createFileRoute } from '@tanstack/react-router';\nimport { ${src.ref} } from '@/lib/source';\n${imports(engine)}\n\nexport const Route = createFileRoute('${route.path}')({\n server: {\n handlers: {\n GET: async ({ params }) => {\n const slugs = params._splat?.split('/') ?? [];\n const page = ${src.resolved}.getPage(slugs.slice(0, -1)${i18n ? ', params.lang' : ''});\n if (!page) return new Response(undefined, { status: 404 });\n\n return ${image(engine).replaceAll('\\n ', '\\n ')};\n },\n },\n },\n});\n`,\n waku: (route, engine, i18n, src) => `import { ${src.ref} } from '@/lib/source';\nimport type { ApiContext } from 'waku/router';\n${imports(engine)}\n\nexport async function GET(_: Request, { params }: ApiContext<'${route.path}'>) {\n const page = ${src.resolved}.getPage(params.slug${i18n ? ', params.lang' : ''});\n if (!page) return new Response(undefined, { status: 404 });\n\n return ${image(engine)};\n}\n\nexport async function getConfig() {\n return {\n render: 'static' as const,\n staticPaths: ${src.resolved}.generateParams().map((item) => ${i18n ? '[item.lang, ...item.slug]' : 'item.slug'}),\n } as const;\n}\n`,\n};\n\nexport const og: Feature<{ engine: Engine }> = {\n id: 'og',\n title: 'OG Image',\n description: 'generate Open Graph images for docs pages',\n requires: [docs],\n options: {\n engine: {\n message: 'Choose an image engine',\n choices: [\n { value: 'takumi', label: 'Takumi', hint: 'outputs WebP, framework-agnostic' },\n { value: 'next-og', label: 'next/og', hint: 'Next.js built-in solution' },\n ],\n },\n },\n async apply(ctx, { engine }) {\n const { baseDir, configFile, i18n, source } = ctx.project;\n const framework = reactFramework(ctx.project);\n if (engine === 'next-og' && framework !== 'next')\n throw new Error('next/og is only available on Next.js');\n if (engine === 'takumi') ctx.addDependencies({ 'takumi-js': null });\n\n const shared = path.join(baseDir, 'lib/shared.ts');\n const imageRoute = await sharedRoute(\n ctx,\n 'docsImageRoute',\n `/og${source.baseUrl.replace(/\\/$/, '')}`,\n );\n const added = await addPageUrl(ctx, 'getPageImageUrl', imageUrl(ext(engine), i18n !== null));\n if (!added && engine === 'takumi') {\n await ctx.source(shared, (file) => {\n file.s.replaceAll('image.png', 'image.webp');\n });\n }\n\n const route = formatRoute(\n {\n segments: [imageRoute, { param: 'slug', catchAll: true, suffix: `image.${ext(engine)}` }],\n locale: true,\n },\n framework,\n i18n,\n 'tsx',\n );\n await ctx.write(\n path.join(baseDir, route.file),\n templates[framework](route, engine, i18n !== null, sourceRef(ctx.project.source.dynamic)),\n );\n\n if (framework === 'react-router') await registerReactRouterRoutes(ctx, [route]);\n if (framework === 'next' && engine === 'takumi' && configFile) {\n await ctx.source(configFile, (file) => {\n const config = getConfigObject(file);\n if (config && !getProperty(config, 'serverExternalPackages'))\n addElements(file, config, [\"serverExternalPackages: ['@takumi-rs/core']\"]);\n });\n }\n if (framework === 'next' && i18n) await extendNextProxy(ctx, route);\n\n ctx.note(\n \"The image URL of a page is `getPageImageUrl(page).url` from '@/lib/shared', reference it in the page metadata (e.g. `og:image`).\",\n );\n },\n};\n"],"mappings":"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"}
|