Press: support route configs

This commit is contained in:
Fuma Nama
2026-05-05 16:39:20 +08:00
parent b4f22b6627
commit a1b9dfdb85
10 changed files with 145 additions and 44 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
"dev": "waku dev",
"build": "waku build",
"start": "waku start",
"types:check": "fumadocs-mdx && tsc --noEmit"
"types:check": "tsc --noEmit"
},
"dependencies": {
"@fumapress/core": "workspace:*",
+8
View File
@@ -4,6 +4,14 @@ import RootLayout from './root';
const router = createRouter({
root: RootLayout,
site: {
name: 'Example Site',
git: {
user: 'fuma-nama',
branch: 'main',
repo: 'fumadocs',
},
},
});
export default adapter(router.createPages());
+19 -1
View File
@@ -1,4 +1,22 @@
export interface Config {}
import type { ComponentType } from 'react';
import type { AppContext } from './lib/shared';
export interface Config {
site?: SiteConfig;
layouts?: Record<string, ComponentType<AppContext & Record<string, unknown>>>;
}
export interface SiteConfig {
name?: string;
git?: {
user: string;
repo: string;
branch: string;
/** the root directory of git repo */
rootDir?: string;
};
}
export function defineConfig(config: Config): Config {
return config;
+19 -12
View File
@@ -1,12 +1,19 @@
import * as waku from 'waku';
import { getLLMText, getPageImage, getPageMarkdownUrl, getSource } from './lib/source';
import {
getLLMText,
getPageImage,
getSource,
markdownPathToSlugs,
slugsToMarkdownPath,
} from './lib/source';
import { llms } from 'fumadocs-core/source/llms';
import { unstable_notFound } from 'waku/router/server';
import { appName } from './lib/shared';
import { AppContext, parseConfig } from './lib/shared';
import { createFromSource } from 'fumadocs-core/search/server';
import type { FC, ReactNode } from 'react';
import type { Config } from './config';
export interface RouterOptions {
export interface RouterOptions extends Config {
root?: FC<{ children: ReactNode }>;
}
@@ -15,6 +22,11 @@ export function createRouter(options: RouterOptions = {}): {
createPages: () => ReturnType<typeof waku.createPages>;
} {
const { root } = options;
const config = parseConfig(options);
const context: AppContext = {
config,
};
const createPages: typeof waku.createPages = (fns, options) => {
return waku.createPages(async (r) => {
const { createApi, createPage, createRoot } = r;
@@ -37,7 +49,7 @@ export function createRouter(options: RouterOptions = {}): {
staticPaths: source.getPages().map((page) => page.slugs),
async component({ slugs }) {
const mod = await import('./layouts/docs/page');
return mod.default({ slugs });
return mod.default({ slugs, ...context });
},
}),
createApi({
@@ -62,14 +74,9 @@ export function createRouter(options: RouterOptions = {}): {
render: 'static',
path: '/[...slugs]',
method: 'GET',
staticPaths: source.getPages().map((page) => getPageMarkdownUrl(page).segments),
staticPaths: source.getPages().map((page) => slugsToMarkdownPath(page.slugs).segments),
async handler(_req, { params }) {
const slugs = [...(params.slugs as string[])];
if (slugs.length === 0) unstable_notFound();
slugs[slugs.length - 1] = slugs[slugs.length - 1]!.replace(/\.md$/, '');
if (slugs.length === 1 && slugs[0] === 'index') slugs.pop();
const slugs = markdownPathToSlugs(params.slugs as string[]);
const page = source.getPage(slugs);
if (!page) unstable_notFound();
@@ -102,7 +109,7 @@ export function createRouter(options: RouterOptions = {}): {
generate({
title: page.data.title,
description: page.data.description,
site: appName,
site: config.site.name,
}),
{
width: 1200,
@@ -1,4 +1,4 @@
import { getPageImage, getPageMarkdownUrl, getSource } from '@/lib/source';
import { getPageImage, getSource, slugsToMarkdownPath } from '@/lib/source';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import {
DocsBody,
@@ -9,12 +9,11 @@ import {
ViewOptionsPopover,
} from 'fumadocs-ui/layouts/docs/page';
import { unstable_notFound } from 'waku/router/server';
import { gitConfig } from '@/lib/shared';
import { AppContext, baseOptions, getGitHubFileUrl } from '@/lib/shared';
import { getMDXComponents } from '@/components/mdx';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/lib/layout.shared';
export default async function Page({ slugs }: { slugs: string[] }) {
export default async function Page({ slugs, config }: { slugs: string[] } & AppContext) {
const source = await getSource();
const page = source.getPage(slugs);
if (!page) unstable_notFound();
@@ -27,9 +26,9 @@ export default async function Page({ slugs }: { slugs: string[] }) {
}),
);
const markdownUrl = getPageMarkdownUrl(page).url;
const markdownUrl = slugsToMarkdownPath(page.slugs).url;
return (
<DocsLayout {...baseOptions()} tree={source.getPageTree()}>
<DocsLayout {...baseOptions(config)} tree={source.getPageTree()}>
<DocsPage toc={toc}>
<meta property="og:image" content={getPageImage(slugs).url} />
<DocsTitle>{page.data.title}</DocsTitle>
@@ -38,7 +37,7 @@ export default async function Page({ slugs }: { slugs: string[] }) {
<MarkdownCopyButton markdownUrl={markdownUrl} />
<ViewOptionsPopover
markdownUrl={markdownUrl}
githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/content/docs/${page.path}`}
githubUrl={page.absolutePath ? getGitHubFileUrl(config, page.absolutePath) : undefined}
/>
</div>
<DocsBody>{body}</DocsBody>
+3 -3
View File
@@ -1,7 +1,7 @@
import type { ReactNode } from 'react';
import { baseOptions } from '@/lib/layout.shared';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { AppContext, baseOptions } from '@/lib/shared';
export default function Layout({ children }: { children: ReactNode }) {
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
export default function Layout({ config, children }: AppContext & { children: ReactNode }) {
return <HomeLayout {...baseOptions(config)}>{children}</HomeLayout>;
}
+21
View File
@@ -0,0 +1,21 @@
import { existsSync } from 'fs';
import { join, dirname } from 'path';
/**
* Returns the absolute path to the root directory of the current git repository.
*/
export function getGitRootDir(startDir = process.cwd()): string | null {
let dir = startDir;
while (true) {
if (existsSync(join(dir, '.git'))) {
return dir;
}
const parent = dirname(dir);
if (parent === dir) {
break; // reached filesystem root
}
dir = parent;
}
return null;
}
@@ -1,11 +0,0 @@
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
import { appName, gitConfig } from './shared';
export function baseOptions(): BaseLayoutProps {
return {
nav: {
title: appName,
},
githubUrl: `https://github.com/${gitConfig.user}/${gitConfig.repo}`,
};
}
+56 -7
View File
@@ -1,8 +1,57 @@
export const appName = 'Waku';
import type { Config } from '@/config';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
import { getGitRootDir } from './fs';
import path from 'node:path';
// fill this with your actual GitHub info, for example:
export const gitConfig = {
user: 'fuma-nama',
repo: 'fumadocs',
branch: 'main',
};
export interface AppContext {
config: InternalConfig;
}
export interface InternalConfig {
site: {
name: string;
git?: {
user: string;
repo: string;
branch: string;
rootDir: string;
};
};
}
export function parseConfig(config: Config): InternalConfig {
return {
...config,
site: {
...config.site,
name: config.site?.name ?? 'Fumapress',
git: config.site?.git
? {
...config.site.git,
rootDir: config.site.git.rootDir ?? getGitRootDir() ?? process.cwd(),
}
: undefined,
},
};
}
export function getGitHubFileUrl(config: InternalConfig, absolutePath: string): string | undefined {
const { git } = config.site;
if (!git) return;
const p = path.relative(git.rootDir, absolutePath).replaceAll(path.sep, '/');
if (p.startsWith('../')) return;
return `https://github.com/${git.user}/${git.repo}/blob/${git.branch}/${p}`;
}
export function baseOptions(config: InternalConfig): BaseLayoutProps {
const { name, git } = config.site;
return {
nav: {
title: name,
},
githubUrl: git ? `https://github.com/${git.user}/${git.repo}` : undefined,
};
}
+12 -2
View File
@@ -33,8 +33,8 @@ export function getPageImage(slugs: string[]) {
};
}
export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
const segments = [...page.slugs];
export function slugsToMarkdownPath(slugs: string[]) {
const segments = [...slugs];
if (segments.length === 0) {
segments.push('index.md');
} else {
@@ -47,6 +47,16 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
};
}
export function markdownPathToSlugs(segs: string[]) {
const slugs = [...segs];
if (slugs.length === 0) return [];
slugs[slugs.length - 1] = slugs[slugs.length - 1]!.replace(/\.md$/, '');
if (slugs.length === 1 && slugs[0] === 'index') slugs.pop();
return slugs;
}
export async function getLLMText(page: (typeof source)['$inferPage']) {
return `# ${page.data.title} (${page.url})