create-fumadocs-app 15.1.3 → 15.2.1

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.
Files changed (59) hide show
  1. package/dist/{chunk-K4QGKCVK.js → chunk-LCUIXVWC.js} +159 -68
  2. package/dist/create-app.js +1 -1
  3. package/dist/index.js +48 -24
  4. package/package.json +2 -2
  5. package/template/react-router/README.md +12 -0
  6. package/template/react-router/app/app.css +6 -0
  7. package/template/react-router/app/docs/page.tsx +63 -0
  8. package/template/react-router/app/docs/search.ts +18 -0
  9. package/template/react-router/app/root.tsx +78 -0
  10. package/template/react-router/app/routes/home.tsx +34 -0
  11. package/template/react-router/app/routes.ts +7 -0
  12. package/template/react-router/app/source.ts +60 -0
  13. package/template/react-router/content/docs/index.mdx +32 -0
  14. package/template/react-router/content/docs/test.mdx +8 -0
  15. package/template/react-router/example.gitignore +6 -0
  16. package/template/react-router/public/favicon.ico +0 -0
  17. package/template/react-router/react-router.config.ts +9 -0
  18. package/template/react-router/tsconfig.json +27 -0
  19. package/template/react-router/vite.config.ts +13 -0
  20. package/template/tanstack-start/README.md +12 -0
  21. package/template/tanstack-start/app/api.ts +6 -0
  22. package/template/tanstack-start/app/app.css +6 -0
  23. package/template/tanstack-start/app/client.tsx +7 -0
  24. package/template/tanstack-start/app/router.tsx +15 -0
  25. package/template/tanstack-start/app/routes/__root.tsx +49 -0
  26. package/template/tanstack-start/app/routes/api/search.ts +20 -0
  27. package/template/tanstack-start/app/routes/docs/$.tsx +71 -0
  28. package/template/tanstack-start/app/routes/index.tsx +25 -0
  29. package/template/tanstack-start/app/ssr.tsx +13 -0
  30. package/template/tanstack-start/app.config.ts +43 -0
  31. package/template/tanstack-start/content/docs/index.mdx +35 -0
  32. package/template/tanstack-start/example.gitignore +4 -0
  33. package/template/tanstack-start/lib/source.ts +70 -0
  34. package/template/tanstack-start/tsconfig.json +15 -0
  35. /package/template/{+shared → +next}/README.md +0 -0
  36. /package/template/{+shared → +next}/app/(home)/layout.tsx +0 -0
  37. /package/template/{+shared → +next}/app/(home)/page.tsx +0 -0
  38. /package/template/{+shared → +next}/app/api/search/route.ts +0 -0
  39. /package/template/{+shared → +next}/app/docs/layout.tsx +0 -0
  40. /package/template/{+shared → +next}/app/layout.config.tsx +0 -0
  41. /package/template/{+shared → +next}/app/layout.tsx +0 -0
  42. /package/template/{+shared → +next}/content/docs/index.mdx +0 -0
  43. /package/template/{+shared → +next}/content/docs/test.mdx +0 -0
  44. /package/template/{+shared → +next}/example.gitignore +0 -0
  45. /package/template/{content-collections → +next+content-collections}/app/docs/[[...slug]]/page.tsx +0 -0
  46. /package/template/{content-collections → +next+content-collections}/content-collections.ts +0 -0
  47. /package/template/{content-collections → +next+content-collections}/lib/source.ts +0 -0
  48. /package/template/{content-collections → +next+content-collections}/next.config.mjs +0 -0
  49. /package/template/{content-collections → +next+content-collections}/tsconfig.json +0 -0
  50. /package/template/{+eslint → +next+eslint}/.eslintrc.json +0 -0
  51. /package/template/{fuma-docs-mdx → +next+fuma-docs-mdx}/app/docs/[[...slug]]/page.tsx +0 -0
  52. /package/template/{fuma-docs-mdx → +next+fuma-docs-mdx}/lib/source.ts +0 -0
  53. /package/template/{fuma-docs-mdx → +next+fuma-docs-mdx}/next.config.mjs +0 -0
  54. /package/template/{fuma-docs-mdx → +next+fuma-docs-mdx}/source.config.ts +0 -0
  55. /package/template/{fuma-docs-mdx → +next+fuma-docs-mdx}/tsconfig.json +0 -0
  56. /package/template/{+tailwindcss → +next+tailwindcss}/app/(home)/page.tsx +0 -0
  57. /package/template/{+tailwindcss → +next+tailwindcss}/app/global.css +0 -0
  58. /package/template/{+tailwindcss → +next+tailwindcss}/app/layout.tsx +0 -0
  59. /package/template/{+tailwindcss → +next+tailwindcss}/postcss.config.mjs +0 -0
