mirror of
https://github.com/zoriya/elysia-swagger.git
synced 2026-06-06 23:22:13 +00:00
wip: scalar elysia integration
This commit is contained in:
+7
-51
@@ -5,6 +5,8 @@ import { filterPaths, registerSchemaPath } from './utils'
|
||||
|
||||
import type { OpenAPIV3 } from 'openapi-types'
|
||||
import type { ElysiaSwaggerConfig } from './types'
|
||||
import { SwaggerUIRender } from './swagger-ui'
|
||||
import { ScalarRender } from './scalar'
|
||||
|
||||
/**
|
||||
* Plugin for [elysia](https://github.com/elysiajs/elysia) that auto-generate Swagger page.
|
||||
@@ -14,6 +16,8 @@ import type { ElysiaSwaggerConfig } from './types'
|
||||
export const swagger =
|
||||
<Path extends string = '/swagger'>(
|
||||
{
|
||||
provider = 'scalar',
|
||||
scalarVersion = '1.12.4',
|
||||
documentation = {},
|
||||
version = '5.9.0',
|
||||
excludeStaticFile = true,
|
||||
@@ -23,6 +27,8 @@ export const swagger =
|
||||
theme = `https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`,
|
||||
autoDarkMode = true
|
||||
}: ElysiaSwaggerConfig<Path> = {
|
||||
provider: 'scalar',
|
||||
scalarVersion: '1.12.4',
|
||||
documentation: {},
|
||||
version: '5.9.0',
|
||||
excludeStaticFile: true,
|
||||
@@ -65,58 +71,8 @@ export const swagger =
|
||||
}
|
||||
)
|
||||
|
||||
return new Response(
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>${info.title}</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="${info.description}"
|
||||
/>
|
||||
<meta
|
||||
name="og:description"
|
||||
content="${info.description}"
|
||||
/>
|
||||
${
|
||||
autoDarkMode && typeof theme === 'string'
|
||||
? `
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #222;
|
||||
color: #faf9a;
|
||||
}
|
||||
.swagger-ui {
|
||||
filter: invert(92%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
.swagger-ui .microlight {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
</style>`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
typeof theme === 'string'
|
||||
? `<link rel="stylesheet" href="${theme}" />`
|
||||
: `<link rel="stylesheet" media="(prefers-color-scheme: light)" href="${theme.light}" />
|
||||
<link rel="stylesheet" media="(prefers-color-scheme: dark)" href="${theme.dark}" />`
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@${version}/swagger-ui-bundle.js" crossorigin></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
window.ui = SwaggerUIBundle(${stringifiedSwaggerOptions});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>`,
|
||||
return new Response(provider === 'swagger-ui' ? SwaggerUIRender(info, version, theme, stringifiedSwaggerOptions, autoDarkMode) : ScalarRender(`${relativePath}/json`, scalarVersion),
|
||||
{
|
||||
headers: {
|
||||
'content-type': 'text/html; charset=utf8'
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
export default `:root {
|
||||
--theme-font: "Inter", var(--system-fonts);
|
||||
}
|
||||
/* basic theme */
|
||||
.light-mode {
|
||||
--theme-color-1: #2a2f45;
|
||||
--theme-color-2: #757575;
|
||||
--theme-color-3: #8e8e8e;
|
||||
--theme-color-accent: #f06292;
|
||||
|
||||
--theme-background-1: #fff;
|
||||
--theme-background-2: #f6f6f6;
|
||||
--theme-background-3: #e7e7e7;
|
||||
--theme-background-accent: #f062921f;
|
||||
|
||||
--theme-border-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.dark-mode {
|
||||
--theme-color-1: rgba(255, 255, 255, 0.9);
|
||||
--theme-color-2: rgba(156, 163, 175, 1);
|
||||
--theme-color-3: rgba(255, 255, 255, 0.44);
|
||||
--theme-color-accent: #f06292;
|
||||
|
||||
--theme-background-1: #111728;
|
||||
--theme-background-2: #1e293b;
|
||||
--theme-background-3: #334155;
|
||||
--theme-background-accent: #f062921f;
|
||||
|
||||
--theme-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
/* Document Sidebar */
|
||||
.light-mode .sidebar,
|
||||
.dark-mode .sidebar {
|
||||
--sidebar-background-1: var(--theme-background-1);
|
||||
--sidebar-item-hover-color: currentColor;
|
||||
--sidebar-item-hover-background: var(--theme-background-2);
|
||||
--sidebar-item-active-background: var(--theme-background-accent);
|
||||
--sidebar-border-color: transparent;
|
||||
--sidebar-color-1: var(--theme-color-1);
|
||||
--sidebar-color-2: var(--theme-color-2);
|
||||
--sidebar-color-active: var(--theme-color-accent);
|
||||
--sidebar-search-background: transparent;
|
||||
--sidebar-search-border-color: var(--theme-border-color);
|
||||
--sidebar-search--color: var(--theme-color-3);
|
||||
}
|
||||
|
||||
/* advanced */
|
||||
.light-mode {
|
||||
--theme-button-1: rgb(49 53 56);
|
||||
--theme-button-1-color: #fff;
|
||||
--theme-button-1-hover: rgb(28 31 33);
|
||||
|
||||
--theme-color-green: #069061;
|
||||
--theme-color-red: #ef0006;
|
||||
--theme-color-yellow: #edbe20;
|
||||
--theme-color-blue: #0082d0;
|
||||
--theme-color-orange: #fb892c;
|
||||
--theme-color-purple: #5203d1;
|
||||
|
||||
--theme-scrollbar-color: rgba(0, 0, 0, 0.18);
|
||||
--theme-scrollbar-color-active: rgba(0, 0, 0, 0.36);
|
||||
}
|
||||
.dark-mode {
|
||||
--theme-button-1: #f6f6f6;
|
||||
--theme-button-1-color: #000;
|
||||
--theme-button-1-hover: #e7e7e7;
|
||||
|
||||
--theme-color-green: #a3ffa9;
|
||||
--theme-color-red: #ffa3a3;
|
||||
--theme-color-yellow: #fffca3;
|
||||
--theme-color-blue: #a5d6ff;
|
||||
--theme-color-orange: #e2ae83;
|
||||
--theme-color-purple: #d2a8ff;
|
||||
|
||||
--theme-scrollbar-color: rgba(255, 255, 255, 0.24);
|
||||
--theme-scrollbar-color-active: rgba(255, 255, 255, 0.48);
|
||||
}
|
||||
.section-container:first-of-type:after,
|
||||
.section-container:first-of-type:before {
|
||||
--stripes: repeating-linear-gradient(
|
||||
100deg,
|
||||
#fff 0%,
|
||||
#fff 0%,
|
||||
transparent 2%,
|
||||
transparent 12%,
|
||||
#fff 17%
|
||||
);
|
||||
--stripesDark: repeating-linear-gradient(
|
||||
100deg,
|
||||
#000 0%,
|
||||
#000 0%,
|
||||
transparent 10%,
|
||||
transparent 12%,
|
||||
#000 17%
|
||||
);
|
||||
--rainbow: repeating-linear-gradient(
|
||||
100deg,
|
||||
#60a5fa 10%,
|
||||
#e879f9 16%,
|
||||
#5eead4 22%,
|
||||
#60a5fa 30%
|
||||
);
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 200px;
|
||||
background-image: var(--stripesDark), var(--rainbow);
|
||||
background-size: 200%, 100%;
|
||||
background-attachment: fixed;
|
||||
mix-blend-mode: difference;
|
||||
filter: opacity(10%) saturate(200%);
|
||||
-webkit-mask-image: radial-gradient(
|
||||
ellipse at 100% 0%,
|
||||
black 40%,
|
||||
transparent 75%
|
||||
);
|
||||
mask-image: radial-gradient(ellipse at 100% 0%, black 40%, transparent 75%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.section-container:first-of-type:after {
|
||||
mix-blend-mode: unset;
|
||||
}
|
||||
.light-mode .section-container:first-of-type:after,
|
||||
.light-mode .section-container:first-of-type:before {
|
||||
background-image: var(--stripes), var(--rainbow);
|
||||
filter: opacity(4%) saturate(200%);
|
||||
mix-blend-mode: unset;
|
||||
}
|
||||
.scalar-api-client__send-request-button,
|
||||
.show-api-client-button {
|
||||
background: #3c82f6 !important;
|
||||
}
|
||||
.show-api-client-button:before {
|
||||
display: none;
|
||||
}
|
||||
.section-container .section:first-of-type {
|
||||
border-bottom: 1px solid
|
||||
var(--theme-border-color, var(--default-theme-border-color));
|
||||
}
|
||||
.section-container[data-v-fcff76dc]:not(:first-of-type) {
|
||||
border-top: none;
|
||||
}
|
||||
.sidebar-search:hover {
|
||||
transition: all 0.15s ease-in-out;
|
||||
--sidebar-search-border-color: var(--theme-color-accent) !important;
|
||||
color: var(--sidebar-color-1) !important;
|
||||
}
|
||||
.scalar-api-client__container .sidebar {
|
||||
--sidebar-border-color: var(--theme-border-color);
|
||||
}
|
||||
`
|
||||
@@ -0,0 +1,26 @@
|
||||
import scalarElysiaTheme from './scalar-elysia-theme'
|
||||
|
||||
export const ScalarRender = (specUrl: string, version: string, customCss?: string) => `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>API Reference</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
${customCss ?? scalarElysiaTheme}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script
|
||||
id="api-reference"
|
||||
data-url="${specUrl}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference@${version}/dist/browser/standalone.min.js"></script>
|
||||
</body>
|
||||
</html>`
|
||||
@@ -0,0 +1,56 @@
|
||||
import { SwaggerInfo } from './types'
|
||||
|
||||
export const SwaggerUIRender = (info: SwaggerInfo, version: string, theme: string | {
|
||||
light: string
|
||||
dark: string
|
||||
}, stringifiedSwaggerOptions: string, autoDarkMode?: boolean) => `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>${info.title}</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="${info.description}"
|
||||
/>
|
||||
<meta
|
||||
name="og:description"
|
||||
content="${info.description}"
|
||||
/>
|
||||
${
|
||||
autoDarkMode && typeof theme === 'string'
|
||||
? `
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #222;
|
||||
color: #faf9a;
|
||||
}
|
||||
.swagger-ui {
|
||||
filter: invert(92%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
.swagger-ui .microlight {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
</style>`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
typeof theme === 'string'
|
||||
? `<link rel="stylesheet" href="${theme}" />`
|
||||
: `<link rel="stylesheet" media="(prefers-color-scheme: light)" href="${theme.light}" />
|
||||
<link rel="stylesheet" media="(prefers-color-scheme: dark)" href="${theme.dark}" />`
|
||||
}
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@${version}/swagger-ui-bundle.js" crossorigin></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
window.ui = SwaggerUIBundle(${stringifiedSwaggerOptions});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
@@ -1,6 +1,13 @@
|
||||
import type { OpenAPIV3 } from 'openapi-types'
|
||||
import type { SwaggerUIOptions } from 'swagger-ui'
|
||||
|
||||
|
||||
export type SwaggerInfo = {
|
||||
title: string
|
||||
description: string
|
||||
version: string
|
||||
}
|
||||
|
||||
export interface ElysiaSwaggerConfig<Path extends string = '/swagger'> {
|
||||
/**
|
||||
* Customize Swagger config, refers to Swagger 2.0 config
|
||||
@@ -12,6 +19,21 @@ export interface ElysiaSwaggerConfig<Path extends string = '/swagger'> {
|
||||
| 'x-express-openapi-additional-middleware'
|
||||
| 'x-express-openapi-validation-strict'
|
||||
>
|
||||
/**
|
||||
* Choose your provider, Scalar or Swagger UI
|
||||
*
|
||||
* @default 'scalar'
|
||||
* @see https://github.com/scalar/scalar
|
||||
* @see https://github.com/swagger-api/swagger-ui
|
||||
*/
|
||||
scalarVersion?: string
|
||||
/**
|
||||
* Version to use for Scalar cdn bundle
|
||||
*
|
||||
* @default '1.12.4'
|
||||
* @see https://github.com/scalar/scalar
|
||||
*/
|
||||
provider?: 'scalar' | 'swagger-ui'
|
||||
/**
|
||||
* Version to use for swagger cdn bundle
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user