create-fumadocs-app 15.1.2 → 15.2.0

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 (34) hide show
  1. package/dist/{chunk-64VYJ6SV.js → chunk-R4Y2JVJN.js} +124 -69
  2. package/dist/create-app.js +1 -1
  3. package/dist/index.js +33 -20
  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/{+shared → +next}/README.md +0 -0
  21. /package/template/{+shared → +next}/app/(home)/layout.tsx +0 -0
  22. /package/template/{+shared → +next}/app/(home)/page.tsx +0 -0
  23. /package/template/{+shared → +next}/app/api/search/route.ts +0 -0
  24. /package/template/{+shared → +next}/app/docs/layout.tsx +0 -0
  25. /package/template/{+shared → +next}/app/layout.config.tsx +0 -0
  26. /package/template/{+shared → +next}/app/layout.tsx +0 -0
  27. /package/template/{+shared → +next}/content/docs/index.mdx +0 -0
  28. /package/template/{+shared → +next}/content/docs/test.mdx +0 -0
  29. /package/template/{+shared → +next}/example.gitignore +0 -0
  30. /package/template/{+eslint → +next+eslint}/.eslintrc.json +0 -0
  31. /package/template/{+tailwindcss → +next+tailwindcss}/app/(home)/page.tsx +0 -0
  32. /package/template/{+tailwindcss → +next+tailwindcss}/app/global.css +0 -0
  33. /package/template/{+tailwindcss → +next+tailwindcss}/app/layout.tsx +0 -0
  34. /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.2", "fumadocs-ui": "15.1.2", "fumadocs-mdx": "11.5.7", "@fumadocs/content-collections": "1.1.8" };
63
+ var versions = { "fumadocs-core": "15.2.0", "fumadocs-ui": "15.2.0", "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,28 @@ 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"
87
98
  }
88
99
  };
89
100
 
@@ -137,35 +148,56 @@ async function create(options) {
137
148
  } = options;
138
149
  const projectName = path.basename(options.outputDir);
139
150
  const dest = path.resolve(cwd, options.outputDir);
