From ba0626495740bea5036307aaee788c0344390171 Mon Sep 17 00:00:00 2001 From: "Ivan.Baranov" Date: Tue, 15 Apr 2025 12:44:14 +0300 Subject: [PATCH] feat: add rapidoc provider --- example/index-rapidoc.ts | 55 ++++++++++++++++++++++++++ package.json | 1 + src/index.ts | 58 +++++++++++++++++++++------ src/rapidoc/index.ts | 84 ++++++++++++++++++++++++++++++++++++++++ src/types.ts | 10 ++++- 5 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 example/index-rapidoc.ts create mode 100644 src/rapidoc/index.ts diff --git a/example/index-rapidoc.ts b/example/index-rapidoc.ts new file mode 100644 index 0000000..e6ec96f --- /dev/null +++ b/example/index-rapidoc.ts @@ -0,0 +1,55 @@ +import { Elysia, t } from 'elysia' +import { swagger } from '../src/index' + +new Elysia({precompile: true}) + .use( + swagger({ + provider: 'rapidoc', + documentation: { + info: { + title: 'Elysia Rapidoc', + version: '1.0.0' + }, + tags: [ + { + name: 'Test', + description: 'Hello' + } + ], + components: { + schemas: { + User: { + description: 'string' + } + }, + securitySchemes: { + JwtAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + description: 'Enter JWT Bearer token **_only_**' + } + } + } + }, + rapidocConfig: { + + } + }) + ) + .get('/ping', async () => { + return { pong: 'ping' } + }, + { + response: { + 200: t.Object({ + pong: t.String({ default: 'ping' }) + }) + } + } + ) + .onStart(() => { + console.info(`🔥 http://localhost:3200/swagger`) + }) + .listen(3200) + diff --git a/package.json b/package.json index 8c5307d..2c294cf 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "license": "MIT", "scripts": { "dev": "bun run --watch example/index.ts", + "dev-rapidoc": "bun run --watch example/index-rapidoc.ts", "test": "bun test && npm run test:node", "test:node": "npm install --prefix ./test/node/cjs/ && npm install --prefix ./test/node/esm/ && node ./test/node/cjs/index.js && node ./test/node/esm/index.js", "build": "bun build.ts", diff --git a/src/index.ts b/src/index.ts index 562bea2..2120ef2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { filterPaths, registerSchemaPath } from './utils' import type { OpenAPIV3 } from 'openapi-types' import type { ReferenceConfiguration } from '@scalar/types' import type { ElysiaSwaggerConfig } from './types' +import { RapidocRender } from "./rapidoc"; /** * Plugin for [elysia](https://github.com/elysiajs/elysia) that auto-generate Swagger page. @@ -21,6 +22,7 @@ export const swagger = async ( scalarVersion = 'latest', scalarCDN = '', scalarConfig = {}, + rapidocConfig = {}, documentation = {}, version = '5.9.0', excludeStaticFile = true, @@ -36,6 +38,7 @@ export const swagger = async ( scalarVersion: 'latest', scalarCDN: '', scalarConfig: {}, + rapidocConfig: {}, documentation: {}, version: '5.9.0', excludeStaticFile: true, @@ -93,18 +96,49 @@ export const swagger = async ( _integration: 'elysiajs' } - return new Response( - provider === 'swagger-ui' - ? SwaggerUIRender( - info, - version, - theme, - stringifiedSwaggerOptions, - autoDarkMode - ) - : ScalarRender(info, scalarVersion, scalarConfiguration, scalarCDN), - { - headers: { + let html: string + switch (provider) { + case 'scalar': + html = ScalarRender(info, scalarVersion, scalarConfiguration, scalarCDN) + break + case 'swagger-ui': + html = SwaggerUIRender( + info, + version, + theme, + stringifiedSwaggerOptions, + autoDarkMode + ) + break + case 'rapidoc': + html = RapidocRender(info, { + specUrl: openAPISpecUrl, + + allowAdvancedSearch: true, + allowSpecFileDownload: true, + defaultSchemaTab: 'schema', + fillRequestFieldsWithExample: false, + headingText: info.title, + layout: 'column', + loadFonts: true, + persistAuth: true, + renderStyle: 'focused', + schemaDescriptionExpanded: true, + schemaExpandLevel: 10, + schemaStyle: 'table', + showHeader: false, + showMethodInNavBar: 'as-colored-block', + theme: 'light', + usePathInNavBar: true, + + ...rapidocConfig + }) + break + + } + + return new Response(html, { + headers: { 'content-type': 'text/html; charset=utf8' } } diff --git a/src/rapidoc/index.ts b/src/rapidoc/index.ts new file mode 100644 index 0000000..2680613 --- /dev/null +++ b/src/rapidoc/index.ts @@ -0,0 +1,84 @@ +import type { OpenAPIV3 } from "openapi-types"; + +/** + * Rapidoc Config + * @see https://rapidocweb.com/api.html + */ +export type RapidocConfig = Partial<{ + specUrl: string + // + allowAdvancedSearch: boolean + allowSpecFileDownload: boolean + allowTry: boolean + defaultSchemaTab: 'schema' + fillRequestFieldsWithExample: boolean + gotoPath: string + headingText: string + infoDescriptionHeadingsInNavbar: boolean + layout: 'row' | 'column' + loadFonts: boolean + monoFont: string + navItemSpacing: 'default' | 'compact' | 'relaxed' + onNavTagClick: 'expand-collapse' | 'show-description' + persistAuth: boolean + renderStyle: 'read' | 'view' | 'focused' + responseAreaHeight: string + schemaDescriptionExpanded: boolean + schemaExpandLevel: number + schemaStyle: 'table' + showComponents: boolean + showCurlBeforeTry: boolean + showHeader: boolean + showInfo: boolean + showMethodInNavBar: 'as-colored-block' + sortEndpointsBy: 'path' | 'method' | 'summary' | 'none' + sortSchemas: boolean + sortTags: boolean + theme: 'light' | 'dark' + usePathInNavBar: boolean +}> + +function kebab(str: string): string { + return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? "-" : "") + $.toLowerCase()) +} + +function renderConfig(config: RapidocConfig): string { + const a: string[] = [] + Object.keys(config).forEach((key) => { + a.push(`${kebab(key)}="${String(config[key as keyof RapidocConfig])}"`) + }) + return a.join(' ') +} + +export const RapidocRender = ( + info: OpenAPIV3.InfoObject, + config: RapidocConfig, + cdn = 'https://unpkg.com/rapidoc/dist/rapidoc-min.js' +) => ` + + + ${info.title} + + + + + + + + + + + +` diff --git a/src/types.ts b/src/types.ts index 5370e38..8ed38a7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,7 @@ import type { OpenAPIV3 } from 'openapi-types' import type { ReferenceConfiguration } from '@scalar/types' import type { SwaggerUIOptions } from './swagger/types' +import { RapidocConfig } from "./rapidoc"; export interface ElysiaSwaggerConfig { /** @@ -19,8 +20,9 @@ export interface ElysiaSwaggerConfig { * @default 'scalar' * @see https://github.com/scalar/scalar * @see https://github.com/swagger-api/swagger-ui + * @see https://github.com/rapi-doc/RapiDoc */ - provider?: 'scalar' | 'swagger-ui' + provider?: 'scalar' | 'swagger-ui' | 'rapidoc' /** * Version to use for Scalar cdn bundle * @@ -47,6 +49,12 @@ export interface ElysiaSwaggerConfig { * @see https://github.com/scalar/scalar/blob/main/documentation/configuration.md */ scalarConfig?: ReferenceConfiguration + /** + * Rapidoc configuration + *' + * @see https://rapidocweb.com/api.html + */ + rapidocConfig?: RapidocConfig /** * Version to use for swagger cdn bundle *