mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
OpenAPI: Use new codeblock tab style
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
---
|
||||
|
||||
Use new codeblock tab style
|
||||
@@ -18,6 +18,12 @@ import {
|
||||
import type { RenderContext } from '@/types';
|
||||
import { APIPlayground, type APIPlaygroundProps } from '@/playground';
|
||||
import { CodeExampleSelector } from '@/ui/lazy';
|
||||
import {
|
||||
CodeBlockTab,
|
||||
CodeBlockTabs,
|
||||
CodeBlockTabsList,
|
||||
CodeBlockTabsTrigger,
|
||||
} from 'fumadocs-ui/components/codeblock';
|
||||
|
||||
export interface ResponsesProps {
|
||||
items: string[];
|
||||
@@ -133,12 +139,26 @@ export function createRenders(): Renderer {
|
||||
),
|
||||
Property,
|
||||
ObjectCollapsible,
|
||||
Requests: (props) => (
|
||||
<Tabs groupId="fumadocs_openapi_requests" {...props} />
|
||||
Requests: ({ items, children }) => (
|
||||
<CodeBlockTabs
|
||||
groupId="fumadocs_openapi_requests"
|
||||
defaultValue={items[0]}
|
||||
>
|
||||
<CodeBlockTabsList>
|
||||
{items.map((item) => (
|
||||
<CodeBlockTabsTrigger key={item} value={item}>
|
||||
{item}
|
||||
</CodeBlockTabsTrigger>
|
||||
))}
|
||||
</CodeBlockTabsList>
|
||||
{children}
|
||||
</CodeBlockTabs>
|
||||
),
|
||||
|
||||
CodeExampleSelector,
|
||||
Request: (props) => <Tab value={props.name}>{props.children}</Tab>,
|
||||
Request: (props) => (
|
||||
<CodeBlockTab value={props.name}>{props.children}</CodeBlockTab>
|
||||
),
|
||||
APIPlayground,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export function Schema({
|
||||
readOnly = false,
|
||||
writeOnly = false,
|
||||
as = 'property',
|
||||
ctx: { renderer },
|
||||
ctx: renderContext,
|
||||
}: {
|
||||
name: string;
|
||||
required?: boolean;
|
||||
@@ -33,6 +33,8 @@ export function Schema({
|
||||
writeOnly?: boolean;
|
||||
ctx: RenderContext;
|
||||
}): ReactNode {
|
||||
const { renderer } = renderContext;
|
||||
|
||||
function propertyBody(
|
||||
schema: Exclude<ResolvedSchema, boolean>,
|
||||
renderPrimitive: (
|
||||
@@ -59,7 +61,7 @@ export function Schema({
|
||||
<TabsList>
|
||||
{items.map((item) => (
|
||||
<TabsTrigger key={item.type} value={item.type}>
|
||||
{schemaToString(item)}
|
||||
{schemaToString(item, renderContext.schema)}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
@@ -88,7 +90,7 @@ export function Schema({
|
||||
<TabsList>
|
||||
{oneOf.map((item, i) => (
|
||||
<TabsTrigger key={i} value={i.toString()}>
|
||||
{schemaToString(item)}
|
||||
{schemaToString(item, renderContext.schema)}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
@@ -309,7 +311,7 @@ export function Schema({
|
||||
return (
|
||||
<renderer.Property
|
||||
name={key}
|
||||
type={schemaToString(schema)}
|
||||
type={schemaToString(schema, renderContext.schema)}
|
||||
deprecated={schema.deprecated}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -5,12 +5,16 @@ export async function getTypescriptSchema(
|
||||
schema: object,
|
||||
dereferenceMap: DereferenceMap,
|
||||
): Promise<string | undefined> {
|
||||
const cloned = structuredClone({ schema, dereferenceMap });
|
||||
return compile(cloned.schema, 'Response', {
|
||||
$refOptions: false,
|
||||
schemaToId: cloned.dereferenceMap,
|
||||
bannerComment: '',
|
||||
additionalProperties: false,
|
||||
enableConstEnums: false,
|
||||
});
|
||||
try {
|
||||
const cloned = structuredClone({ schema, dereferenceMap });
|
||||
return await compile(cloned.schema, 'Response', {
|
||||
$refOptions: false,
|
||||
schemaToId: cloned.dereferenceMap,
|
||||
bannerComment: '',
|
||||
additionalProperties: false,
|
||||
enableConstEnums: false,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to generate typescript schema:', e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,71 @@
|
||||
import { type ParsedSchema, type ResolvedSchema } from '@/utils/schema';
|
||||
import type { ProcessedDocument } from '@/utils/process-document';
|
||||
|
||||
export function schemaToString(schema: ResolvedSchema, isRoot = true): string {
|
||||
if (schema === true) return 'any';
|
||||
else if (schema === false) return 'never';
|
||||
export function schemaToString(
|
||||
value: ResolvedSchema,
|
||||
ctx?: ProcessedDocument,
|
||||
): string {
|
||||
function run(schema: ResolvedSchema, isRoot: boolean): string {
|
||||
if (schema === true) return 'any';
|
||||
else if (schema === false) return 'never';
|
||||
|
||||
if (isNullable(schema) && isRoot) {
|
||||
const type = schemaToString(schema, false);
|
||||
if (isNullable(schema) && isRoot) {
|
||||
const type = run(schema, false);
|
||||
|
||||
// null if schema only contains `nullable`
|
||||
return type === 'unknown' ? 'null' : `${type} | null`;
|
||||
// null if schema only contains `nullable`
|
||||
return type === 'unknown' ? 'null' : `${type} | null`;
|
||||
}
|
||||
|
||||
if (schema.title) return schema.title;
|
||||
const referenceName = ctx?.dereferenceMap.get(schema);
|
||||
if (referenceName) return referenceName.split('/').at(-1)!;
|
||||
|
||||
if (Array.isArray(schema.type)) {
|
||||
return schema.type
|
||||
.map((type, _, originalType) => {
|
||||
schema.type = type;
|
||||
const str = run(schema, false);
|
||||
schema.type = originalType;
|
||||
|
||||
return str;
|
||||
})
|
||||
.filter((v) => v !== 'unknown' && v !== 'null')
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
if (schema.type === 'array')
|
||||
return `array<${schema.items ? run(schema.items, true) : 'unknown'}>`;
|
||||
|
||||
if (schema.oneOf) {
|
||||
return schema.oneOf
|
||||
.map((one) => run(one, false))
|
||||
.filter((v) => v !== 'unknown' && v !== 'null')
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
const combinedOf = schema.anyOf ?? schema.allOf;
|
||||
if (combinedOf) {
|
||||
return combinedOf
|
||||
.map((one) => run(one, false))
|
||||
.filter((v) => v !== 'unknown' && v !== 'null')
|
||||
.join(' & ');
|
||||
}
|
||||
|
||||
if (schema.not) return `not ${run(schema.not, false)}`;
|
||||
if (schema.type === 'string' && schema.format === 'binary') return 'file';
|
||||
|
||||
if (schema.type && Array.isArray(schema.type)) {
|
||||
return schema.type.filter((v) => v !== 'null').join(' | ');
|
||||
}
|
||||
|
||||
if (schema.type) {
|
||||
return schema.type as string;
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
if (schema.title) return schema.title;
|
||||
|
||||
if (Array.isArray(schema.type)) {
|
||||
return schema.type
|
||||
.map((type) =>
|
||||
schemaToString(
|
||||
{
|
||||
...schema,
|
||||
type,
|
||||
},
|
||||
false,
|
||||
),
|
||||
)
|
||||
.filter((v) => v !== 'unknown' && v !== 'null')
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
if (schema.type === 'array')
|
||||
return `array<${schema.items ? schemaToString(schema.items) : 'unknown'}>`;
|
||||
|
||||
if (schema.oneOf) {
|
||||
return schema.oneOf
|
||||
.map((one) => schemaToString(one, false))
|
||||
.filter((v) => v !== 'unknown' && v !== 'null')
|
||||
.join(' | ');
|
||||
}
|
||||
|
||||
const combinedOf = schema.anyOf ?? schema.allOf;
|
||||
if (combinedOf) {
|
||||
return combinedOf
|
||||
.map((one) => schemaToString(one, false))
|
||||
.filter((v) => v !== 'unknown' && v !== 'null')
|
||||
.join(' & ');
|
||||
}
|
||||
|
||||
if (schema.not) return `not ${schemaToString(schema.not, false)}`;
|
||||
if (schema.type === 'string' && schema.format === 'binary') return 'file';
|
||||
|
||||
if (schema.type && Array.isArray(schema.type)) {
|
||||
return schema.type.filter((v) => v !== 'null').join(' | ');
|
||||
}
|
||||
|
||||
if (schema.type) {
|
||||
return schema.type as string;
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
return run(value, true);
|
||||
}
|
||||
|
||||
function isNullable(schema: ParsedSchema): boolean {
|
||||
|
||||
Reference in New Issue
Block a user