151
+ const isNext = options.template === "content-collections" || options.template === "fuma-docs-mdx";
140
152
  function defaultRename(file) {
141
153
  file = file.replace("example.gitignore", ".gitignore");
142
154
  if (!options.useSrcDir) {
143
155
  return file;
144
156
  }
145
- for (const dir of ["app", "lib"]) {
146
- const relative = path.relative(path.join(dest, dir), file);
157
+ if (isNext) {
158
+ for (const dir of ["app", "lib"]) {
159
+ const relative = path.relative(path.join(dest, dir), file);
160
+ if (!relative.startsWith(`..${path.sep}`)) {
161
+ return path.join(dest, "src", dir, relative);
162
+ }
163
+ }
164
+ } else if (options.template === "react-router") {
165
+ const relative = path.relative(path.join(dest, "app"), file);
147
166
  if (!relative.startsWith(`..${path.sep}`)) {
148
- return path.join(dest, "src", dir, relative);
167
+ return path.join(dest, "src", relative);
149
168
  }
150
169
  }
151
170
  return file;
152
171
  }
153
- await copy(path.join(sourceDir, `template/+shared`), dest, defaultRename);
172
+ if (isNext) {
173
+ await copy(path.join(sourceDir, `template/+next`), dest, defaultRename);
174
+ }
154
175
  await copy(
155
176
  path.join(sourceDir, `template/${options.template}`),
156
177
  dest,
157
178
  defaultRename
158
179
  );
159
- if (options.tailwindcss) {
180
+ if (isNext && options.tailwindcss) {
160
181
  await copy(
161
- path.join(sourceDir, `template/+tailwindcss`),
182
+ path.join(sourceDir, `template/+next+tailwindcss`),
162
183
  dest,
163
184
  defaultRename
164
185
  );
186
+ if (options.useSrcDir) {
187
+ const cssPath = path.join(dest, "src/app/global.css");
188
+ await fs.writeFile(
189
+ cssPath,
190
+ (await fs.readFile(cssPath)).toString().replace("../", "../../")
191
+ );
192
+ }
165
193
  log("Configured Tailwind CSS");
166
194
  }
167
- if (options.eslint) {
168
- await copy(path.join(sourceDir, `template/+eslint`), dest, defaultRename);
195
+ if (isNext && options.eslint) {
196
+ await copy(
197
+ path.join(sourceDir, `template/+next+eslint`),
198
+ dest,
199
+ defaultRename
200
+ );
169
201
  log("Configured ESLint");
170
202
  }
171
203
  if (options.useSrcDir) {
@@ -178,16 +210,12 @@ async function create(options) {
178
210
  });
179
211
  }
180
212
  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
213
  }
189
214
  const packageJson = createPackageJson(projectName, options);
190
- await fs.writeFile(path.join(dest, "package.json"), packageJson);
215
+ await fs.writeFile(
216
+ path.join(dest, "package.json"),
217
+ JSON.stringify(packageJson, null, 2)
218
+ );
191
219
  const readMe = await getReadme(dest, projectName);
192
220
  await fs.writeFile(path.join(dest, "README.md"), readMe);
193
221
  if (installDeps) {
@@ -219,65 +247,92 @@ async function copy(from, to, rename = (s) => s) {
219
247
  }
220
248
  }
221
249
  function createPackageJson(projectName, options) {
222
- const packageJson = {
250
+ if (options.template === "react-router") {
251
+ return {
252
+ name: projectName,
253
+ private: true,
254
+ type: "module",
255
+ scripts: {
256
+ build: "react-router build",
257
+ dev: "react-router dev",
258
+ start: "react-router-serve ./build/server/index.js",
259
+ typecheck: "react-router typegen && tsc"
260
+ },
261
+ dependencies: {
262
+ ...pick(versions, [
263
+ "@fumadocs/mdx-remote",
264
+ "fumadocs-core",
265
+ "fumadocs-ui"
266
+ ]),
267
+ ...pick(package_default.dependencies, [
268
+ "@react-router/node",
269
+ "@react-router/serve",
270
+ "gray-matter",
271
+ "isbot",
272
+ "react",
273
+ "react-dom",
274
+ "react-router",
275
+ "shiki"
276
+ ])
277
+ },
278
+ devDependencies: pick(package_default.dependencies, [
279
+ "@react-router/dev",
280
+ "@tailwindcss/vite",
281
+ "@types/node",
282
+ "@types/react",
283
+ "@types/react-dom",
284
+ "react-router-devtools",
285
+ "tailwindcss",
286
+ "typescript",
287
+ "vite",
288
+ "vite-tsconfig-paths"
289
+ ])
290
+ };
291
+ }
292
+ return {
223
293
  name: projectName,
224
294
  version: "0.0.0",
225
295
  private: true,
226
296
  scripts: {
227
297
  build: "next build",
228
298
  dev: "next dev",
229
- start: "next start"
299
+ start: "next start",
300
+ ...options.template === "fuma-docs-mdx" ? {
301
+ postinstall: "fumadocs-mdx"
302
+ } : null
230
303
  },
231
304
  dependencies: {
232
305
  ...pick(package_default.dependencies, ["next", "react", "react-dom"]),
233
- ...pick(versions, ["fumadocs-ui", "fumadocs-core"])
306
+ ...pick(versions, ["fumadocs-ui", "fumadocs-core"]),
307
+ ...options.template === "content-collections" ? {
308
+ ...pick(package_default.dependencies, [
309
+ "@content-collections/mdx",
310
+ "@content-collections/core",
311
+ "@content-collections/next"
312
+ ]),
313
+ ...pick(versions, ["@fumadocs/content-collections"])
314
+ } : null,
315
+ ...options.template === "fuma-docs-mdx" ? pick(versions, ["fumadocs-mdx"]) : null
234
316
  },
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"
317
+ devDependencies: {
318
+ ...pick(package_default.dependencies, [
319
+ "@types/node",
320
+ "@types/react",
321
+ "@types/react-dom",
322
+ "typescript"
249
323
  ]),
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, [
324
+ ...options.tailwindcss ? pick(package_default.dependencies, [
268
325
  "@tailwindcss/postcss",
269
326
  "tailwindcss",
270
327
  "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);
328
+ ]) : null,
329
+ ...options.template === "fuma-docs-mdx" ? pick(package_default.dependencies, ["@types/mdx"]) : null,
330
+ ...options.eslint ? {
331
+ eslint: "^8",
332
+ "eslint-config-next": package_default.dependencies.next
333
+ } : null
334
+ }
335
+ };
281
336
  }
282
337
  function pick(obj, keys) {
283
338
  const result = {};
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  create
3
- } from "./chunk-64VYJ6SV.js";
3
+ } from "./chunk-R4Y2JVJN.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-64VYJ6SV.js";
6
+ } from "./chunk-R4Y2JVJN.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,32 @@ async function main() {
32
31
  defaultValue: "my-app"
33
32
  }),