@@ -60,7 +60,7 @@ function tryGitInit(root) {
60
60
  }
61
61
 
62
62
  // src/versions.js
63
- var versions = { "fumadocs-core": "15.1.3", "fumadocs-ui": "15.1.3", "fumadocs-mdx": "11.5.7", "@fumadocs/content-collections": "1.1.8" };
63
+ var versions = { "fumadocs-core": "15.2.1", "fumadocs-ui": "15.2.1", "fumadocs-mdx": "11.5.7", "@fumadocs/mdx-remote": "1.3.0", "@fumadocs/content-collections": "1.1.8" };
64
64
 
65
65
  // ../create-app-versions/package.json
66
66
  var package_default = {
@@ -73,17 +73,33 @@ var package_default = {
73
73
  "@content-collections/core": "^0.8.2",
74
74
  "@content-collections/mdx": "^0.2.2",
75
75
  "@content-collections/next": "^0.2.6",
76
- "@tailwindcss/postcss": "^4.0.15",
76
+ "@react-router/dev": "^7.4.1",
77
+ "@react-router/node": "^7.4.1",
78
+ "@react-router/serve": "^7.4.1",
79
+ "@tailwindcss/postcss": "^4.0.17",
80
+ "@tailwindcss/vite": "^4.0.17",
77
81
  "@types/mdx": "^2.0.13",
78
- "@types/node": "22.13.11",
82
+ "@types/node": "22.13.14",
79
83
  "@types/react": "^19.0.12",
80
84
  "@types/react-dom": "^19.0.4",
81
- next: "15.2.3",
85
+ "gray-matter": "^4.0.3",
86
+ isbot: "^5.1.17",
87
+ next: "15.2.4",
82
88
  postcss: "^8.5.3",
83
- react: "^19.0.0",
84
- "react-dom": "^19.0.0",
85
- tailwindcss: "^4.0.15",
86
- typescript: "^5.8.2"
89
+ react: "^19.1.0",
90
+ "react-dom": "^19.1.0",
91
+ "react-router": "^7.4.1",
92
+ "react-router-devtools": "^1.1.8",
93
+ shiki: "^3.2.1",
94
+ tailwindcss: "^4.0.17",
95
+ typescript: "^5.8.2",
96
+ vite: "^6.2.3",
97
+ "vite-tsconfig-paths": "^5.1.4",
98
+ "@tanstack/react-router": "^1.114.29",
99
+ "@tanstack/react-start": "^1.114.29",
100
+ "fast-glob": "^3.3.3",
101
+ vinxi: "^0.5.3",
102
+ "@vitejs/plugin-react": "^4.3.4"
87
103
  }
88
104
  };
89
105
 
@@ -137,9 +153,10 @@ async function create(options) {
137
153
  } = options;
138
154
  const projectName = path.basename(options.outputDir);
139
155
  const dest = path.resolve(cwd, options.outputDir);
