OpenAPI: change Scalar usage

This commit is contained in:
Fuma Nama
2025-01-31 17:22:45 +08:00
parent 4c947b353d
commit 1286a044e6
16 changed files with 78 additions and 89 deletions
+29
View File
@@ -0,0 +1,29 @@
---
'fumadocs-openapi': major
---
**Change interface for `useScalar`**
From:
```tsx
import { createOpenAPI } from 'fumadocs-openapi/server';
import { APIPlayground } from 'fumadocs-openapi/scalar';
export const openapi = createOpenAPI({
useScalar: true
});
```
To:
```tsx
import { createOpenAPI } from 'fumadocs-openapi/server';
import { APIPlayground } from 'fumadocs-openapi/scalar';
export const openapi = createOpenAPI({
renderer: {
APIPlayground,
},
});
```
+4 -1
View File
@@ -115,9 +115,12 @@ You can enable the Scalar API Client using `useScalar` option in `createOpenAPI(
```ts
import { createOpenAPI } from 'fumadocs-openapi/server';
import { APIPlayground } from 'fumadocs-openapi/scalar';
export const openapi = createOpenAPI({
useScalar: true,
renderer: {
APIPlayground,
},
});
```
-1
View File
@@ -19,7 +19,6 @@
"@radix-ui/react-popover": "^1.1.5",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.7",
"@scalar/api-client-react": "^1.1.17",
"@shikijs/rehype": "^2.1.0",
"@shikijs/twoslash": "^2.1.0",
"@theguild/remark-mermaid": "^0.2.0",
+6 -1
View File
@@ -2,6 +2,7 @@ import { createMDXSource } from 'fumadocs-mdx';
import { loader } from 'fumadocs-core/source';
import { attachFile, createOpenAPI } from 'fumadocs-openapi/server';
import { docs, meta } from '@/.source';
import { APIPlayground } from 'fumadocs-openapi/scalar';
export const source = loader({
baseUrl: '/docs',
@@ -11,4 +12,8 @@ export const source = loader({
},
});
export const openapi = createOpenAPI();
export const openapi = createOpenAPI({
renderer: {
APIPlayground,
},
});
+4
View File
@@ -20,6 +20,10 @@
"import": "./dist/ui/index.js",
"types": "./dist/ui/index.d.ts"
},
"./scalar": {
"import": "./dist/scalar/index.js",
"types": "./dist/scalar/index.d.ts"
},
"./server": {
"import": "./dist/server/index.js",
"types": "./dist/server/index.d.ts"
+1 -2
View File
@@ -14,7 +14,6 @@ import type {
import { getPreferredType, NoReference } from '@/utils/schema';
import { getTypescriptSchema } from '@/utils/get-typescript-schema';
import { getSecurities, getSecurityPrefix } from '@/utils/get-security';
import { Playground } from '@/render/playground';
import { idToTitle } from '@/utils/id-to-title';
import { type ResponseTypeProps } from '@/render/renderer';
import { Markdown } from './markdown';
@@ -202,7 +201,7 @@ export function Operation({
const info = (
<ctx.renderer.APIInfo head={headNode} method={method.method} route={path}>
{type === 'operation' ? (
<Playground path={path} method={method} ctx={ctx} />
<ctx.renderer.APIPlayground path={path} method={method} ctx={ctx} />
) : null}
{security ? (
<>
+2 -12
View File
@@ -10,7 +10,7 @@ import {
type ParsedSchema,
} from '@/utils/schema';
import { getSecurities } from '@/utils/get-security';
import { ScalarPlayground } from '@/ui';
import { APIPlayground } from '@/ui';
interface BaseRequestField {
name: string;
@@ -104,16 +104,6 @@ export function Playground({
method: MethodInformation;
ctx: RenderContext;
}): ReactNode {
if (ctx.useScalar) {
return (
<ScalarPlayground
spec={ctx.schema.downloaded}
method={method.method}
path={path}
/>
);
}
let currentId = 0;
const bodyContent = method.requestBody?.content;
const mediaType = bodyContent ? getPreferredType(bodyContent) : undefined;
@@ -157,7 +147,7 @@ export function Playground({
proxyUrl: ctx.proxyUrl,
};
return <ctx.renderer.APIPlayground {...props} />;
return <APIPlayground {...props} />;
}
function getAuthorizationField(
+8 -6
View File
@@ -1,7 +1,6 @@
import type { ComponentType, ReactNode } from 'react';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
import type { APIPlaygroundProps } from '@/render/playground';
import { CodeBlock } from '@/render/codeblock';
import {
API,
@@ -10,9 +9,9 @@ import {
APIExample,
Property,
ObjectCollapsible,
APIPlayground,
} from '@/ui';
import type { RenderContext, ServerObject } from '@/types';
import type { MethodInformation, RenderContext, ServerObject } from '@/types';
import { Playground } from '@/render/playground';
export interface ResponsesProps {
items: string[];
@@ -81,7 +80,11 @@ export interface Renderer {
*/
ObjectCollapsible: ComponentType<ObjectCollapsibleProps>;
Property: ComponentType<PropertyProps>;
APIPlayground: ComponentType<APIPlaygroundProps>;
APIPlayground: ComponentType<{
path: string;
method: MethodInformation;
ctx: RenderContext;
}>;
}
export type {
@@ -93,7 +96,6 @@ export type {
export function createRenders(
shikiOptions: RenderContext['shikiOptions'],
_useScalar: boolean,
): Renderer {
return {
Root: (props) => (
@@ -139,6 +141,6 @@ export function createRenders(
/>
</Tab>
),
APIPlayground,
APIPlayground: Playground,
};
}
+18
View File
@@ -0,0 +1,18 @@
import type { MethodInformation, RenderContext } from '@/types';
import dynamic from 'next/dynamic';
const Client = dynamic(() => import('./client'));
export function APIPlayground({
path,
method,
ctx,
}: {
path: string;
method: MethodInformation;
ctx: RenderContext;
}) {
return (
<Client method={method.method} path={path} spec={ctx.schema.downloaded} />
);
}
+1 -3
View File
@@ -18,7 +18,6 @@ type ApiPageContextProps = Pick<
| 'generateCodeSamples'
| 'proxyUrl'
| 'showResponseSchema'
| 'useScalar'
>;
export interface ApiPageProps extends ApiPageContextProps {
@@ -124,12 +123,11 @@ export async function getContext(
const server = servers[0];
return {
useScalar: options.useScalar ?? false,
schema,
proxyUrl: options.proxyUrl,
showResponseSchema: options.showResponseSchema,
renderer: {
...createRenders(options.shikiOptions, options.useScalar ?? false),
...createRenders(options.shikiOptions),
...options.renderer,
},
shikiOptions: options.shikiOptions,
-5
View File
@@ -34,11 +34,6 @@ type Awaitable<T> = T | Promise<T>;
export type DereferenceMap = Map<unknown, string>;
export interface RenderContext {
/**
* Use Scalar for API Playground
*/
useScalar: boolean;
/**
* The url of proxy to avoid CORS issues
*/
+3 -48
View File
@@ -1,23 +1,15 @@
'use client';
import {
type ButtonHTMLAttributes,
type HTMLAttributes,
type ReactNode,
} from 'react';
import { Check, Copy } from 'lucide-react';
import { cn, useCopyButton, buttonVariants } from 'fumadocs-ui/components/api';
import { type HTMLAttributes, type ReactNode } from 'react';
import { cn } from 'fumadocs-ui/components/api';
import dynamic from 'next/dynamic';
import { ApiProvider, useApiContext } from '@/ui/contexts/api';
import { ApiProvider } from '@/ui/contexts/api';
import { type RootProps } from '@/render/renderer';
import type { RenderContext } from '@/types';
import { getUrl } from '@/utils/server-url';
export const APIPlayground = dynamic(() =>
import('./playground').then((mod) => mod.APIPlayground),
);
export const ScalarPlayground = dynamic(() => import('./scalar/index'));
export function Root({
children,
baseUrl,
@@ -47,41 +39,4 @@ export function Root({
);
}
export function CopyRouteButton({
className,
route,
...props
}: ButtonHTMLAttributes<HTMLButtonElement> & {
route: string;
}): ReactNode {
const { serverRef } = useApiContext();
const [checked, onCopy] = useCopyButton(() => {
void navigator.clipboard.writeText(
`${serverRef.current ? getUrl(serverRef.current.url, serverRef.current.variables) : ''}${route}`,
);
});
return (
<button
type="button"
className={cn(
buttonVariants({
color: 'ghost',
className,
}),
)}
onClick={onCopy}
aria-label="Copy route path"
{...props}
>
{checked ? (
<Check className="size-full" />
) : (
<Copy className="size-full" />
)}
</button>
);
}
export { useSchemaContext } from './contexts/schema';
+1 -6
View File
@@ -9,12 +9,7 @@ import {
} from 'fumadocs-ui/components/ui/collapsible';
import { ChevronDown } from 'lucide-react';
export {
Root,
useSchemaContext,
APIPlayground,
ScalarPlayground,
} from './client';
export { Root, useSchemaContext, APIPlayground } from './client';
export function APIInfo({
className,
+1 -1
View File
@@ -39,7 +39,7 @@ export const roundedTable = {
borderSpacing: '0',
background: 'var(--color-fd-card)',
borderRadius: 'var(--radius-lg)',
border: '1p solid var(--color-fd-border)',
border: '1px solid var(--color-fd-border)',
overflow: 'hidden',
},
th: {
-3
View File
@@ -77,9 +77,6 @@ importers:
'@radix-ui/react-tooltip':
specifier: ^1.1.7
version: 1.1.7(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected]([email protected]))([email protected])
'@scalar/api-client-react':
specifier: ^1.1.17
version: 1.1.17(@hyperjump/[email protected])([email protected])([email protected])([email protected])
'@shikijs/rehype':
specifier: ^2.1.0
version: 2.1.0