34
33
  template: () => select({
35
- message: "Choose a content source",
34
+ message: "Choose a template",
36
35
  initialValue: "fuma-docs-mdx",
37
36
  options: [
38
37
  {
39
38
  value: "fuma-docs-mdx",
40
- label: "Fumadocs MDX",
39
+ label: "Next.js: Fumadocs MDX",
41
40
  hint: "recommended"
42
41
  },
43
- { value: "content-collections", label: "Content Collections" }
42
+ {
43
+ value: "content-collections",
44
+ label: "Next.js: Content Collections"
45
+ },
46
+ {
47
+ value: "react-router",
48
+ label: "React Router: MDX Remote"
49
+ }
44
50
  ]
45
51
  }),
46
52
  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
- }),
53
+ eslint: (v) => {
54
+ if (v.results.template === "react-router") return;
55
+ return confirm({
56
+ message: "Add default ESLint configuration?",
57
+ initialValue: false
58
+ });
59
+ },
52
60
  installDeps: () => confirm({
53
61
  message: `Do you want to install packages automatically? (detected as ${manager})`
54
62
  })
@@ -62,9 +70,10 @@ async function main() {
62
70
  );
63
71
  const projectName = options.name.toLowerCase().replace(/\s/, "-");
64
72
  const dest = path.resolve(cwd, projectName);
