Skip to content

feat: add rapidoc provider #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions example/index-rapidoc.ts
Original file line number Diff line number Diff line change
@@ -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)

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
58 changes: 46 additions & 12 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -21,6 +22,7 @@ export const swagger = async <Path extends string = '/swagger'>(
scalarVersion = 'latest',
scalarCDN = '',
scalarConfig = {},
rapidocConfig = {},
documentation = {},
version = '5.9.0',
excludeStaticFile = true,
Expand All @@ -36,6 +38,7 @@ export const swagger = async <Path extends string = '/swagger'>(
scalarVersion: 'latest',
scalarCDN: '',
scalarConfig: {},
rapidocConfig: {},
documentation: {},
version: '5.9.0',
excludeStaticFile: true,
Expand Down Expand Up @@ -93,18 +96,49 @@ export const swagger = async <Path extends string = '/swagger'>(
_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'
}
}
Expand Down
84 changes: 84 additions & 0 deletions src/rapidoc/index.ts
Original file line number Diff line number Diff line change
@@ -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'
) => `<!doctype html>
<html>
<head>
<title>${info.title}</title>
<meta
name="description"
content="${info.description}"
/>
<meta
name="og:description"
content="${info.description}"
/>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1" />
<style>
body {
margin: 0;
}
</style>
<script type="module" src="${cdn}" crossorigin></script>
</head>
<body>
<rapi-doc ${renderConfig(config)}></rapi-doc>
</body>
</html>
`
10 changes: 9 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -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<Path extends string = '/swagger'> {
/**
Expand All @@ -19,8 +20,9 @@ export interface ElysiaSwaggerConfig<Path extends string = '/swagger'> {
* @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
*
Expand All @@ -47,6 +49,12 @@ export interface ElysiaSwaggerConfig<Path extends string = '/swagger'> {
* @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
*
Expand Down