mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-24 20:09:02 +00:00
refactor: improve routing utils in templates
This commit is contained in:
@@ -4,7 +4,7 @@ import * as Twoslash from 'fumadocs-twoslash/ui';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { TypeTable } from 'fumadocs-ui/components/type-table';
|
||||
import * as Preview from '@/components/preview';
|
||||
import { createMetadata, getPageImage } from '@/lib/metadata';
|
||||
import { createMetadata, getPageImageUrl } from '@/lib/metadata';
|
||||
import { source } from '@/lib/source';
|
||||
import { Wrapper } from '@/components/preview/wrapper';
|
||||
import { Mermaid } from '@/components/mdx/mermaid';
|
||||
@@ -176,7 +176,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
const description = page.data.description ?? 'The library for building documentation sites';
|
||||
|
||||
const image = {
|
||||
url: getPageImage(page).url,
|
||||
url: getPageImageUrl(page).url,
|
||||
width: 1200,
|
||||
height: 630,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { source } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { generate as MetadataImage, getImageResponseOptions } from './generate';
|
||||
import { ImageResponse } from 'takumi-js/response';
|
||||
import { getPageImage } from '@/lib/metadata';
|
||||
import { getPageImageUrl } from '@/lib/metadata';
|
||||
|
||||
export const revalidate = false;
|
||||
|
||||
@@ -21,6 +21,6 @@ export function generateStaticParams(): {
|
||||
slug: string[];
|
||||
}[] {
|
||||
return source.getPages().map((page) => ({
|
||||
slug: getPageImage(page).segments,
|
||||
slug: getPageImageUrl(page).segments,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -280,19 +280,12 @@ export default {
|
||||
} satisfies Config;
|
||||
```
|
||||
|
||||
```ts tab="Tanstack Start" title="lib/source.ts"
|
||||
```ts tab="Tanstack Start" title="lib/shared.ts"
|
||||
// create the following utils:
|
||||
|
||||
export function markdownPathToSlugs(segs: string[]) {
|
||||
if (segs.length === 0) return [];
|
||||
export const docsRoute = '/docs';
|
||||
|
||||
const out = [...segs];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
export function slugsToMarkdownPath(slugs: string[]) {
|
||||
export function encodeMarkdownUrl(slugs: string[], locale?: string) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
@@ -300,23 +293,31 @@ export function slugsToMarkdownPath(slugs: string[]) {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/docs/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
/** @returns page slugs */
|
||||
export function decodeMarkdownUrl(segments: string[]) {
|
||||
if (segments.length === 0) return [];
|
||||
|
||||
const out = [...segments];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
```
|
||||
|
||||
```ts tab="Tanstack Start" title="src/routes/docs/{$}[.]md.ts"
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { source, markdownPathToSlugs } from '@/lib/source';
|
||||
import { source } from '@/lib/source';
|
||||
import { decodeMarkdownUrl } from '@/lib/shared';
|
||||
import { getLLMText } from '@/lib/get-llm-text';
|
||||
|
||||
export const Route = createFileRoute('/docs/{$}.md')({
|
||||
server: {
|
||||
handlers: {
|
||||
GET: async ({ params }) => {
|
||||
const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
|
||||
const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
|
||||
const page = source.getPage(slugs);
|
||||
if (!page) throw notFound();
|
||||
|
||||
@@ -364,12 +365,12 @@ export async function getConfig() {
|
||||
```ts tab="Waku" title="lib/source.ts"
|
||||
// create a util for Markdown URLs (append /content.md):
|
||||
|
||||
export function getPageMarkdownUrl(page: { slugs: string[] }) {
|
||||
export function getPageMarkdownUrl(page: { slugs: string[]; locale?: string }) {
|
||||
const segments = [...page.slugs, 'content.md'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/llms.mdx/docs/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, 'llms.mdx', 'docs', ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -13,19 +13,19 @@ Add the following under your loader, and define image metadata for pages:
|
||||
|
||||
```ts tab="lib/source.ts"
|
||||
// [!code ++:8]
|
||||
export function getPageImage(page: (typeof source)['$inferPage']) {
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, 'og', 'docs', ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```tsx tab="app/docs/[[...slug]]/page.tsx"
|
||||
import { notFound } from 'next/navigation';
|
||||
import { source, getPageImage } from '@/lib/source';
|
||||
import { source, getPageImageUrl } from '@/lib/source';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
|
||||
@@ -38,7 +38,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
// [!code ++]
|
||||
images: getPageImage(page).url,
|
||||
images: getPageImageUrl(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -30,19 +30,19 @@ Add the following under your loader, and define image metadata for pages:
|
||||
|
||||
```ts tab="Next.js" title="lib/source.ts"
|
||||
// [!code ++:8]
|
||||
export function getPageImage(page: (typeof source)['$inferPage']) {
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, 'og', 'docs', ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```tsx tab="Next.js" title="app/docs/[[...slug]]/page.tsx"
|
||||
import { notFound } from 'next/navigation';
|
||||
import { source, getPageImage } from '@/lib/source';
|
||||
import { source, getPageImageUrl } from '@/lib/source';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
|
||||
@@ -55,57 +55,60 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
// [!code ++]
|
||||
images: getPageImage(page).url,
|
||||
images: getPageImageUrl(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
```tsx tab="React Router" title="app/lib/og.ts"
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
```tsx tab="React Router" title="app/lib/shared.ts"
|
||||
export const docsImageRoute = '/og/docs';
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
};
|
||||
// [!code ++:5]
|
||||
export function getPageImagePath(slugs: string[], locale?: string) {
|
||||
return (
|
||||
'/' + [locale, ...docsImageRoute.split('/'), ...slugs, 'image.webp'].filter(Boolean).join('/')
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```tsx tab="React Router" title="app/routes/docs.tsx"
|
||||
// [!code ++]
|
||||
import { getPageImage } from '@/lib/og';
|
||||
import { getPageImagePath } from '@/lib/shared';
|
||||
|
||||
// Somewhere in your loader
|
||||
return {
|
||||
// [!code ++]
|
||||
imagePath: getPageImagePath(page.slugs, page.locale),
|
||||
};
|
||||
|
||||
// Somewhere in your component or client loader
|
||||
return (
|
||||
<>
|
||||
{/* [!code ++] */}
|
||||
<meta property="og:image" content={getPageImage(page.slugs).url} />
|
||||
<meta property="og:image" content={imagePath} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
```tsx tab="Vite Based Framework" title="app/lib/source.ts"
|
||||
// [!code ++:8]
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
// [!code ++:5]
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [page.locale, 'og', 'docs', ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
```
|
||||
|
||||
```tsx tab="Vite Based Framework" title="app/routes/docs.tsx"
|
||||
// [!code ++]
|
||||
import { getPageImage } from '@/lib/source';
|
||||
import { getPageImageUrl } from '@/lib/source';
|
||||
|
||||
// Somewhere in your component or client loader
|
||||
return (
|
||||
<>
|
||||
{/* [!code ++] */}
|
||||
<meta property="og:image" content={getPageImage(page.slugs).url} />
|
||||
<meta property="og:image" content={getPageImageUrl(page)} />
|
||||
</>
|
||||
);
|
||||
```
|
||||
@@ -115,7 +118,7 @@ return (
|
||||
Finally, create a route handler to generate images at build time:
|
||||
|
||||
```tsx tab="Next.js" title="app/og/docs/[...slug]/route.tsx"
|
||||
import { getPageImage, source } from '@/lib/source';
|
||||
import { getPageImageUrl, source } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ImageResponse } from 'takumi-js/response';
|
||||
import { generate as DefaultImage } from 'fumadocs-ui/og/takumi';
|
||||
@@ -140,7 +143,7 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
|
||||
export function generateStaticParams() {
|
||||
return source.getPages().map((page) => ({
|
||||
lang: page.locale,
|
||||
slug: getPageImage(page).segments,
|
||||
slug: getPageImageUrl(page).segments,
|
||||
}));
|
||||
}
|
||||
```
|
||||
@@ -217,6 +220,7 @@ export default [
|
||||
import type { Config } from '@react-router/dev/config';
|
||||
import { glob } from 'node:fs/promises';
|
||||
import { createGetUrl, getSlugs } from 'fumadocs-core/source';
|
||||
import { getPageImagePath } from '@/lib/shared';
|
||||
|
||||
const getUrl = createGetUrl('/docs');
|
||||
|
||||
@@ -233,7 +237,7 @@ export default {
|
||||
const slugs = getSlugs(entry);
|
||||
|
||||
paths.push(getUrl(slugs));
|
||||
paths.push(`/og/docs/${[...slugs, 'image.webp'].join('/')}`);
|
||||
paths.push(getPageImagePath(slugs));
|
||||
}
|
||||
|
||||
return paths;
|
||||
|
||||
@@ -34,12 +34,12 @@ export function createMetadata(override: Metadata): Metadata {
|
||||
};
|
||||
}
|
||||
|
||||
export function getPageImage(page: Page) {
|
||||
export function getPageImageUrl(page: Page) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, 'og', ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,10 @@ export function getStructuredData(entry: CollectionEntry<'docs'>): StructuredDat
|
||||
return structure(entry.body);
|
||||
}
|
||||
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [page.locale, 'og', 'docs', ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
async function createMySource() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { render, type CollectionEntry } from "astro:content";
|
||||
import { Docs } from "@/components/docs";
|
||||
import defaultMdxComponents from "fumadocs-ui/mdx";
|
||||
import "@/styles/global.css";
|
||||
import { getPageImage, source } from "@/lib/source";
|
||||
import { getPageImageUrl, source } from "@/lib/source";
|
||||
import Layout from "@/components/layout.astro";
|
||||
import type { TOCItemType } from "fumadocs-core/toc";
|
||||
|
||||
@@ -21,7 +21,8 @@ export async function getStaticPaths() {
|
||||
const { page } = Astro.props;
|
||||
const { Content, headings } = await render(page);
|
||||
const slugs = typeof Astro.params.slug === "string" ? Astro.params.slug.split("/") : [];
|
||||
const image = getPageImage(slugs).url;
|
||||
const docsPage = source.getPage(slugs)!;
|
||||
const image = getPageImageUrl(docsPage);
|
||||
|
||||
const toc: TOCItemType[] = headings.map((heading) => ({
|
||||
depth: heading.depth,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import {
|
||||
DocsBody,
|
||||
DocsDescription,
|
||||
@@ -57,7 +57,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
images: getPageImage(page).url,
|
||||
images: getPageImageUrl(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, source } from '@/lib/source';
|
||||
import { getPageImageUrl, source } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { generate as DefaultImage } from 'fumadocs-ui/og';
|
||||
@@ -23,6 +23,6 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
|
||||
export function generateStaticParams() {
|
||||
return source.getPages().map((page) => ({
|
||||
lang: page.locale,
|
||||
slug: getPageImage(page).segments,
|
||||
slug: getPageImageUrl(page).segments,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ export const source = loader({
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function getPageImage(page: (typeof source)['$inferPage']) {
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getPageMarkdownUrl, getSource } from '@/lib/source';
|
||||
import { getPageImageUrl, getPageMarkdownUrl, getSource } from '@/lib/source';
|
||||
import {
|
||||
DocsBody,
|
||||
DocsDescription,
|
||||
@@ -80,7 +80,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
images: getPageImage(page).url,
|
||||
images: getPageImageUrl(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getSource } from '@/lib/source';
|
||||
import { getPageImageUrl, getSource } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { generate as DefaultImage } from 'fumadocs-ui/og';
|
||||
@@ -25,6 +25,6 @@ export async function generateStaticParams() {
|
||||
const docs = await getSource();
|
||||
return docs.getPages().map((page) => ({
|
||||
lang: page.locale,
|
||||
slug: getPageImage(page).segments,
|
||||
slug: getPageImageUrl(page).segments,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ export async function getSource() {
|
||||
return docsLoader.get();
|
||||
}
|
||||
|
||||
export function getPageImage(page: (typeof docsLoader)['$inferPage']) {
|
||||
export function getPageImageUrl(page: (typeof docsLoader)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export function getPageMarkdownUrl(page: (typeof docsLoader)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import {
|
||||
DocsBody,
|
||||
DocsDescription,
|
||||
@@ -57,7 +57,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
images: getPageImage(page).url,
|
||||
images: getPageImageUrl(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, source } from '@/lib/source';
|
||||
import { getPageImageUrl, source } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { generate as DefaultImage } from 'fumadocs-ui/og';
|
||||
@@ -23,6 +23,6 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
|
||||
export function generateStaticParams() {
|
||||
return source.getPages().map((page) => ({
|
||||
lang: page.locale,
|
||||
slug: getPageImage(page).segments,
|
||||
slug: getPageImageUrl(page).segments,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ export const source = loader({
|
||||
plugins: [],
|
||||
});
|
||||
|
||||
export function getPageImage(page: (typeof source)['$inferPage']) {
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import {
|
||||
DocsBody,
|
||||
DocsDescription,
|
||||
@@ -57,7 +57,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
images: getPageImage(page).url,
|
||||
images: getPageImageUrl(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, source } from '@/lib/source';
|
||||
import { getPageImageUrl, source } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { generate as DefaultImage } from 'fumadocs-ui/og';
|
||||
@@ -23,6 +23,6 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
|
||||
export function generateStaticParams() {
|
||||
return source.getPages().map((page) => ({
|
||||
lang: page.locale,
|
||||
slug: getPageImage(page).segments,
|
||||
slug: getPageImageUrl(page).segments,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ export const source = loader({
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function getPageImage(page: (typeof source)['$inferPage']) {
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export function getPageImagePath(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
|
||||
return `/og/docs/${segments.join('/')}`;
|
||||
}
|
||||
@@ -9,3 +9,9 @@ export const gitConfig = {
|
||||
repo: 'fumadocs',
|
||||
branch: 'main',
|
||||
};
|
||||
|
||||
export function getPageImagePath(slugs: string[], locale?: string) {
|
||||
return (
|
||||
'/' + [locale, ...docsImageRoute.split('/'), ...slugs, 'image.webp'].filter(Boolean).join('/')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@ import {
|
||||
import { getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import browserCollections from 'collections/browser';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { gitConfig } from '@/lib/shared';
|
||||
import { gitConfig, getPageImagePath } from '@/lib/shared';
|
||||
import { useFumadocsLoader } from 'fumadocs-core/source/client';
|
||||
import { getPageImagePath } from '@/lib/og';
|
||||
import { useMDXComponents } from '@/components/mdx';
|
||||
|
||||
export async function loader({ params }: Route.LoaderArgs) {
|
||||
@@ -25,7 +24,7 @@ export async function loader({ params }: Route.LoaderArgs) {
|
||||
path: page.path,
|
||||
markdownUrl: getPageMarkdownUrl(page).url,
|
||||
pageTree: await source.serializePageTree(source.getPageTree()),
|
||||
imagePath: getPageImagePath(slugs),
|
||||
imagePath: getPageImagePath(page.slugs, page.locale),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Config } from '@react-router/dev/config';
|
||||
import { glob } from 'node:fs/promises';
|
||||
import { createGetUrl, getSlugs } from 'fumadocs-core/source';
|
||||
import { getPageImagePath } from './app/lib/og';
|
||||
import { getPageImagePath } from '@/lib/shared';
|
||||
|
||||
const getUrl = createGetUrl('/docs');
|
||||
|
||||
|
||||
@@ -9,13 +9,10 @@ export const source = loader({
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
@@ -23,7 +20,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { PageProps } from 'waku/router';
|
||||
import { createRelativeLink } from 'fumadocs-ui/mdx';
|
||||
import {
|
||||
@@ -21,7 +21,7 @@ export default function Page({ slugs }: PageProps<'/docs/[...slugs]'>) {
|
||||
const markdownUrl = getPageMarkdownUrl(page).url;
|
||||
return (
|
||||
<DocsPage toc={page.data.toc}>
|
||||
<meta property="og:image" content={getPageImage(slugs).url} />
|
||||
<meta property="og:image" content={getPageImageUrl(page)} />
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription className="mb-0">{page.data.description}</DocsDescription>
|
||||
<div className="flex flex-row gap-2 items-center border-b pt-2 pb-6">
|
||||
|
||||
@@ -8,3 +8,24 @@ export const gitConfig = {
|
||||
repo: 'fumadocs',
|
||||
branch: 'main',
|
||||
};
|
||||
|
||||
export function encodeMarkdownUrl(slugs: string[], locale?: string) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
/** @returns page slugs */
|
||||
export function decodeMarkdownUrl(segments: string[]) {
|
||||
if (segments.length === 0) return [];
|
||||
|
||||
const out = [...segments];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -20,29 +20,6 @@ export async function getSource() {
|
||||
return source.get();
|
||||
}
|
||||
|
||||
export function markdownPathToSlugs(segs: string[]) {
|
||||
if (segs.length === 0) return [];
|
||||
|
||||
const out = [...segs];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
export function slugsToMarkdownPath(slugs: string[]) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsRoute}/${segments.join('/')}`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLLMText(page: (typeof source)['$inferPage']) {
|
||||
return `# ${page.data.title} (${page.url})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { getSource, slugsToMarkdownPath } from '@/lib/source';
|
||||
import { getSource } from '@/lib/source';
|
||||
import {
|
||||
DocsBody,
|
||||
DocsDescription,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ViewOptionsPopover,
|
||||
} from 'fumadocs-ui/layouts/docs/page';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { gitConfig } from '@/lib/shared';
|
||||
import { encodeMarkdownUrl, gitConfig } from '@/lib/shared';
|
||||
import { useFumadocsLoader } from 'fumadocs-core/source/client';
|
||||
import { useMemo } from 'react';
|
||||
import { useMDXComponents } from '@/components/mdx';
|
||||
@@ -40,7 +40,7 @@ const serverLoader = createServerFn({
|
||||
path: page.path,
|
||||
frontmatter: page.data.frontmatter,
|
||||
render: serialize(),
|
||||
markdownUrl: slugsToMarkdownPath(page.slugs).url,
|
||||
markdownUrl: encodeMarkdownUrl(page.slugs, page.locale),
|
||||
pageTree: await source.serializePageTree(source.getPageTree()),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { getLLMText, getSource, markdownPathToSlugs } from '@/lib/source';
|
||||
import { getLLMText, getSource } from '@/lib/source';
|
||||
import { decodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
export const Route = createFileRoute('/docs/{$}.md')({
|
||||
server: {
|
||||
handlers: {
|
||||
GET: async ({ params }) => {
|
||||
const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
|
||||
const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
|
||||
const source = await getSource();
|
||||
const page = source.getPage(slugs);
|
||||
if (!page) throw notFound();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createMiddleware, createStart } from '@tanstack/react-start';
|
||||
import { isMarkdownPreferred } from 'fumadocs-core/negotiation';
|
||||
import { redirect } from '@tanstack/react-router';
|
||||
import { docsRoute } from '@/lib/shared';
|
||||
import { slugsToMarkdownPath } from './lib/source';
|
||||
import { docsRoute, encodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
const llmMiddleware = createMiddleware().server(({ next, request }) => {
|
||||
const url = new URL(request.url);
|
||||
@@ -16,7 +15,7 @@ const llmMiddleware = createMiddleware().server(({ next, request }) => {
|
||||
.slice(docsRoute.length)
|
||||
.split('/')
|
||||
.filter((v) => v.length > 0);
|
||||
url.pathname = slugsToMarkdownPath(slugs).url;
|
||||
url.pathname = encodeMarkdownUrl(slugs);
|
||||
|
||||
throw redirect(url);
|
||||
}
|
||||
|
||||
@@ -8,3 +8,24 @@ export const gitConfig = {
|
||||
repo: 'fumadocs',
|
||||
branch: 'main',
|
||||
};
|
||||
|
||||
export function encodeMarkdownUrl(slugs: string[], locale?: string) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
/** @returns page slugs */
|
||||
export function decodeMarkdownUrl(segments: string[]) {
|
||||
if (segments.length === 0) return [];
|
||||
|
||||
const out = [...segments];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -17,29 +17,6 @@ export const source = loader(
|
||||
},
|
||||
);
|
||||
|
||||
export function markdownPathToSlugs(segs: string[]) {
|
||||
if (segs.length === 0) return [];
|
||||
|
||||
const out = [...segs];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
export function slugsToMarkdownPath(slugs: string[]) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsRoute}/${segments.join('/')}`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLLMText(page: (typeof source)['$inferPage']) {
|
||||
if (page.type === 'openapi') return JSON.stringify(page.data.getSchema(), null, 2);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/notebook';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { slugsToMarkdownPath, source } from '@/lib/source';
|
||||
import { source } from '@/lib/source';
|
||||
import browserCollections from 'collections/browser';
|
||||
import {
|
||||
DocsBody,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
ViewOptionsPopover,
|
||||
} from 'fumadocs-ui/layouts/notebook/page';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { gitConfig } from '@/lib/shared';
|
||||
import { encodeMarkdownUrl, gitConfig } from '@/lib/shared';
|
||||
import { useFumadocsLoader } from 'fumadocs-core/source/client';
|
||||
import { Suspense, type ReactNode } from 'react';
|
||||
import { useMDXComponents } from '@/components/mdx';
|
||||
@@ -53,7 +53,7 @@ const serverLoader = createServerFn({
|
||||
return {
|
||||
type: 'docs',
|
||||
path: page.path,
|
||||
markdownUrl: slugsToMarkdownPath(page.slugs).url,
|
||||
markdownUrl: encodeMarkdownUrl(page.slugs, page.locale),
|
||||
pageTree,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { getLLMText, markdownPathToSlugs, source } from '@/lib/source';
|
||||
import { getLLMText, source } from '@/lib/source';
|
||||
import { decodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
export const Route = createFileRoute('/docs/{$}.md')({
|
||||
server: {
|
||||
handlers: {
|
||||
GET: async ({ params }) => {
|
||||
const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
|
||||
const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
|
||||
const page = source.getPage(slugs);
|
||||
if (!page) throw notFound();
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createMiddleware, createStart } from '@tanstack/react-start';
|
||||
import { isMarkdownPreferred } from 'fumadocs-core/negotiation';
|
||||
import { redirect } from '@tanstack/react-router';
|
||||
import { docsRoute } from '@/lib/shared';
|
||||
import { slugsToMarkdownPath } from './lib/source';
|
||||
import { docsRoute, encodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
const llmMiddleware = createMiddleware().server(({ next, request }) => {
|
||||
const url = new URL(request.url);
|
||||
@@ -16,7 +15,7 @@ const llmMiddleware = createMiddleware().server(({ next, request }) => {
|
||||
.slice(docsRoute.length)
|
||||
.split('/')
|
||||
.filter((v) => v.length > 0);
|
||||
url.pathname = slugsToMarkdownPath(slugs).url;
|
||||
url.pathname = encodeMarkdownUrl(slugs);
|
||||
|
||||
throw redirect(url);
|
||||
}
|
||||
|
||||
@@ -8,3 +8,24 @@ export const gitConfig = {
|
||||
repo: 'fumadocs',
|
||||
branch: 'main',
|
||||
};
|
||||
|
||||
export function encodeMarkdownUrl(slugs: string[], locale?: string) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
/** @returns page slugs */
|
||||
export function decodeMarkdownUrl(segments: string[]) {
|
||||
if (segments.length === 0) return [];
|
||||
|
||||
const out = [...segments];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -9,29 +9,6 @@ export const source = loader({
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function markdownPathToSlugs(segs: string[]) {
|
||||
if (segs.length === 0) return [];
|
||||
|
||||
const out = [...segs];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
export function slugsToMarkdownPath(slugs: string[]) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsRoute}/${segments.join('/')}`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLLMText(page: (typeof source)['$inferPage']) {
|
||||
const processed = await page.data.getText('processed');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, Link, notFound } from '@tanstack/react-router';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { slugsToMarkdownPath, source } from '@/lib/source';
|
||||
import { source } from '@/lib/source';
|
||||
import browserCollections from 'collections/browser';
|
||||
import {
|
||||
DocsBody,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
ViewOptionsPopover,
|
||||
} from 'fumadocs-ui/layouts/docs/page';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { gitConfig } from '@/lib/shared';
|
||||
import { encodeMarkdownUrl, gitConfig } from '@/lib/shared';
|
||||
import { staticFunctionMiddleware } from '@tanstack/start-static-server-functions';
|
||||
import { useFumadocsLoader } from 'fumadocs-core/source/client';
|
||||
import { Suspense } from 'react';
|
||||
@@ -39,7 +39,7 @@ const loader = createServerFn({
|
||||
|
||||
return {
|
||||
path: page.path,
|
||||
markdownUrl: slugsToMarkdownPath(page.slugs).url,
|
||||
markdownUrl: encodeMarkdownUrl(page.slugs, page.locale),
|
||||
pageTree: await source.serializePageTree(source.getPageTree()),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { getLLMText, markdownPathToSlugs, source } from '@/lib/source';
|
||||
import { getLLMText, source } from '@/lib/source';
|
||||
import { decodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
export const Route = createFileRoute('/docs/{$}.md')({
|
||||
server: {
|
||||
handlers: {
|
||||
GET: async ({ params }) => {
|
||||
const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
|
||||
const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
|
||||
const page = source.getPage(slugs);
|
||||
if (!page) throw notFound();
|
||||
|
||||
|
||||
@@ -8,3 +8,24 @@ export const gitConfig = {
|
||||
repo: 'fumadocs',
|
||||
branch: 'main',
|
||||
};
|
||||
|
||||
export function encodeMarkdownUrl(slugs: string[], locale?: string) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
/** @returns page slugs */
|
||||
export function decodeMarkdownUrl(segments: string[]) {
|
||||
if (segments.length === 0) return [];
|
||||
|
||||
const out = [...segments];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -9,29 +9,6 @@ export const source = loader({
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function markdownPathToSlugs(segs: string[]) {
|
||||
if (segs.length === 0) return [];
|
||||
|
||||
const out = [...segs];
|
||||
out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
|
||||
if (out.length === 1 && out[0] === 'index') out.pop();
|
||||
return out;
|
||||
}
|
||||
|
||||
export function slugsToMarkdownPath(slugs: string[]) {
|
||||
const segments = [...slugs];
|
||||
if (segments.length === 0) {
|
||||
segments.push('index.md');
|
||||
} else {
|
||||
segments[segments.length - 1] += '.md';
|
||||
}
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsRoute}/${segments.join('/')}`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getLLMText(page: (typeof source)['$inferPage']) {
|
||||
const processed = await page.data.getText('processed');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { createServerFn } from '@tanstack/react-start';
|
||||
import { slugsToMarkdownPath, source } from '@/lib/source';
|
||||
import { source } from '@/lib/source';
|
||||
import browserCollections from 'collections/browser';
|
||||
import {
|
||||
DocsBody,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
ViewOptionsPopover,
|
||||
} from 'fumadocs-ui/layouts/docs/page';
|
||||
import { baseOptions } from '@/lib/layout.shared';
|
||||
import { gitConfig } from '@/lib/shared';
|
||||
import { encodeMarkdownUrl, gitConfig } from '@/lib/shared';
|
||||
import { useFumadocsLoader } from 'fumadocs-core/source/client';
|
||||
import { Suspense } from 'react';
|
||||
import { useMDXComponents } from '@/components/mdx';
|
||||
@@ -37,7 +37,7 @@ const serverLoader = createServerFn({
|
||||
|
||||
return {
|
||||
path: page.path,
|
||||
markdownUrl: slugsToMarkdownPath(page.slugs).url,
|
||||
markdownUrl: encodeMarkdownUrl(page.slugs, page.locale),
|
||||
pageTree: await source.serializePageTree(source.getPageTree()),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { createFileRoute, notFound } from '@tanstack/react-router';
|
||||
import { getLLMText, markdownPathToSlugs, source } from '@/lib/source';
|
||||
import { getLLMText, source } from '@/lib/source';
|
||||
import { decodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
export const Route = createFileRoute('/docs/{$}.md')({
|
||||
server: {
|
||||
handlers: {
|
||||
GET: async ({ params }) => {
|
||||
const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
|
||||
const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
|
||||
const page = source.getPage(slugs);
|
||||
if (!page) throw notFound();
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createMiddleware, createCsrfMiddleware, createStart } from '@tanstack/react-start';
|
||||
import { isMarkdownPreferred } from 'fumadocs-core/negotiation';
|
||||
import { redirect } from '@tanstack/react-router';
|
||||
import { docsRoute } from '@/lib/shared';
|
||||
import { slugsToMarkdownPath } from './lib/source';
|
||||
import { docsRoute, encodeMarkdownUrl } from '@/lib/shared';
|
||||
|
||||
const csrfMiddleware = createCsrfMiddleware({
|
||||
filter: (ctx) => ctx.handlerType === 'serverFn',
|
||||
@@ -20,7 +19,7 @@ const llmMiddleware = createMiddleware().server(({ next, request }) => {
|
||||
.slice(docsRoute.length)
|
||||
.split('/')
|
||||
.filter((v) => v.length > 0);
|
||||
url.pathname = slugsToMarkdownPath(slugs).url;
|
||||
url.pathname = encodeMarkdownUrl(slugs);
|
||||
|
||||
throw redirect(url);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,10 @@ export const source = loader({
|
||||
plugins: [lucideIconsPlugin()],
|
||||
});
|
||||
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
@@ -23,7 +20,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsContentRoute}/${segments.join('/')}`,
|
||||
url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { PageProps } from 'waku/router';
|
||||
import { createRelativeLink } from 'fumadocs-ui/mdx';
|
||||
import {
|
||||
@@ -21,7 +21,7 @@ export default function Page({ slugs }: PageProps<'/docs/[...slugs]'>) {
|
||||
const markdownUrl = getPageMarkdownUrl(page).url;
|
||||
return (
|
||||
<DocsPage toc={page.data.toc}>
|
||||
<meta property="og:image" content={getPageImage(slugs).url} />
|
||||
<meta property="og:image" content={getPageImageUrl(page)} />
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription className="mb-0">{page.data.description}</DocsDescription>
|
||||
<div className="flex flex-row gap-2 items-center border-b pt-2 pb-6">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { SortedResult } from '@/search';
|
||||
import type { SearchClient } from '../client';
|
||||
import { BASE_PATH } from '@/utils/url';
|
||||
import { BASE_PATH, join } from '@/utils/url';
|
||||
|
||||
export interface FetchOptions {
|
||||
/**
|
||||
@@ -26,7 +26,7 @@ export interface FetchOptions {
|
||||
const globalCache = new Map();
|
||||
|
||||
export function fetchClient({
|
||||
api = `${BASE_PATH}api/search`,
|
||||
api = join(BASE_PATH, '/api/search'),
|
||||
locale,
|
||||
tag,
|
||||
cache = globalCache,
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { SearchClient } from '../client';
|
||||
import type { ExportedData } from '../flexsearch';
|
||||
import type { Document } from 'flexsearch';
|
||||
import { createDocument, search, type Doc } from '../flexsearch/utils';
|
||||
import { BASE_PATH } from '@/utils/url';
|
||||
import { BASE_PATH, join } from '@/utils/url';
|
||||
|
||||
export interface FlexsearchStaticOptions {
|
||||
/**
|
||||
@@ -22,7 +22,7 @@ function initDocument(data: Record<string, string>) {
|
||||
const cacheMap = new Map<string, Promise<Map<string, Document<Doc>>>>();
|
||||
|
||||
export function flexsearchStaticClient(options: FlexsearchStaticOptions = {}): SearchClient {
|
||||
const { from = `${BASE_PATH}api/search`, locale = '', tag } = options;
|
||||
const { from = join(BASE_PATH, '/api/search'), locale = '', tag } = options;
|
||||
|
||||
let dbs = cacheMap.get(from);
|
||||
if (!dbs && typeof window !== 'undefined') {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { searchAdvanced } from '@/search/orama/search/advanced';
|
||||
import type { advancedSchema, simpleSchema } from '@/search/orama/create-db';
|
||||
import type { ExportedData } from '@/search/server';
|
||||
import type { SearchClient } from '../client';
|
||||
import { BASE_PATH } from '@/utils/url';
|
||||
import { BASE_PATH, join } from '@/utils/url';
|
||||
|
||||
export interface StaticOptions {
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ async function loadDB(
|
||||
return dbs;
|
||||
}
|
||||
|
||||
function getDBCached({ from = `${BASE_PATH}api/search`, initOrama }: StaticOptions) {
|
||||
function getDBCached({ from = join(BASE_PATH, '/api/search'), initOrama }: StaticOptions) {
|
||||
const cacheKey = from;
|
||||
const cached = cache.get(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
/**
|
||||
* The base path (Vite), always ends with `/`.
|
||||
* The base path (Vite)
|
||||
*/
|
||||
export let BASE_PATH: string =
|
||||
export const BASE_PATH: string =
|
||||
typeof import.meta.env !== 'undefined' && typeof import.meta.env.BASE_URL === 'string'
|
||||
? import.meta.env.BASE_URL
|
||||
: '/';
|
||||
if (!BASE_PATH.endsWith('/')) BASE_PATH += '/';
|
||||
|
||||
export function join(...paths: string[]) {
|
||||
let out = '';
|
||||
for (let p of paths) {
|
||||
if (out.length > 0) {
|
||||
if (p.startsWith('/')) p = p.slice(1);
|
||||
if (!out.endsWith('/')) out += '/';
|
||||
}
|
||||
out += p;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* normalize URL into the Fumadocs standard form (`/slug-1/slug-2`).
|
||||
|
||||
@@ -13,13 +13,10 @@ export function getStructuredData(entry: CollectionEntry<'docs'>): StructuredDat
|
||||
return structure(entry.body);
|
||||
}
|
||||
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
export function getPageImageUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/docs/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [page.locale, 'og', 'docs', ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
async function createMySource() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { render, type CollectionEntry } from "astro:content";
|
||||
import { Docs } from "@/components/docs";
|
||||
import defaultMdxComponents from "fumadocs-ui/mdx";
|
||||
import "@/styles/global.css";
|
||||
import { getPageImage, source } from "@/lib/source";
|
||||
import { getPageImageUrl, source } from "@/lib/source";
|
||||
import Layout from "@/components/layout.astro";
|
||||
import type { TOCItemType } from "fumadocs-core/toc";
|
||||
|
||||
@@ -21,7 +21,8 @@ export async function getStaticPaths() {
|
||||
const { page } = Astro.props;
|
||||
const { Content, headings } = await render(page);
|
||||
const slugs = typeof Astro.params.slug === "string" ? Astro.params.slug.split("/") : [];
|
||||
const image = getPageImage(slugs).url;
|
||||
const docsPage = source.getPage(slugs)!;
|
||||
const image = getPageImageUrl(docsPage);
|
||||
|
||||
const toc: TOCItemType[] = headings.map((heading) => ({
|
||||
depth: heading.depth,
|
||||
|
||||
@@ -17,11 +17,8 @@ export const getSource = revalidable({
|
||||
export type Source = Awaited<ReturnType<typeof getSource>>;
|
||||
export type SourcePage = Source['$inferPage'];
|
||||
|
||||
export function getPageImage(slugs: string[]) {
|
||||
const segments = [...slugs, 'image.webp'];
|
||||
export function getPageImageUrl(page: SourcePage) {
|
||||
const segments = [...page.slugs, 'image.webp'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `/og/${segments.join('/')}`,
|
||||
};
|
||||
return '/' + [page.locale, 'og', ...segments].filter(Boolean).join('/');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PageProps } from 'waku/router';
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import { getPageImage, getSource, type SourcePage } from '@/lib/source';
|
||||
import { getPageImageUrl, getSource, type SourcePage } from '@/lib/source';
|
||||
import { layoutConfig } from '@/layouts/config';
|
||||
import { getConfigRuntime } from '@/config/load-runtime';
|
||||
import { Card, Cards } from 'fumadocs-ui/components/card';
|
||||
@@ -211,7 +211,7 @@ async function MdContent({
|
||||
return renderContainer(
|
||||
<DocsPage toc={toc}>
|
||||
<title>{page.data.title}</title>
|
||||
<meta property="og:image" content={getPageImage(slugs).url} />
|
||||
<meta property="og:image" content={getPageImageUrl(page)} />
|
||||
<meta property="og:description" content={page.data.description} />
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription>{page.data.description}</DocsDescription>
|
||||
|
||||
Reference in New Issue
Block a user