mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 06:31:49 +00:00
OpenAPI: Reduce initial loaded bundle size
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
---
|
||||
|
||||
Reduce initial loaded bundle size
|
||||
@@ -183,7 +183,7 @@ export async function generateTags(
|
||||
|
||||
function pageContent(
|
||||
doc: OpenAPI.Document,
|
||||
props: Omit<ApiPageProps, 'ctx'>,
|
||||
props: Omit<ApiPageProps, 'document'>,
|
||||
): string {
|
||||
const slugger = new Slugger();
|
||||
const toc: TableOfContents = [];
|
||||
|
||||
@@ -83,7 +83,6 @@ export function Operation({
|
||||
name="body"
|
||||
schema={noRef(body.content[type].schema ?? {})}
|
||||
ctx={{
|
||||
parseObject: true,
|
||||
readOnly: method.method === 'GET',
|
||||
writeOnly: method.method !== 'GET',
|
||||
required: body.required ?? false,
|
||||
|
||||
@@ -5,12 +5,12 @@ import type { APIPlaygroundProps } from '@/render/playground';
|
||||
import { CodeBlock } from '@/render/codeblock';
|
||||
import {
|
||||
API,
|
||||
APIInfo,
|
||||
APIPlayground,
|
||||
APIExample,
|
||||
Root,
|
||||
APIInfo,
|
||||
APIExample,
|
||||
Property,
|
||||
ObjectCollapsible,
|
||||
APIPlayground,
|
||||
} from '@/ui';
|
||||
|
||||
export interface ResponsesProps {
|
||||
|
||||
@@ -23,8 +23,12 @@ interface Context {
|
||||
|
||||
required: boolean;
|
||||
|
||||
/** Render the full object */
|
||||
parseObject: boolean;
|
||||
/**
|
||||
* Render the full object
|
||||
*
|
||||
* @defaultValue true
|
||||
* */
|
||||
parseObject?: boolean;
|
||||
|
||||
/**
|
||||
* Parse binary format string to be files
|
||||
@@ -60,7 +64,8 @@ export function Schema({
|
||||
(schema.writeOnly === true && !ctx.writeOnly)
|
||||
)
|
||||
return null;
|
||||
ctx.allowFile ??= true;
|
||||
const parseObject = ctx.parseObject ?? true;
|
||||
|
||||
const stack = ctx.stack ?? [];
|
||||
|
||||
const { renderer } = ctx.render;
|
||||
@@ -75,7 +80,7 @@ export function Schema({
|
||||
}
|
||||
|
||||
// object type
|
||||
if (isObject(schema) && ctx.parseObject) {
|
||||
if (isObject(schema) && parseObject) {
|
||||
const { additionalProperties, properties } = schema;
|
||||
|
||||
if (additionalProperties === true) {
|
||||
@@ -139,7 +144,7 @@ export function Schema({
|
||||
);
|
||||
}
|
||||
|
||||
if (isObject(schema) && !ctx.parseObject) {
|
||||
if (isObject(schema) && !parseObject) {
|
||||
child.push(
|
||||
<renderer.ObjectCollapsible key="attributes" name="Attributes">
|
||||
<Schema
|
||||
@@ -286,7 +291,7 @@ function getSchemaType(schema: OpenAPI.SchemaObject, ctx: Context): string {
|
||||
}
|
||||
|
||||
if (schema.type === 'string' && schema.format === 'binary' && ctx.allowFile)
|
||||
return 'File';
|
||||
return 'file';
|
||||
if (schema.type) return schema.type;
|
||||
|
||||
if (isObject(schema)) return 'object';
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { type OpenAPIV3 as OpenAPI } from 'openapi-types';
|
||||
import Slugger from 'github-slugger';
|
||||
import { Operation } from '@/render/operation';
|
||||
import type { RenderContext } from '@/types';
|
||||
import { createMethod } from '@/schema/method';
|
||||
import { defaultRenderer, type Renderer } from '@/render/renderer';
|
||||
|
||||
export interface ApiPageProps
|
||||
extends Pick<
|
||||
RenderContext,
|
||||
'generateCodeSamples' | 'generateTypeScriptSchema'
|
||||
> {
|
||||
document: OpenAPI.Document;
|
||||
|
||||
export interface ApiPageProps {
|
||||
/**
|
||||
* An array of operation
|
||||
*/
|
||||
operations: { path: string; method: OpenAPI.HttpMethods }[];
|
||||
hasHead: boolean;
|
||||
ctx: RenderContext;
|
||||
renderer?: Partial<Renderer>;
|
||||
}
|
||||
|
||||
export function APIPage({
|
||||
operations,
|
||||
ctx,
|
||||
hasHead = true,
|
||||
}: ApiPageProps): ReactElement {
|
||||
const schema = ctx.document;
|
||||
export function APIPage(props: ApiPageProps): ReactElement {
|
||||
const { operations, document, hasHead = true } = props;
|
||||
|
||||
const ctx = getContext(document, props);
|
||||
return (
|
||||
<ctx.renderer.Root baseUrl={ctx.baseUrl}>
|
||||
{operations.map((item) => {
|
||||
const operation = schema.paths[item.path]?.[item.method];
|
||||
const operation = document.paths[item.path]?.[item.method];
|
||||
if (!operation) return null;
|
||||
const method = createMethod(item.method, operation);
|
||||
|
||||
@@ -40,3 +45,20 @@ export function APIPage({
|
||||
</ctx.renderer.Root>
|
||||
);
|
||||
}
|
||||
|
||||
function getContext(
|
||||
document: OpenAPI.Document,
|
||||
options: ApiPageProps,
|
||||
): RenderContext {
|
||||
return {
|
||||
document,
|
||||
renderer: {
|
||||
...defaultRenderer,
|
||||
...options.renderer,
|
||||
},
|
||||
generateTypeScriptSchema: options.generateTypeScriptSchema,
|
||||
generateCodeSamples: options.generateCodeSamples,
|
||||
baseUrl: document.servers?.[0].url ?? 'https://example.com',
|
||||
slugger: new Slugger(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import type { OpenAPIV3 as OpenAPI } from 'openapi-types';
|
||||
import Parser from '@apidevtools/json-schema-ref-parser';
|
||||
import type { FC } from 'react';
|
||||
import Slugger from 'github-slugger';
|
||||
import { type FC } from 'react';
|
||||
import { APIPage, type ApiPageProps } from '@/server/api-page';
|
||||
import type { RenderContext } from '@/types';
|
||||
import { defaultRenderer, type Renderer } from '@/render/renderer';
|
||||
|
||||
export interface OpenAPIOptions
|
||||
extends Pick<
|
||||
RenderContext,
|
||||
'generateCodeSamples' | 'generateTypeScriptSchema'
|
||||
> {
|
||||
extends Omit<Partial<ApiPageProps>, 'document'> {
|
||||
documentOrPath: string | OpenAPI.Document;
|
||||
|
||||
renderer?: Partial<Renderer>;
|
||||
}
|
||||
|
||||
export interface OpenAPIServer {
|
||||
APIPage: FC<Omit<ApiPageProps, 'ctx'>>;
|
||||
APIPage: FC<Omit<ApiPageProps, 'document'>>;
|
||||
}
|
||||
|
||||
export function createOpenAPI(options: OpenAPIOptions): OpenAPIServer {
|
||||
@@ -25,26 +17,9 @@ export function createOpenAPI(options: OpenAPIOptions): OpenAPIServer {
|
||||
|
||||
return {
|
||||
APIPage: async (props) => {
|
||||
const ctx = getContext((await document) as OpenAPI.Document, options);
|
||||
|
||||
return <APIPage ctx={ctx} {...props} />;
|
||||
return (
|
||||
<APIPage document={(await document) as OpenAPI.Document} {...props} />
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getContext(
|
||||
document: OpenAPI.Document,
|
||||
options: OpenAPIOptions,
|
||||
): RenderContext {
|
||||
return {
|
||||
document,
|
||||
renderer: {
|
||||
...defaultRenderer,
|
||||
...options.renderer,
|
||||
},
|
||||
generateTypeScriptSchema: options.generateTypeScriptSchema,
|
||||
generateCodeSamples: options.generateCodeSamples,
|
||||
baseUrl: document.servers?.[0].url ?? 'https://example.com',
|
||||
slugger: new Slugger(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,16 +14,16 @@ export const attachFile: BuildPageTreeOptions['attachFile'] = (node, file) => {
|
||||
if ('method' in data && typeof data.method === 'string') {
|
||||
const color = getBadgeColor(data.method);
|
||||
|
||||
node.name = [
|
||||
node.name,
|
||||
' ',
|
||||
// eslint-disable-next-line react/jsx-key -- static
|
||||
<span
|
||||
className={badgeVariants({ className: 'ms-auto text-nowrap', color })}
|
||||
>
|
||||
{data.method}
|
||||
</span>,
|
||||
];
|
||||
node.name = (
|
||||
<>
|
||||
{node.name}{' '}
|
||||
<span
|
||||
className={badgeVariants({ className: 'ms-auto text-nowrap', color })}
|
||||
>
|
||||
{data.method}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
import { type ButtonHTMLAttributes, type HTMLAttributes } from 'react';
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
import { cn, useCopyButton, buttonVariants } from 'fumadocs-ui/components/api';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { ApiProvider, useApiContext } from '@/ui/contexts/api';
|
||||
import { type RootProps } from '@/render/renderer';
|
||||
|
||||
export const APIPlayground = dynamic(() =>
|
||||
import('./playground').then((mod) => mod.APIPlayground),
|
||||
);
|
||||
|
||||
export function Root({
|
||||
children,
|
||||
baseUrl,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import dynamic from 'next/dynamic';
|
||||
import { cn } from 'fumadocs-ui/components/api';
|
||||
import { Fragment, type HTMLAttributes, type ReactNode } from 'react';
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
@@ -6,11 +5,7 @@ import { badgeVariants, getBadgeColor } from '@/ui/components/variants';
|
||||
import type { APIInfoProps, PropertyProps } from '@/render/renderer';
|
||||
import { CopyRouteButton } from '@/ui/client';
|
||||
|
||||
export { Root, useSchemaContext } from './client';
|
||||
|
||||
export const APIPlayground = dynamic(() =>
|
||||
import('./playground').then((mod) => mod.APIPlayground),
|
||||
);
|
||||
export { Root, useSchemaContext, APIPlayground } from './client';
|
||||
|
||||
function Route({ route }: { route: string }): ReactNode {
|
||||
const segments = route.split('/').filter((part) => part.length > 0);
|
||||
|
||||
Reference in New Issue
Block a user