add: docs
This commit is contained in:
parent
cd2e9c589e
commit
3b6bed8c80
7
packages/docs/.gitignore
vendored
Normal file
7
packages/docs/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.DS_Store
|
||||
/node_modules/
|
||||
|
||||
# React Router
|
||||
/.react-router/
|
||||
/build/
|
||||
.source
|
||||
3
packages/docs/app/app.css
Normal file
3
packages/docs/app/app.css
Normal file
@ -0,0 +1,3 @@
|
||||
@import "tailwindcss";
|
||||
@import "fumadocs-ui/css/neutral.css";
|
||||
@import "fumadocs-ui/css/preset.css";
|
||||
51
packages/docs/app/docs/page.tsx
Normal file
51
packages/docs/app/docs/page.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import browserCollections from "fumadocs-mdx:collections/browser";
|
||||
import { useFumadocsLoader } from "fumadocs-core/source/client";
|
||||
import { DocsLayout } from "fumadocs-ui/layouts/docs";
|
||||
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/layouts/docs/page";
|
||||
import defaultMdxComponents from "fumadocs-ui/mdx";
|
||||
import { baseOptions } from "@/lib/layout.shared";
|
||||
import { source } from "@/lib/source";
|
||||
import type { Route } from "./+types/page";
|
||||
|
||||
export async function loader({ params }: Route.LoaderArgs) {
|
||||
const slugs = params["*"].split("/").filter((v) => v.length > 0);
|
||||
const page = source.getPage(slugs);
|
||||
if (!page) throw new Response("Not found", { status: 404 });
|
||||
|
||||
return {
|
||||
path: page.path,
|
||||
pageTree: await source.serializePageTree(source.getPageTree()),
|
||||
};
|
||||
}
|
||||
|
||||
const clientLoader = browserCollections.docs.createClientLoader({
|
||||
component(
|
||||
{ toc, frontmatter, default: Mdx },
|
||||
// you can define props for the `<Content />` component
|
||||
props?: {
|
||||
className?: string;
|
||||
}
|
||||
) {
|
||||
return (
|
||||
<DocsPage toc={toc} {...props}>
|
||||
<title>{frontmatter.title}</title>
|
||||
<meta name="description" content={frontmatter.description} />
|
||||
<DocsTitle>{frontmatter.title}</DocsTitle>
|
||||
<DocsDescription>{frontmatter.description}</DocsDescription>
|
||||
<DocsBody>
|
||||
<Mdx components={{ ...defaultMdxComponents }} />
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default function Page({ loaderData }: Route.ComponentProps) {
|
||||
const { path, pageTree } = useFumadocsLoader(loaderData);
|
||||
|
||||
return (
|
||||
<DocsLayout {...baseOptions()} tree={pageTree}>
|
||||
{clientLoader.useContent(path)}
|
||||
</DocsLayout>
|
||||
);
|
||||
}
|
||||
12
packages/docs/app/docs/search.ts
Normal file
12
packages/docs/app/docs/search.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { createFromSource } from "fumadocs-core/search/server";
|
||||
import { source } from "@/lib/source";
|
||||
import type { Route } from "./+types/search";
|
||||
|
||||
const server = createFromSource(source, {
|
||||
// https://docs.orama.com/docs/orama-js/supported-languages
|
||||
language: "english",
|
||||
});
|
||||
|
||||
export async function loader({ request }: Route.LoaderArgs) {
|
||||
return server.GET(request);
|
||||
}
|
||||
9
packages/docs/app/lib/layout.shared.tsx
Normal file
9
packages/docs/app/lib/layout.shared.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
|
||||
|
||||
export function baseOptions(): BaseLayoutProps {
|
||||
return {
|
||||
nav: {
|
||||
title: "React Router",
|
||||
},
|
||||
};
|
||||
}
|
||||
7
packages/docs/app/lib/source.ts
Normal file
7
packages/docs/app/lib/source.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { docs } from "fumadocs-mdx:collections/server";
|
||||
import { loader } from "fumadocs-core/source";
|
||||
|
||||
export const source = loader({
|
||||
source: docs.toFumadocsSource(),
|
||||
baseUrl: "/docs",
|
||||
});
|
||||
75
packages/docs/app/root.tsx
Normal file
75
packages/docs/app/root.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { RootProvider } from "fumadocs-ui/provider/react-router";
|
||||
import {
|
||||
isRouteErrorResponse,
|
||||
Links,
|
||||
Meta,
|
||||
Outlet,
|
||||
Scripts,
|
||||
ScrollRestoration,
|
||||
} from "react-router";
|
||||
import type { Route } from "./+types/root";
|
||||
import "./app.css";
|
||||
|
||||
export const links: Route.LinksFunction = () => [
|
||||
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
|
||||
{
|
||||
rel: "preconnect",
|
||||
href: "https://fonts.gstatic.com",
|
||||
crossOrigin: "anonymous",
|
||||
},
|
||||
{
|
||||
rel: "stylesheet",
|
||||
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
|
||||
},
|
||||
];
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body className="flex flex-col min-h-screen">
|
||||
<RootProvider>{children}</RootProvider>
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
|
||||
let message = "Oops!";
|
||||
let details = "An unexpected error occurred.";
|
||||
let stack: string | undefined;
|
||||
|
||||
if (isRouteErrorResponse(error)) {
|
||||
message = error.status === 404 ? "404" : "Error";
|
||||
details =
|
||||
error.status === 404
|
||||
? "The requested page could not be found."
|
||||
: error.statusText || details;
|
||||
} else if (import.meta.env.DEV && error && error instanceof Error) {
|
||||
details = error.message;
|
||||
stack = error.stack;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="pt-16 p-4 w-full max-w-350 mx-auto">
|
||||
<h1>{message}</h1>
|
||||
<p>{details}</p>
|
||||
{stack && (
|
||||
<pre className="w-full p-4 overflow-x-auto">
|
||||
<code>{stack}</code>
|
||||
</pre>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
7
packages/docs/app/routes.ts
Normal file
7
packages/docs/app/routes.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { index, type RouteConfig, route } from "@react-router/dev/routes";
|
||||
|
||||
export default [
|
||||
index("routes/home.tsx"),
|
||||
route("docs/*", "docs/page.tsx"),
|
||||
route("api/search", "docs/search.ts"),
|
||||
] satisfies RouteConfig;
|
||||
30
packages/docs/app/routes/home.tsx
Normal file
30
packages/docs/app/routes/home.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { HomeLayout } from "fumadocs-ui/layouts/home";
|
||||
import { Link } from "react-router";
|
||||
import { baseOptions } from "@/lib/layout.shared";
|
||||
import type { Route } from "./+types/home";
|
||||
|
||||
export function meta(_: Route.MetaArgs) {
|
||||
return [
|
||||
{ title: "New React Router App" },
|
||||
{ name: "description", content: "Welcome to React Router!" },
|
||||
];
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<HomeLayout {...baseOptions()}>
|
||||
<div className="p-4 flex flex-col items-center justify-center text-center flex-1">
|
||||
<h1 className="text-xl font-bold mb-2">Fumadocs on React Router.</h1>
|
||||
<p className="text-fd-muted-foreground mb-4">
|
||||
The truly flexible docs framework on React.js.
|
||||
</p>
|
||||
<Link
|
||||
className="text-sm bg-fd-primary text-fd-primary-foreground rounded-full font-medium px-4 py-2.5"
|
||||
to="/docs"
|
||||
>
|
||||
Open Docs
|
||||
</Link>
|
||||
</div>
|
||||
</HomeLayout>
|
||||
);
|
||||
}
|
||||
1180
packages/docs/bun.lock
Normal file
1180
packages/docs/bun.lock
Normal file
File diff suppressed because it is too large
Load Diff
32
packages/docs/content/index.mdx
Normal file
32
packages/docs/content/index.mdx
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Hello World
|
||||
description: |
|
||||
Your first `document`
|
||||
You'll love it!
|
||||
---
|
||||
|
||||
Hey there! Fumadocs is the docs framework that also works on React Router!
|
||||
|
||||
## Heading
|
||||
|
||||
Hello World
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about React Router" href="https://reactrouter.com" />
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
|
||||
</Cards>
|
||||
|
||||
```ts
|
||||
console.log('I love React!');
|
||||
```
|
||||
|
||||
### Heading
|
||||
|
||||
#### Heading
|
||||
|
||||
| Head | Description |
|
||||
| ------------------------------- | ----------------------------------- |
|
||||
| `hello` | Hello World |
|
||||
| very **important** | Hey |
|
||||
| _Surprisingly_ | Fumadocs |
|
||||
| very long text that looks weird | hello world hello world hello world |
|
||||
3
packages/docs/content/meta.json
Normal file
3
packages/docs/content/meta.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"pages": ["index", "..."]
|
||||
}
|
||||
24
packages/docs/content/test.mdx
Normal file
24
packages/docs/content/test.mdx
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Test
|
||||
description: A document to test Fumadocs
|
||||
---
|
||||
|
||||
Hey there!
|
||||
|
||||
## Cards
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about Next.js" href="https://nextjs.org/docs" />
|
||||
<Card title="Learn more about Fumadocs" href="https://fumadocs.dev" />
|
||||
</Cards>
|
||||
|
||||
### CodeBlock
|
||||
|
||||
```js
|
||||
console.log('Hello World');
|
||||
```
|
||||
|
||||
#### List
|
||||
|
||||
- Hello
|
||||
- World
|
||||
36
packages/docs/package.json
Normal file
36
packages/docs/package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "docs",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "react-router build",
|
||||
"dev": "react-router dev",
|
||||
"start": "react-router-serve ./build/server/index.js",
|
||||
"types:check": "react-router typegen && fumadocs-mdx && tsc --noEmit",
|
||||
"postinstall": "fumadocs-mdx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-router/node": "^7.12.0",
|
||||
"@react-router/serve": "^7.12.0",
|
||||
"fumadocs-core": "16.4.7",
|
||||
"fumadocs-mdx": "14.2.6",
|
||||
"fumadocs-ui": "16.4.7",
|
||||
"isbot": "^5.1.32",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-router": "^7.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@react-router/dev": "^7.12.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/node": "^25.0.5",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"react-router-devtools": "^6.1.0",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
"vite-tsconfig-paths": "^6.0.4"
|
||||
}
|
||||
}
|
||||
23
packages/docs/react-router.config.ts
Normal file
23
packages/docs/react-router.config.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { glob } from "node:fs/promises";
|
||||
import type { Config } from "@react-router/dev/config";
|
||||
import { createGetUrl, getSlugs } from "fumadocs-core/source";
|
||||
|
||||
const getUrl = createGetUrl("/docs");
|
||||
|
||||
export default {
|
||||
ssr: true,
|
||||
async prerender({ getStaticPaths }) {
|
||||
const paths: string[] = [];
|
||||
const excluded: string[] = ["/api/search"];
|
||||
|
||||
for (const path of getStaticPaths()) {
|
||||
if (!excluded.includes(path)) paths.push(path);
|
||||
}
|
||||
|
||||
for await (const entry of glob("**/*.mdx", { cwd: "content/docs" })) {
|
||||
paths.push(getUrl(getSlugs(entry)));
|
||||
}
|
||||
|
||||
return paths;
|
||||
},
|
||||
} satisfies Config;
|
||||
7
packages/docs/source.config.ts
Normal file
7
packages/docs/source.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
|
||||
|
||||
export const docs = defineDocs({
|
||||
dir: "content",
|
||||
});
|
||||
|
||||
export default defineConfig();
|
||||
23
packages/docs/tsconfig.json
Normal file
23
packages/docs/tsconfig.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"include": ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"types": ["node", "vite/client"],
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"rootDirs": [".", "./.react-router/types"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./app/*"],
|
||||
"fumadocs-mdx:collections/*": [".source/*"]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
17
packages/docs/vite.config.ts
Normal file
17
packages/docs/vite.config.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { reactRouter } from "@react-router/dev/vite";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import mdx from "fumadocs-mdx/vite";
|
||||
import { defineConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import * as MdxConfig from "./source.config";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
mdx(MdxConfig),
|
||||
tailwindcss(),
|
||||
reactRouter(),
|
||||
tsconfigPaths({
|
||||
root: __dirname,
|
||||
}),
|
||||
],
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user