156
+ const isNext = options.template.startsWith("+next");
140
157
  function defaultRename(file) {
141
158
  file = file.replace("example.gitignore", ".gitignore");
142
- if (!options.useSrcDir) {
159
+ if (!options.useSrcDir || !isNext) {
143
160
  return file;
144
161
  }
145
162
  for (const dir of ["app", "lib"]) {
@@ -150,25 +167,38 @@ async function create(options) {
150
167
  }
151
168
  return file;
152
169
  }
153
- await copy(path.join(sourceDir, `template/+shared`), dest, defaultRename);
170
+ if (isNext) {
171
+ await copy(path.join(sourceDir, `template/+next`), dest, defaultRename);
172
+ }
154
173
  await copy(
155
174
  path.join(sourceDir, `template/${options.template}`),
156
175
  dest,
157
176
  defaultRename
158
177
  );
159
- if (options.tailwindcss) {
178
+ if (isNext && options.tailwindcss) {
160
179
  await copy(
161
- path.join(sourceDir, `template/+tailwindcss`),
180
+ path.join(sourceDir, `template/+next+tailwindcss`),
162
181
  dest,
163
182
  defaultRename
164
183
  );
184
+ if (options.useSrcDir) {
185
+ const cssPath = path.join(dest, "src/app/global.css");
186
+ await fs.writeFile(
187
+ cssPath,
188
+ (await fs.readFile(cssPath)).toString().replace("../", "../../")
189
+ );
190
+ }
165
191
  log("Configured Tailwind CSS");
166
192
  }
167
- if (options.eslint) {
168
- await copy(path.join(sourceDir, `template/+eslint`), dest, defaultRename);
193
+ if (isNext && options.eslint) {
194
+ await copy(
195
+ path.join(sourceDir, `template/+next+eslint`),
196
+ dest,
197
+ defaultRename
198
+ );
169
199
  log("Configured ESLint");
170
200
  }
171
- if (options.useSrcDir) {
201
+ if (isNext && options.useSrcDir) {
172
202
  const tsconfigPath = path.join(dest, "tsconfig.json");
173
203
  const content = (await fs.readFile(tsconfigPath)).toString();
174
204
  const config = JSON.parse(content);
@@ -178,16 +208,12 @@ async function create(options) {
178
208
  });
179
209
  }
180
210
  await fs.writeFile(tsconfigPath, JSON.stringify(config, null, 2));
181
- if (options.tailwindcss) {
182
- const cssPath = path.join(dest, "src/app/global.css");
183
- await fs.writeFile(
184
- cssPath,
185
- (await fs.readFile(cssPath)).toString().replace("../", "../../")
186
- );
187
- }
188
211
  }
189
212
  const packageJson = createPackageJson(projectName, options);
190
- await fs.writeFile(path.join(dest, "package.json"), packageJson);
213
+ await fs.writeFile(
214
+ path.join(dest, "package.json"),
215
+ JSON.stringify(packageJson, null, 2)
216
+ );
191
217
  const readMe = await getReadme(dest, projectName);
192
218
  await fs.writeFile(path.join(dest, "README.md"), readMe);
193
219
  if (installDeps) {
@@ -219,65 +245,130 @@ async function copy(from, to, rename = (s) => s) {
219
245
  }
220
246
  }
221
247
  function createPackageJson(projectName, options) {
222
- const packageJson = {
248
+ if (options.template === "react-router") {
249
+ return {
250
+ name: projectName,
251
+ private: true,
252
+ type: "module",
253
+ scripts: {
254
+ build: "react-router build",
255
+ dev: "react-router dev",
256
+ start: "react-router-serve ./build/server/index.js",
257
+ typecheck: "react-router typegen && tsc"
258
+ },
259
+ dependencies: {
260
+ ...pick(versions, [
261
+ "@fumadocs/mdx-remote",
262
+ "fumadocs-core",
263
+ "fumadocs-ui"
264
+ ]),
265
+ ...pick(package_default.dependencies, [
266
+ "@react-router/node",
267
+ "@react-router/serve",
268
+ "gray-matter",
269
+ "isbot",
270
+ "react",
271
+ "react-dom",
272
+ "react-router",
273
+ "shiki"
274
+ ])
275
+ },
276
+ devDependencies: pick(package_default.dependencies, [
277
+ "@react-router/dev",
278
+ "@tailwindcss/vite",
279
+ "@types/node",
280
+ "@types/react",
281
+ "@types/react-dom",
282
+ "react-router-devtools",
283
+ "tailwindcss",
284
+ "typescript",
285
+ "vite",
286
+ "vite-tsconfig-paths"
287
+ ])
288
+ };
289
+ }
290
+ if (options.template === "tanstack-start") {
291
+ return {
292
+ name: projectName,
293
+ type: "module",
294
+ scripts: {
295
+ dev: "vinxi dev",
296
+ build: "NODE_ENV=production vinxi build",
297
+ start: "vinxi start"
298
+ },
299
+ private: true,
300
+ dependencies: {
301
+ ...pick(versions, [
302
+ "@fumadocs/mdx-remote",
303
+ "fumadocs-ui",
304
+ "fumadocs-core"
305
+ ]),
306
+ ...pick(package_default.dependencies, [
307
+ "@tanstack/react-router",
308
+ "@tanstack/react-start",
309
+ "fast-glob",
310
+ "gray-matter",
311
+ "react",
312
+ "react-dom",
313
+ "vinxi"
314
+ ])
315
+ },
316
+ devDependencies: pick(package_default.dependencies, [
317
+ "@tailwindcss/vite",
318
+ "@types/react",
319
+ "@types/react-dom",
320
+ "@vitejs/plugin-react",
321
+ "tailwindcss",
322
+ "typescript",
323
+ "vite",
324
+ "vite-tsconfig-paths"
325
+ ])
326
+ };
327
+ }
328
+ return {
223
329
  name: projectName,
224
330
  version: "0.0.0",
225
331
  private: true,
226
332
  scripts: {
227
333
  build: "next build",
228
334
  dev: "next dev",
229
- start: "next start"
335
+ start: "next start",
336
+ ...options.template === "+next+fuma-docs-mdx" ? {
337
+ postinstall: "fumadocs-mdx"
338
+ } : null
230
339
  },
231
340
  dependencies: {
232
341
  ...pick(package_default.dependencies, ["next", "react", "react-dom"]),
233
- ...pick(versions, ["fumadocs-ui", "fumadocs-core"])
342
+ ...pick(versions, ["fumadocs-ui", "fumadocs-core"]),
343
+ ...options.template === "+next+content-collections" ? {
344
+ ...pick(package_default.dependencies, [
345
+ "@content-collections/mdx",
346
+ "@content-collections/core",
347
+ "@content-collections/next"
348
+ ]),
349
+ ...pick(versions, ["@fumadocs/content-collections"])
350
+ } : null,
351
+ ...options.template === "+next+fuma-docs-mdx" ? pick(versions, ["fumadocs-mdx"]) : null
234
352
  },
235
- devDependencies: pick(package_default.dependencies, [
236
- "@types/node",
237
- "@types/react",
238
- "@types/react-dom",
239
- "typescript"
240
- ])
241
- };
242
- if (options.template === "content-collections") {
243
- Object.assign(
244
- packageJson.dependencies,
245
- pick(package_default.dependencies, [
246
- "@content-collections/mdx",
247
- "@content-collections/core",
248
- "@content-collections/next"
353
+ devDependencies: {
354
+ ...pick(package_default.dependencies, [
355
+ "@types/node",
356
+ "@types/react",
357
+ "@types/react-dom",
358
+ "typescript",
359
+ "@types/mdx"
249
360
  ]),
250
- pick(versions, ["@fumadocs/content-collections"])
251
- );
252
- }
253
- if (options.template === "fuma-docs-mdx") {
254
- packageJson.scripts.postinstall = "fumadocs-mdx";
255
- Object.assign(
256
- packageJson.dependencies,
257
- pick(versions, ["fumadocs-mdx"])
258
- );
259
- Object.assign(
260
- packageJson.devDependencies,
261
- pick(package_default.dependencies, ["@types/mdx"])
262
- );
263
- }
264
- if (options.tailwindcss) {
265
- Object.assign(
266
- packageJson.devDependencies,
267
- pick(package_default.dependencies, [
361
+ ...options.tailwindcss ? pick(package_default.dependencies, [
268
362
  "@tailwindcss/postcss",
269
363
  "tailwindcss",
270
364
  "postcss"
271
- ])
272
- );
273
- }
274
- if (options.eslint) {
275
- Object.assign(packageJson.devDependencies, {
276
- eslint: "^8",
277
- "eslint-config-next": package_default.dependencies.next
278
- });
279
- }
280
- return JSON.stringify(packageJson, void 0, 2);
365
+ ]) : null,
366
+ ...options.eslint ? {
367
+ eslint: "^8",
368
+ "eslint-config-next": package_default.dependencies.next
369
+ } : null
370
+ }
371
+ };
281
372
  }
282
373
  function pick(obj, keys) {
283
374
  const result = {};
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  create
3
- } from "./chunk-K4QGKCVK.js";
3
+ } from "./chunk-LCUIXVWC.js";
4
4
  export {
5
5
  create
6
6
  };
package/dist/index.js CHANGED
@@ -3,10 +3,9 @@ import {
3
3
  create,
4
4
  cwd,
5
5
  getPackageManager
6
- } from "./chunk-K4QGKCVK.js";
6
+ } from "./chunk-LCUIXVWC.js";
7
7
 
8
8
  // src/index.ts
9
- import { existsSync } from "node:fs";
10
9
  import fs from "node:fs/promises";
11
10
  import path from "node:path";
12
11
  import {
@@ -32,23 +31,43 @@ async function main() {
32
31
  defaultValue: "my-app"
33
32
  }),
34
33
  template: () => select({
35
- message: "Choose a content source",
36
- initialValue: "fuma-docs-mdx",
34
+ message: "Choose a template",
35
+ initialValue: "+next+fuma-docs-mdx",
37
36
  options: [
38
37
  {
39
- value: "fuma-docs-mdx",
40
- label: "Fumadocs MDX",
38
+ value: "+next+fuma-docs-mdx",
39
+ label: "Next.js: Fumadocs MDX",
41
40
  hint: "recommended"
42
41
  },
43
- { value: "content-collections", label: "Content Collections" }
42
+ {
43
+ value: "+next+content-collections",
44
+ label: "Next.js: Content Collections"
45
+ },
46
+ {
47
+ value: "react-router",
48
+ label: "React Router: MDX Remote"
49
+ },
50
+ {
51
+ value: "tanstack-start",
52
+ label: "Tanstack Start: MDX Remote",
53
+ hint: "Experimental"
54
+ }
44
55
  ]
45
56
  }),
46
- src: () => confirm({ message: "Use `/src` directory?", initialValue: false }),
47
- tailwindcss: () => confirm({ message: "Use Tailwind CSS for styling?" }),
48
- eslint: () => confirm({
49
- message: "Add default ESLint configuration?",
50
- initialValue: false
51
- }),
57
+ src: (v) => {
58
+ if (!v.results.template?.startsWith("+next")) return;
59
+ return confirm({
60
+ message: "Use `/src` directory?",
61
+ initialValue: false
62
+ });
63
+ },
64
+ eslint: (v) => {
65
+ if (!v.results.template?.startsWith("+next")) return;
66
+ return confirm({
67
+ message: "Add default ESLint configuration?",
68
+ initialValue: false
69
+ });
70
+ },
52
71
  installDeps: () => confirm({
53
72
  message: `Do you want to install packages automatically? (detected as ${manager})`
54
73
  })
@@ -62,9 +81,10 @@ async function main() {
62
81
  );
63
82
  const projectName = options.name.toLowerCase().replace(/\s/, "-");
64
83
  const dest = path.resolve(cwd, projectName);
65
- if (existsSync(dest)) {
84
+ const destDir = await fs.readdir(dest).catch(() => null);
85
+ if (destDir && destDir.length > 0) {
66
86
  const del = await confirm({
67
- message: `${projectName} already exists, do you want to delete it?`
87
+ message: `directory ${projectName} already exists, do you want to delete its files?`
68
88
  });
69
89
  if (isCancel(del)) {
70
90
  cancel();
@@ -72,24 +92,28 @@ async function main() {
72
92
  }
73
93
  if (del) {
74
94
  const info2 = spinner();
75
- info2.start(`Deleting ${projectName}`);
76
- await fs.rm(dest, {
77
- recursive: true,
78
- force: true
79
- });
80
- info2.stop(`Deleted ${projectName}`);
95
+ info2.start(`Deleting files in ${projectName}`);
96
+ await Promise.all(
97
+ destDir.map((item) => {
98
+ return fs.rm(path.join(dest, item), {
99
+ recursive: true,
100
+ force: true
101
+ });
102
+ })
103
+ );
104
+ info2.stop(`Deleted files in ${projectName}`);
81
105
  }
82
106
  }
83
107
  const info = spinner();
84
108
  info.start(`Generating Project`);
85
109
  await create({
86
110
  packageManager: manager,
87
- tailwindcss: options.tailwindcss,
111
+ tailwindcss: true,
88
112
  template: options.template,
89
113
  outputDir: dest,
90
114
  installDeps: options.installDeps,
91
- eslint: options.eslint,
92
- useSrcDir: options.src,
115
+ eslint: options.eslint === true,
116
+ useSrcDir: options.src === true,
93
117
  log: (message) => {
94
118
  info.message(message);
95
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fumadocs-app",
3
- "version": "15.1.3",
3
+ "version": "15.2.1",
4
4
  "description": "Create a new documentation site with Fumadocs",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/cross-spawn": "^6.0.6",
28
- "@types/node": "22.13.11",
28
+ "@types/node": "22.13.14",
29
29
  "fast-glob": "^3.3.3",
30
30
  "eslint-config-custom": "0.0.0",
31
31
  "tsconfig": "0.0.0"
@@ -0,0 +1,12 @@
1
+ This is a React Router application generated with
2
+ [Create Fumadocs](https://github.com/fuma-nama/fumadocs).
3
+
4
+ Run development server:
5
+
6
+ ```bash
7
+ npm run dev
8
+ # or
9
+ pnpm dev
10
+ # or
11
+ yarn dev
12
+ ```
@@ -0,0 +1,6 @@
1
+ @import 'tailwindcss';
2
+ @import 'fumadocs-ui/css/neutral.css';
3
+ @import 'fumadocs-ui/css/preset.css';
4
+
5
+ /* path of `fumadocs-ui` relative to the CSS file */
6
+ @source '../node_modules/fumadocs-ui/dist/**/*.js';
@@ -0,0 +1,63 @@
1
+ import type { Route } from './+types/page';
2
+ import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3
+ import {
4
+ DocsBody,
5
+ DocsDescription,
6
+ DocsPage,
7
+ DocsTitle,
8
+ } from 'fumadocs-ui/page';
9
+ import { source } from '@/source';
10
+ import defaultMdxComponents from 'fumadocs-ui/mdx';
11
+ import { executeMdxSync } from '@fumadocs/mdx-remote/client';
12
+ import type { PageTree } from 'fumadocs-core/server';
13
+ import { createCompiler } from '@fumadocs/mdx-remote';
14
+ import * as path from 'node:path';
15
+
16
+ export function meta({}: Route.MetaArgs) {
17
+ return [
18
+ { title: 'New React Router App' },
19
+ { name: 'description', content: 'Welcome to React Router!' },
20
+ ];
21
+ }
22
+ const compiler = createCompiler({
23
+ development: false,
24
+ });
25
+
26
+ export async function loader({ params }: Route.LoaderArgs) {
27
+ const slugs = params['*'].split('/').filter((v) => v.length > 0);
28
+ const page = source.getPage(slugs);
29
+ if (!page) throw new Error('Not found');
30
+
31
+ const compiled = await compiler.compileFile({
32
+ path: path.resolve('content/docs', page.file.path),
33
+ value: page.data.content,
34
+ });
35
+
36
+ return {
37
+ page,
38
+ compiled: compiled.toString(),
39
+ tree: source.pageTree,
40
+ };
41
+ }
42
+
43
+ export default function Page(props: Route.ComponentProps) {
44
+ const { page, compiled, tree } = props.loaderData;
45
+ const { default: Mdx, toc } = executeMdxSync(compiled);
46
+
47
+ return (
48
+ <DocsLayout
49
+ nav={{
50
+ title: 'React Router',
51
+ }}
52
+ tree={tree as PageTree.Root}
53
+ >
54
+ <DocsPage toc={toc}>
55
+ <DocsTitle>{page.data.title}</DocsTitle>
56
+ <DocsDescription>{page.data.description}</DocsDescription>
57
+ <DocsBody>
58
+ <Mdx components={defaultMdxComponents} />
59
+ </DocsBody>
60
+ </DocsPage>
61
+ </DocsLayout>
62
+ );
63
+ }
@@ -0,0 +1,18 @@
1
+ import type { Route } from './+types/search';
2
+ import { createSearchAPI } from 'fumadocs-core/search/server';
3
+ import { source } from '@/source';
4
+ import { structure } from 'fumadocs-core/mdx-plugins';
5
+
6
+ const server = createSearchAPI('advanced', {
7
+ indexes: source.getPages().map((page) => ({
8
+ id: page.url,
9
+ url: page.url,
10
+ title: page.data.title ?? '',
11
+ description: page.data.description,
12
+ structuredData: structure(page.data.content),
13
+ })),
14
+ });
15
+
16
+ export async function loader({ request }: Route.LoaderArgs) {
17
+ return server.GET(request);
18
+ }
@@ -0,0 +1,78 @@
1
+ import {
2
+ isRouteErrorResponse,
3
+ Links,
4
+ Meta,
5
+ Outlet,
6
+ Scripts,
7
+ ScrollRestoration,
8
+ } from 'react-router';
9
+ import { RootProvider } from 'fumadocs-ui/provider/base';
10
+ import { ReactRouterProvider } from 'fumadocs-core/framework/react-router';
11
+ import type { Route } from './+types/root';
12
+ import './app.css';
13
+
14
+ export const links: Route.LinksFunction = () => [
15
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
16
+ {
17
+ rel: 'preconnect',
18
+ href: 'https://fonts.gstatic.com',
19
+ crossOrigin: 'anonymous',
20
+ },
21
+ {
22
+ rel: 'stylesheet',
23
+ href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
24
+ },
25
+ ];
26
+
27
+ export function Layout({ children }: { children: React.ReactNode }) {
28
+ return (
29
+ <html lang="en" suppressHydrationWarning>
30
+ <head>
31
+ <meta charSet="utf-8" />
32
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
33
+ <Meta />
34
+ <Links />
35
+ </head>
36
+ <body>
37
+ <ReactRouterProvider>
38
+ <RootProvider>{children}</RootProvider>
39
+ </ReactRouterProvider>
40
+ <ScrollRestoration />
41
+ <Scripts />
42
+ </body>
43
+ </html>
44
+ );
45
+ }
46
+
47
+ export default function App() {
48
+ return <Outlet />;
49
+ }
50
+
51
+ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
52
+ let message = 'Oops!';
53
+ let details = 'An unexpected error occurred.';
54
+ let stack: string | undefined;
55
+
56
+ if (isRouteErrorResponse(error)) {
57
+ message = error.status === 404 ? '404' : 'Error';
58
+ details =
59
+ error.status === 404
60
+ ? 'The requested page could not be found.'
61
+ : error.statusText || details;
62
+ } else if (import.meta.env.DEV && error && error instanceof Error) {
63
+ details = error.message;
64
+ stack = error.stack;
65
+ }
66
+
67
+ return (
68
+ <main className="pt-16 p-4 container mx-auto">
69
+ <h1>{message}</h1>
70
+ <p>{details}</p>
71
+ {stack && (
72
+ <pre className="w-full p-4 overflow-x-auto">
73
+ <code>{stack}</code>
74
+ </pre>
75
+ )}
76
+ </main>
77
+ );
78
+ }
@@ -0,0 +1,34 @@
1
+ import type { Route } from './+types/home';
2
+ import { HomeLayout } from 'fumadocs-ui/layouts/home';
3
+ import { Link } from 'react-router';
4
+
5
+ export function meta({}: Route.MetaArgs) {
6
+ return [
7
+ { title: 'New React Router App' },
8
+ { name: 'description', content: 'Welcome to React Router!' },
9
+ ];
10
+ }
11
+
12
+ export default function Home() {
13
+ return (
14
+ <HomeLayout
15
+ className="text-center"
16
+ nav={{
17
+ title: 'React Router',
18
+ }}
19
+ >
20
+ <div className="py-12">
21
+ <h1 className="text-xl font-bold mb-2">Fumadocs on React Router.</h1>
22
+ <p className="text-fd-muted-foreground mb-8">
23
+ The truly flexible docs framework on React.js.
24
+ </p>
25
+ <Link
26
+ className="text-sm bg-fd-primary text-fd-primary-foreground rounded-full font-medium px-4 py-2.5"
27
+ to="/docs"
28
+ >
29
+ Open Docs
30
+ </Link>
31
+ </div>
32
+ </HomeLayout>
33
+ );
34
+ }
@@ -0,0 +1,7 @@
1
+ import { type RouteConfig, index, route } from '@react-router/dev/routes';
2
+
3
+ export default [
4
+ index('routes/home.tsx'),
5
+ route('docs/*', 'docs/page.tsx'),
6
+ route('api/search', 'docs/search.ts'),
7
+ ] satisfies RouteConfig;