mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Press: support route configs
This commit is contained in:
@@ -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:*",
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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}`,
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user