Chore: migrate other packages

This commit is contained in:
Fuma Nama
2026-03-15 00:18:30 +08:00
parent f45d703bb1
commit 1fc5549716
14 changed files with 114 additions and 57 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'fumadocs-typescript': minor
'fumadocs-openapi': minor
---
Bump deps
@@ -93,13 +93,19 @@ export function createRehypeCode<
}>),
) {
async function initTransformer(_options?: Options) {
const { highlighter, options } =
typeof highlighterFactory === 'function'
? await highlighterFactory(_options)
: {
highlighter: await highlighterFactory.getOrInit(),
options: (_options ?? {}) as RehypeCodeOptionsCommon,
};
let highlighter: HighlighterCore;
let options: RehypeCodeOptionsCommon;
if (typeof highlighterFactory === 'function') {
const out = await highlighterFactory(_options);
highlighter = out.highlighter;
options = out.options;
} else {
// TODO: When newer Shiki supported it, register lang alias dynamically instead of creating new instance
highlighter = _options?.langAlias
? await highlighterFactory.init(_options)
: await highlighterFactory.getOrInit();
options = (_options ?? {}) as RehypeCodeOptionsCommon;
}
const transformers = options.transformers ? [...options.transformers] : [];
transformers.unshift({
+8 -3
View File
@@ -83,6 +83,7 @@
"fumadocs-core": "workspace:*",
"fumadocs-ui": "workspace:*",
"json-schema-typed": "^8.0.2",
"shiki": "^4.0.2",
"tailwindcss": "^4.2.1",
"tsconfig": "workspace:*",
"tsdown": "0.21.2"
@@ -90,13 +91,17 @@
"peerDependencies": {
"@scalar/api-client-react": "*",
"@types/react": "*",
"fumadocs-core": "^16.5.0",
"fumadocs-ui": "^16.5.0",
"fumadocs-core": "^16.7.0",
"fumadocs-ui": "^16.7.0",
"json-schema-typed": "*",
"react": "^19.2.0",
"react-dom": "^19.2.0"
"react-dom": "^19.2.0",
"shiki": "*"
},
"peerDependenciesMeta": {
"shiki": {
"optional": true
},
"json-schema-typed": {
"optional": true
},
+2 -4
View File
@@ -15,7 +15,6 @@ import type { FetchResult } from '@/playground/fetcher';
import type { SecurityEntry } from '@/playground/index';
import { getStatusInfo } from './status-info';
import { joinURL, resolveRequestData, resolveServerUrl, withBase } from '@/utils/url';
import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock.core';
import { MethodLabel } from '@/ui/components/method-label';
import { useQuery } from '@/utils/use-query';
import {
@@ -52,6 +51,7 @@ import {
import { objectGet, objectSet, stringifyFieldKey } from '@fumari/stf/lib/utils';
import { FieldInput, FieldSet, JsonInput, ObjectInput } from './components/inputs';
import type { ParameterObject } from '@/types';
import { ClientCodeBlock } from '@/ui/components/codeblock';
export interface FormValues extends Record<string, unknown> {
path: Record<string, unknown>;
@@ -696,7 +696,6 @@ function Route({ route, ...props }: ComponentProps<'div'> & { route: string }) {
function DefaultResultDisplay({ data, reset }: { data: FetchResult; reset: () => void }) {
const statusInfo = useMemo(() => getStatusInfo(data.status), [data.status]);
const { shikiOptions } = useApiContext();
return (
<div className="flex flex-col gap-3 mt-2 px-3 py-2 border-y bg-fd-secondary text-fd-secondary-foreground">
@@ -715,10 +714,9 @@ function DefaultResultDisplay({ data, reset }: { data: FetchResult; reset: () =>
</div>
<p className="text-sm text-fd-muted-foreground">{data.status}</p>
{data.data !== undefined && (
<DynamicCodeBlock
<ClientCodeBlock
lang={typeof data.data === 'string' && data.data.length > 50000 ? 'text' : data.type}
code={typeof data.data === 'string' ? data.data : JSON.stringify(data.data, null, 2)}
options={shikiOptions}
/>
)}
</div>
+12 -7
View File
@@ -1,11 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any -- rehype-react without types */
import Slugger from 'github-slugger';
import type { Awaitable, DistributiveOmit, MethodInformation, RenderContext } from '@/types';
import type { Awaitable, MethodInformation, RenderContext } from '@/types';
import type { NoReference } from '@/utils/schema';
import type { ProcessedDocument } from '@/utils/process-document';
import { defaultAdapters, MediaAdapter } from '@/requests/media/adapter';
import type { FC, HTMLAttributes, ReactNode } from 'react';
import { highlight, type CoreHighlightOptions } from 'fumadocs-core/highlight/core';
import type { OpenAPIServer } from '@/server';
import type { APIPageClientOptions } from './client';
import { Heading } from 'fumadocs-ui/components/heading';
@@ -20,10 +19,11 @@ import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
import type { SchemaUIOptions } from './schema';
import type { ResponseTab } from './operation/response-tabs';
import type { ExampleRequestItem } from './operation/request-tabs';
import type { ResolvedShikiConfig } from 'fumadocs-core/highlight/config';
import { APIPage, type ApiPageProps, type OperationItem, type WebhookItem } from './api-page';
import type { CodeUsageGeneratorRegistry, InlineCodeUsageGenerator } from '@/requests/generators';
import type { JSONSchema } from 'json-schema-typed';
import type { BundledTheme, CodeOptionsThemes, CodeToHastOptionsCommon } from 'shiki';
import { highlightHast, type ShikiFactory } from 'fumadocs-core/highlight/shiki';
export interface GenerateTypeScriptDefinitionsContext extends RenderContext {
operation: NoReference<MethodInformation>;
@@ -75,9 +75,9 @@ export interface CreateAPIPageOptions {
*/
generateCodeSamples?: (method: MethodInformation) => Awaitable<InlineCodeUsageGenerator[]>;
shiki: ResolvedShikiConfig;
shiki: ShikiFactory;
renderMarkdown?: (md: string) => ReactNode;
shikiOptions?: DistributiveOmit<CoreHighlightOptions, 'config' | 'lang' | 'components'>;
shikiOptions: Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BundledTheme>;
/**
* Show full response schema instead of only example response & Typescript definitions.
@@ -233,6 +233,8 @@ export function createAPIPage(
.use(createRehypeCode(options.shiki), {
langs: [],
lazy: true,
defaultColor: false,
...options.shikiOptions,
})
.use(rehypeReact);
}
@@ -293,10 +295,13 @@ export function createAPIPage(
return options.renderCodeBlock({ lang, code });
}
const rendered = await highlight(code, {
const hast = await highlightHast(await options.shiki.getOrInit(), code, {
lang,
defaultColor: false,
...options.shikiOptions,
config: options.shiki,
});
const rendered = toJsxRuntime(hast, {
...JsxRuntime,
components: {
pre: Pre,
},
@@ -0,0 +1,31 @@
'use client';
import {
DynamicCodeBlock,
type DynamicCodeblockProps,
} from 'fumadocs-ui/components/dynamic-codeblock.core';
import { useApiContext } from '../contexts/api';
import { createContext, type ReactNode, use } from 'react';
import type { ShikiFactory } from 'fumadocs-core/highlight/shiki';
const CodeBlockContext = createContext<ShikiFactory | null>(null);
export function ClientCodeBlock(props: Omit<DynamicCodeblockProps, 'highlighter' | 'options'>) {
const { shikiOptions } = useApiContext();
const ctx = use(CodeBlockContext);
if (!ctx)
throw new Error(
'Missing Shiki context, please wrap your <APIPage /> component under <ClientCodeBlockProvider />',
);
return <DynamicCodeBlock highlighter={() => ctx.getOrInit()} options={shikiOptions} {...props} />;
}
export function ClientCodeBlockProvider({
factory,
children,
}: {
factory: ShikiFactory;
children: ReactNode;
}) {
return <CodeBlockContext value={factory}>{children}</CodeBlockContext>;
}
+6 -5
View File
@@ -1,9 +1,10 @@
'use client';
import * as base from 'fumadocs-core/highlight/core/client';
import { configDefault } from 'fumadocs-core/highlight';
import type { ReactNode } from 'react';
import { ClientCodeBlockProvider } from './components/codeblock';
import { defaultShikiFactory } from 'fumadocs-core/highlight/shiki/full';
export function ShikiConfigProvider({ children }: { children: ReactNode }) {
const config = base.useShikiConfigOptional() ?? configDefault;
return <base.ShikiConfigProvider config={config}>{children}</base.ShikiConfigProvider>;
export function FullProvider({ children }: { children: ReactNode }) {
return (
<ClientCodeBlockProvider factory={defaultShikiFactory}>{children}</ClientCodeBlockProvider>
);
}
+7 -6
View File
@@ -1,14 +1,15 @@
import type { OpenAPIServer } from '@/server';
import * as base from './base';
import { configDefault } from 'fumadocs-core/highlight';
import { ApiPageProps } from './api-page';
import { ShikiConfigProvider } from './full.client';
import type { ApiPageProps } from './api-page';
import { defaultShikiFactory } from 'fumadocs-core/highlight/shiki/full';
import { FullProvider } from './full.client';
export type CreateAPIPageOptions = Partial<base.CreateAPIPageOptions>;
export function createAPIPage(server: OpenAPIServer, options: CreateAPIPageOptions = {}) {
const APIPage = base.createAPIPage(server, {
shiki: configDefault,
shiki: defaultShikiFactory,
shikiOptions: { themes: { light: 'github-light', dark: 'github-dark' } },
async generateTypeScriptDefinitions(schema, ctx) {
if (typeof schema !== 'object') return;
const { compile } = await import('@fumari/json-schema-ts');
@@ -29,9 +30,9 @@ export function createAPIPage(server: OpenAPIServer, options: CreateAPIPageOptio
return function APIPageFull(props: ApiPageProps) {
return (
<ShikiConfigProvider>
<FullProvider>
<APIPage {...props} />
</ShikiConfigProvider>
</FullProvider>
);
};
}
@@ -8,11 +8,11 @@ import {
SelectContent,
SelectItem,
} from '@/ui/components/select';
import { DynamicCodeBlock } from 'fumadocs-ui/components/dynamic-codeblock.core';
import { useState, useEffect, useMemo, createContext, ReactNode, useRef, use } from 'react';
import type { ExampleRequestItem } from '../request-tabs';
import type { RawRequestData, RequestData } from '@/requests/types';
import type { CodeUsageGenerator } from '@/requests/generators';
import { ClientCodeBlock } from '@/ui/components/codeblock';
export type ExampleUpdateListener = (data: RawRequestData, encoded: RequestData) => void;
@@ -135,7 +135,7 @@ export function UsageTab({
lang,
_client,
}: Pick<CodeUsageGenerator, 'lang' | '_client'> & { id: string }) {
const { shikiOptions, mediaAdapters, codeUsages } = useApiContext();
const { mediaAdapters, codeUsages } = useApiContext();
const {
examples,
example: selectedExampleId,
@@ -186,5 +186,5 @@ export function UsageTab({
if (!code) return null;
return <DynamicCodeBlock lang={lang} code={code} options={shikiOptions} />;
return <ClientCodeBlock lang={lang} code={code} />;
}
+3 -2
View File
@@ -1,6 +1,6 @@
import { RenderContext } from '@/types';
import { processDocument } from '@/utils/process-document';
import { configDefault } from 'fumadocs-core/highlight';
import { defaultShikiFactory } from 'fumadocs-core/highlight/shiki/full';
import Slugger from 'github-slugger';
export async function renderContextFrom(input: string): Promise<RenderContext> {
@@ -11,7 +11,8 @@ export async function renderContextFrom(input: string): Promise<RenderContext> {
generateTypeScriptDefinitions() {
return '';
},
shiki: configDefault,
shiki: defaultShikiFactory,
shikiOptions: { theme: 'github-light' },
renderCodeBlock(_lang, code) {
return code;
},
+7 -2
View File
@@ -59,6 +59,7 @@
"fumadocs-core": "workspace:*",
"fumadocs-ui": "workspace:*",
"mdast-util-mdx": "^3.0.0",
"shiki": "^4.0.2",
"tsconfig": "workspace:*",
"tsdown": "0.21.2",
"typescript": "^5.9.3",
@@ -69,12 +70,16 @@
"@types/hast": "*",
"@types/mdast": "*",
"@types/react": "*",
"fumadocs-core": "^16.5.0",
"fumadocs-ui": "^16.5.0",
"fumadocs-core": "^16.7.0",
"fumadocs-ui": "^16.7.0",
"react": "*",
"shiki": "*",
"typescript": "*"
},
"peerDependenciesMeta": {
"shiki": {
"optional": true
},
"@types/estree": {
"optional": true
},
@@ -3,13 +3,12 @@ import type { Nodes } from 'hast';
import type { Transformer } from 'unified';
import type { Expression, ExpressionStatement, ObjectExpression } from 'estree';
import { createGenerator, type DocEntry, type Generator } from '@/lib/base';
import { type MarkdownRenderer, markdownRenderer } from '@/markdown';
import { type MarkdownRenderer, markdownRenderer, type ShikiOptions } from '@/markdown';
import { valueToEstree } from 'estree-util-value-to-estree';
import { visit } from 'unist-util-visit';
import { type BaseTypeTableProps, type GenerateTypeTableOptions } from '@/lib/type-table';
import { toEstree } from 'hast-util-to-estree';
import { type ParameterTag, parseTags } from '@/lib/parse-tags';
import type { ResolvedShikiConfig } from 'fumadocs-core/highlight/config';
import type { MdxJsxAttribute, MdxJsxExpressionAttribute, MdxJsxFlowElement } from 'mdast-util-mdx';
import type { VFile } from 'vfile';
@@ -118,7 +117,7 @@ export interface RemarkAutoTypeTableOptions {
/**
* config for Shiki when using default `renderMarkdown` & `renderType`.
*/
shiki?: ResolvedShikiConfig;
shiki?: ShikiOptions;
renderMarkdown?: MarkdownRenderer['renderMarkdownToHast'];
renderType?: MarkdownRenderer['renderTypeToHast'];
+9 -7
View File
@@ -1,33 +1,35 @@
import type { ElementContent, Nodes } from 'hast';
import { remark } from 'remark';
import { remarkGfm } from 'fumadocs-core/mdx-plugins/remark-gfm';
import { createRehypeCode } from 'fumadocs-core/mdx-plugins/rehype-code.core';
import { rehypeCode } from 'fumadocs-core/mdx-plugins/rehype-code';
import remarkRehype from 'remark-rehype';
import { highlightHast } from 'fumadocs-core/highlight/core';
import { configDefault } from 'fumadocs-core/highlight';
import { highlightHast } from 'fumadocs-core/highlight';
import type { CodeToHastOptionsCommon, CodeOptionsThemes, BundledTheme } from 'shiki';
export interface MarkdownRenderer {
renderTypeToHast: (type: string) => Nodes | Promise<Nodes>;
renderMarkdownToHast: (md: string) => Nodes | Promise<Nodes>;
}
export function markdownRenderer(shiki = configDefault): MarkdownRenderer {
export type ShikiOptions = Omit<CodeToHastOptionsCommon, 'lang'> & CodeOptionsThemes<BundledTheme>;
export function markdownRenderer(options?: ShikiOptions): MarkdownRenderer {
const processor = remark()
.use(remarkGfm)
.use(remarkRehype)
.use(createRehypeCode(shiki), {
lazy: true,
.use(rehypeCode, {
langs: ['ts', 'tsx'],
// disable default transformers & meta parser
transformers: [],
parseMetaString: undefined,
...options,
});
return {
async renderTypeToHast(type) {
const nodes = await highlightHast(type, {
config: shiki,
lang: 'ts',
structure: 'inline',
...options,
});
return {
@@ -1,6 +1,6 @@
import { type ParameterNode, type TypeNode, TypeTable } from 'fumadocs-ui/components/type-table';
import { type Jsx, toJsxRuntime } from 'hast-util-to-jsx-runtime';
import * as runtime from 'react/jsx-runtime';
import { toJsxRuntime } from 'hast-util-to-jsx-runtime';
import * as JsxRuntime from 'react/jsx-runtime';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import 'server-only';
import type { ComponentProps, ReactNode } from 'react';
@@ -8,14 +8,13 @@ import { type BaseTypeTableProps, type GenerateTypeTableOptions } from '@/lib/ty
import { type Generator } from '@/lib/base';
import type { Nodes } from 'hast';
import { parseTags } from '@/lib/parse-tags';
import type { ResolvedShikiConfig } from 'fumadocs-core/highlight/config';
import { markdownRenderer } from '@/markdown';
import { markdownRenderer, type ShikiOptions } from '@/markdown';
export interface AutoTypeTableProps extends BaseTypeTableProps, ComponentProps<'div'> {
generator: Generator;
/** Shiki configuration when using default `renderMarkdown` & `renderType` */
shiki?: ResolvedShikiConfig;
shiki?: ShikiOptions;
options?: GenerateTypeTableOptions;
renderMarkdown?: (md: string) => Promise<ReactNode>;
@@ -82,9 +81,7 @@ export async function AutoTypeTable({
function toJsx(hast: Nodes) {
return toJsxRuntime(hast, {
Fragment: runtime.Fragment,
jsx: runtime.jsx as Jsx,
jsxs: runtime.jsxs as Jsx,
...JsxRuntime,
components: { ...defaultMdxComponents, img: undefined },
});
}