CLI: fix Takumi plugin

This commit is contained in:
Fuma Nama
2026-03-02 16:22:41 +08:00
parent 51239a8309
commit 31407986d0
8 changed files with 37 additions and 53 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'create-fumadocs-app': patch
---
fix Takumi plugin
+5
View File
@@ -173,6 +173,11 @@ const showcases: ShowcaseObject[] = [
name: 'Freestyle',
url: 'https://freestyle.sh',
},
{
image: '/showcases/zap-studio.png',
name: 'Zap Studio',
url: 'https://zapstudio.dev',
},
{
image: VisionUI,
name: 'Vision UI',
-12
View File
@@ -39,18 +39,6 @@ const config: NextConfig = {
},
];
},
async rewrites() {
return [
{
source: '/docs/:path*.mdx',
destination: '/llms.mdx/:path*',
},
{
source: '/docs.mdx',
destination: '/llms.mdx',
},
];
},
};
const withMDX = createMDX();
+6
View File
@@ -2,8 +2,14 @@ import { NextRequest, NextResponse } from 'next/server';
import { isMarkdownPreferred, rewritePath } from 'fumadocs-core/negotiation';
const { rewrite: rewriteLLM } = rewritePath('/docs/*path', '/llms.mdx/*path');
const { rewrite: rewriteMdx } = rewritePath('/docs{/*path}.mdx', '/llms.mdx{/*path}');
export default function proxy(request: NextRequest) {
const result = rewriteMdx(request.nextUrl.pathname);
if (result) {
return NextResponse.rewrite(new URL(result, request.nextUrl));
}
if (isMarkdownPreferred(request)) {
const result = rewriteLLM(request.nextUrl.pathname);
Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

+9 -10
View File
@@ -138,24 +138,23 @@ async function main(): Promise<void> {
},
ogImage: async ({ results }: { results: { template?: Template } }) => {
if (config.ogImage !== undefined) return config.ogImage;
if (isCI) return 'takumi';
if (!results.template?.startsWith('+next')) return 'takumi';
if (isCI) return 'next/og';
return select({
message: 'Configure Open Graph Image generation?',
options: [
{
value: 'next-og',
label: 'next/og',
hint: 'Next.js built-in solution',
},
{
value: 'takumi',
label: 'Takumi',
hint: 'Output WebP format, framework-agnostic',
},
{
value: 'next-og',
label: 'next/og',
hint: 'Next.js built-in solution',
disabled: !results.template?.startsWith('+next'),
},
],
initialValue: 'takumi' as const,
});
},
installDeps: async () => {
@@ -203,8 +202,8 @@ async function main(): Promise<void> {
}
if (options.ogImage) {
const { ogImage } = await import('./plugins/og-image');
plugins.push(ogImage(options.ogImage));
const { nextUseTakumi } = await import('./plugins/next-use-takumi');
plugins.push(nextUseTakumi());
}
await create({
@@ -1,13 +1,13 @@
import { TemplatePlugin, TemplatePluginContext } from '@/index';
import { depVersions } from '@/constants';
import { pick, replace, replaceAll } from '@/utils';
import { pick } from '@/utils';
import { join } from 'node:path';
import { readFile, writeFile } from 'node:fs/promises';
export function ogImage(type: 'next-og' | 'takumi'): TemplatePlugin {
export function nextUseTakumi(): TemplatePlugin {
return {
packageJson(packageJson) {
if (this.template.value.startsWith('+next') && type === 'takumi') {
if (this.template.value.startsWith('+next')) {
return {
...packageJson,
dependencies: {
@@ -19,7 +19,7 @@ export function ogImage(type: 'next-og' | 'takumi'): TemplatePlugin {
return packageJson;
},
async afterWrite() {
if (this.template.value.startsWith('+next') && type === 'takumi') {
if (this.template.value.startsWith('+next')) {
await replaceImports(this);
await replaceImagePath(this);
await nextConfigExternal(this);
@@ -31,10 +31,10 @@ export function ogImage(type: 'next-og' | 'takumi'): TemplatePlugin {
async function replaceImports(context: TemplatePluginContext) {
const path = join(context.appDir, 'app/og/docs/[...slug]/route.tsx');
const content = await readFile(path, 'utf-8');
let replaced = replaceAll(content, 'next/og', '@takumi-rs/image-response');
replaced = replaceAll(replaced, 'fumadocs-ui/og', 'fumadocs-ui/og/takumi');
replaced = replace(replaced, 'height: 630,', "height: 630,\n format: 'webp',");
const replaced = content
.replaceAll('next/og', '@takumi-rs/image-response')
.replaceAll('fumadocs-ui/og', 'fumadocs-ui/og/takumi')
.replace('height: 630,', "height: 630,\n format: 'webp',");
await writeFile(path, replaced);
}
@@ -42,22 +42,19 @@ async function replaceImports(context: TemplatePluginContext) {
async function replaceImagePath(context: TemplatePluginContext) {
const path = join(context.appDir, 'lib/source.ts');
const content = await readFile(path, 'utf-8');
const replaced = replaceAll(content, 'image.png', 'image.webp');
const replaced = content.replaceAll('image.png', 'image.webp');
await writeFile(path, replaced);
}
async function nextConfigExternal(context: TemplatePluginContext) {
const path = join(context.appDir, 'next.config.mjs');
const path = join(context.dest, 'next.config.mjs');
const content = await readFile(path, 'utf-8');
const replaced = replace(
content,
const replaced = content.replace(
'const config = {',
`const config = {
serverExternalPackages: ['@takumi-rs/image-response'],
`,
serverExternalPackages: ['@takumi-rs/image-response'],`,
);
await writeFile(path, replaced);
-16
View File
@@ -108,19 +108,3 @@ export function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pi
return result as Pick<T, K>;
}
export function replace(content: string, target: string | RegExp, replacement: string): string {
const result = content.replace(target, replacement);
if (result === content) {
throw new Error(`Failed to replace "${target.toString()}"`);
}
return result;
}
export function replaceAll(content: string, target: string | RegExp, replacement: string): string {
const result = content.replaceAll(target, replacement);
if (result === content) {
throw new Error(`Failed to replace all "${target.toString()}"`);
}
return result;
}