65
- if (existsSync(dest)) {
73
+ const destDir = await fs.readdir(dest).catch(() => null);
74
+ if (destDir && destDir.length > 0) {
66
75
  const del = await confirm({
67
- message: `${projectName} already exists, do you want to delete it?`
76
+ message: `directory ${projectName} already exists, do you want to delete its files?`
68
77
  });
69
78
  if (isCancel(del)) {
70
79
  cancel();
@@ -72,23 +81,27 @@ async function main() {
72
81
  }
73
82
  if (del) {
74
83
  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}`);
84
+ info2.start(`Deleting files in ${projectName}`);
85
+ await Promise.all(
86
+ destDir.map((item) => {
87
+ return fs.rm(path.join(dest, item), {
88
+ recursive: true,
89
+ force: true
90
+ });
91
+ })
92
+ );
93
+ info2.stop(`Deleted files in ${projectName}`);
81
94
  }
82
95
  }
83
96
  const info = spinner();
84
97
  info.start(`Generating Project`);
85
98
  await create({
86
99
  packageManager: manager,
87
- tailwindcss: options.tailwindcss,
100
+ tailwindcss: true,
88
101
  template: options.template,
89
102
  outputDir: dest,
90
103
  installDeps: options.installDeps,
91
- eslint: options.eslint,
104
+ eslint: options.eslint === true,
92
105
  useSrcDir: options.src,
93
106
  log: (message) => {
94
107
  info.message(message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fumadocs-app",
3
- "version": "15.1.2",
3
+ "version": "15.2.0",
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
+
15
+ export function meta({}: Route.MetaArgs) {
16
+ return [
17
+ { title: 'New React Router App' },
18
+ { name: 'description', content: 'Welcome to React Router!' },
19
+ ];
20
+ }
21
+ const compiler = createCompiler({
22
+ development: false,
23
+ });
24
+
25
+ export async function loader({ params }: Route.LoaderArgs) {
26
+ const slugs = params['*'].split('/').filter((v) => v.length > 0);
27
+ const page = source.getPage(slugs);
28
+ if (!page) throw new Error('Not found');
29
+
30
+ const compiled = String(
31
+ await compiler.compileFile({
32
+ value: page.data.content,
33
+ }),
34
+ );
35
+
36
+ return {
37
+ page,
38
+ compiled,
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;
@@ -0,0 +1,60 @@
1
+ import {
2
+ loader,
3
+ type MetaData,
4
+ type PageData,
5
+ type Source,
6
+ type VirtualFile,
7
+ } from 'fumadocs-core/source';
8
+ import matter from 'gray-matter';
9
+ import * as path from 'node:path';
10
+
11
+ const files = Object.entries(
12
+ import.meta.glob<true, 'raw'>('/content/docs/**/*', {
13
+ eager: true,
14
+ query: '?raw',
15
+ import: 'default',
16
+ }),
17
+ );
18
+
19
+ const virtualFiles: VirtualFile[] = files.flatMap(([file, content]) => {
20
+ const ext = path.extname(file);
21
+ const virtualPath = path.relative(
22
+ 'content/docs',
23
+ path.join(process.cwd(), file),
24
+ );
25
+
26
+ if (ext === '.mdx' || ext === '.md') {
27
+ const parsed = matter(content);
28
+
29
+ return {
30
+ type: 'page',
31
+ path: virtualPath,
32
+ data: {
33
+ ...parsed.data,
34
+ content: parsed.content,
35
+ },
36
+ };
37
+ }
38
+
39
+ if (ext === '.json') {
40
+ return {
41
+ type: 'meta',
42
+ path: virtualPath,
43
+ data: JSON.parse(content),
44
+ };
45
+ }
46
+
47
+ return [];
48
+ });
49
+
50
+ export const source = loader({
51
+ source: {
52
+ files: virtualFiles,
53
+ } as Source<{
54
+ pageData: PageData & {
55
+ content: string;
56
+ };
57
+ metaData: MetaData;
58
+ }>,
59
+ baseUrl: '/docs',
60
+ });
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Hello World
3
+ description: |
4
+ Your first `document`
5
+ You'll love it!
6
+ ---
7
+
8
+ Hey there! Fumadocs is the docs framework that also works on React Router!
9
+
10
+ ## Heading
11
+
12
+ Hello World
13
+
14
+ <Cards>
15
+ <Card title="Learn more about React Router" href="https://reactrouter.com" />
16
+ <Card title="Learn more about Fumadocs" href="https://fumadocs.vercel.app" />
17
+ </Cards>
18
+
19
+ ```ts
20
+ console.log('I love React!');
21
+ ```
22
+
23
+ ### Heading
24
+
25
+ #### Heading
26
+
27
+ | Head | Description |
28
+ | ------------------------------- | ----------------------------------- |
29
+ | `hello` | Hello World |
30
+ | very **important** | Hey |
31
+ | _Surprisingly_ | Fumadocs |
32
+ | very long text that looks weird | hello world hello world hello world |
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: Fumadocs
3
+ description: You can just be minimal.
4
+ ---
5
+
6
+ ## Overview
7
+
8
+ Fumadocs is a docs framework.
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ /node_modules/
3
+
4
+ # React Router
5
+ /.react-router/
6
+ /build/
@@ -0,0 +1,9 @@
1
+ import type { Config } from '@react-router/dev/config';
2
+ import { source } from './app/source';
3
+
4
+ export default {
5
+ ssr: true,
6
+ async prerender({ getStaticPaths }) {
7
+ return [...getStaticPaths(), ...source.getPages().map((page) => page.url)];
8
+ },
9
+ } satisfies Config;
@@ -0,0 +1,27 @@
1
+ {
2
+ "include": [
3
+ "**/*",
4
+ "**/.server/**/*",
5
+ "**/.client/**/*",
6
+ ".react-router/types/**/*"
7
+ ],
8
+ "compilerOptions": {
9
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
10
+ "types": ["node", "vite/client"],
11
+ "target": "esnext",
12
+ "module": "esnext",
13
+ "moduleResolution": "bundler",
14
+ "jsx": "react-jsx",
15
+ "rootDirs": [".", "./.react-router/types"],
16
+ "baseUrl": ".",
17
+ "paths": {
18
+ "@/*": ["./app/*"]
19
+ },
20
+ "esModuleInterop": true,
21
+ "verbatimModuleSyntax": true,
22
+ "noEmit": true,
23
+ "resolveJsonModule": true,
24
+ "skipLibCheck": true,
25
+ "strict": true
26
+ }
27
+ }
@@ -0,0 +1,13 @@
1
+ import { reactRouter } from '@react-router/dev/vite';
2
+ import tailwindcss from '@tailwindcss/vite';
3
+ import { defineConfig } from 'vite';
4
+ import tsconfigPaths from 'vite-tsconfig-paths';
5
+
6
+ export default defineConfig({
7
+ build: {
8
+ rollupOptions: {
9
+ external: ['shiki'],
10
+ },
11
+ },
12
+ plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
13
+ });
File without changes
File without changes
File without changes
File without changes
File without changes