-
Notifications
You must be signed in to change notification settings - Fork 191
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(zod-openapi): create swagger-ui jsx component * feat(zod-openapi): add swagger ui docs * chore(zod-openapi): add versioning doc * chore(zod-openapi): fix SwaggerUI component doc * refactor(zod-openapi): remove jsx comment * feat(swagger-ui): create new package * feat(swagger-ui): provides swagger ui middleware and swaggerui component * feat(zod-openapi): Changed to use @hono/swagger-ui * chore: add versioning doc * feat: update ci for @hono/swagger-ui * refactor: remove package-lock.json * refactor: reverted the extra changes. * chore: remove old changeset doc * refactor(swagger-ui): remove unused file * refactor(swagger-ui): change input type * feat(swagger-ui): Select only the options you need. * chore(swagger-ui): update README * refactor(swagger-ui): rewrite simple * refactor(zod-openapi): remove swagger-ui * chore(swagger-ui): fix readme content * chore(dep): add @types/swagger-ui-dist for option types support * feat: implement SwaggerConfig Renderer for mapping config object to html * feat: extend some option support for swagger-ui-dist * test: move option rendering test and add some cases * feat: add manually option for making full customizable * docs: update swagger-ui middleware README * fix: do not escape HTML strings * docs: update README of swagger-ui middleware * ci: update workflow environment for swagger-ui middleware --------- Co-authored-by: naporin0624 <[email protected]>
- Loading branch information
1 parent
296446b
commit c608fa9
Showing
15 changed files
with
833 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@hono/swagger-ui': minor | ||
--- | ||
|
||
Create a package that provides swagger ui in hono |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: ci-swagger-ui | ||
on: | ||
push: | ||
branches: [main] | ||
paths: | ||
- 'packages/swagger-ui/**' | ||
pull_request: | ||
branches: ['*'] | ||
paths: | ||
- 'packages/swagger-ui/**' | ||
|
||
jobs: | ||
ci: | ||
runs-on: ubuntu-latest | ||
defaults: | ||
run: | ||
working-directory: ./packages/swagger-ui | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 20.x | ||
- run: yarn install --frozen-lockfile | ||
- run: yarn build | ||
- run: yarn test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
# Swagger UI Middleware and Component for Hono | ||
|
||
This library, `@hono/swagger-ui`, provides a middleware and a component for integrating Swagger UI with Hono applications. Swagger UI is an interactive documentation interface for APIs compliant with the OpenAPI Specification, making it easier to understand and test API endpoints. | ||
|
||
## Installation | ||
|
||
```bash | ||
npm install @hono/swagger-ui | ||
# or | ||
yarn add @hono/swagger-ui | ||
``` | ||
|
||
## Usage | ||
|
||
### Middleware Usage | ||
|
||
You can use the `swaggerUI` middleware to serve Swagger UI on a specific route in your Hono application. Here's how you can do it: | ||
|
||
```ts | ||
import { Hono } from 'hono' | ||
import { swaggerUI } from '@hono/swagger-ui' | ||
|
||
const app = new Hono() | ||
|
||
// Use the middleware to serve Swagger UI at /ui | ||
app.get('/ui', swaggerUI({ url: '/doc' })) | ||
|
||
export default app | ||
``` | ||
|
||
### Component Usage | ||
|
||
If you are using `hono/html`, you can use the `SwaggerUI` component to render Swagger UI within your custom HTML structure. Here's an example: | ||
|
||
```ts | ||
import { Hono } from 'hono' | ||
import { html } from 'hono/html' | ||
import { SwaggerUI } from '@hono/swagger-ui' | ||
|
||
const app = new Hono() | ||
|
||
app.get('/ui', (c) => { | ||
return c.html(html` | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<meta name="description" content="Custom Swagger" /> | ||
<title>Custom Swagger</title> | ||
<script> | ||
// custom script | ||
</script> | ||
<style> | ||
/* custom style */ | ||
</style> | ||
</head> | ||
${SwaggerUI({ url: '/doc' })} | ||
</html> | ||
`) | ||
export default app | ||
``` | ||
In this example, the `SwaggerUI` component is used to render Swagger UI within a custom HTML structure, allowing for additional customization such as adding custom scripts and styles. | ||
### With `OpenAPIHono` Usage | ||
Hono's middleware has OpenAPI integration `@hono/zod-openapi`, so you can use it to create an OpenAPI document and serve it easily with Swagger UI. | ||
```ts | ||
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi' | ||
import { swaggerUI } from '@hono/swagger-ui' | ||
|
||
const app = new OpenAPIHono() | ||
|
||
app.openapi( | ||
createRoute({ | ||
method: 'get', | ||
path: '/hello', | ||
responses: { | ||
200: { | ||
description: 'Respond a message', | ||
content: { | ||
'application/json': { | ||
schema: z.object({ | ||
message: z.string() | ||
}) | ||
} | ||
} | ||
} | ||
} | ||
}), | ||
(c) => { | ||
return c.jsonT({ | ||
message: 'hello' | ||
}) | ||
} | ||
) | ||
|
||
app.get( | ||
'/ui', | ||
swaggerUI({ | ||
url: '/doc' | ||
}) | ||
) | ||
|
||
app.doc('/doc', { | ||
info: { | ||
title: 'An API', | ||
version: 'v1' | ||
}, | ||
openapi: '3.1.0' | ||
}) | ||
|
||
export default app | ||
``` | ||
## Options | ||
Both the middleware and the component accept an options object for customization. | ||
The following options are available: | ||
- `version` (string, optional): The version of Swagger UI to use, defaults to `latest`. | ||
- `manuallySwaggerUIHtml` (string, optional): If you want to use your own custom HTML, you can specify it here. If this option is specified, the all options except `version` will be ignored. | ||
and most of options from [Swagger UI]( | ||
https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/ | ||
) are supported as well. | ||
such as: | ||
- `url` (string, optional): The URL pointing to the OpenAPI definition (v2 or v3) that describes the API. | ||
- `urls` (array, optional): An array of OpenAPI definitions (v2 or v3) that describe the APIs. Each definition must have a `name` and `url`. | ||
- `presets` (array, optional): An array of presets to use for Swagger UI. | ||
- `plugins` (array, optional): An array of plugins to use for Swagger UI. | ||
## Authors | ||
- naporitan <https://github.com/naporin0624> | ||
- sor4chi <https://github.com/sor4chi> | ||
## License | ||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
{ | ||
"name": "@hono/swagger-ui", | ||
"version": "0.0.0", | ||
"description": "A middleware for using SwaggerUI in Hono", | ||
"type": "module", | ||
"main": "dist/index.cjs", | ||
"module": "dist/index.js", | ||
"types": "dist/index.d.cts", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
}, | ||
"require": { | ||
"types": "./dist/index.d.cts", | ||
"default": "./dist/index.cjs" | ||
} | ||
} | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"scripts": { | ||
"test": "vitest run", | ||
"build": "tsup ./src/index.ts --format esm,cjs --dts", | ||
"publint": "publint", | ||
"release": "yarn build && yarn test && yarn publint && yarn publish" | ||
}, | ||
"license": "MIT", | ||
"private": false, | ||
"publishConfig": { | ||
"registry": "https://registry.npmjs.org", | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/honojs/middleware.git" | ||
}, | ||
"homepage": "https://github.com/honojs/middleware", | ||
"peerDependencies": { | ||
"hono": "*" | ||
}, | ||
"devDependencies": { | ||
"@types/swagger-ui-dist": "^3.30.3", | ||
"hono": "^3.7.2", | ||
"publint": "^0.2.2", | ||
"tsup": "^7.2.0", | ||
"vite": "^4.4.9", | ||
"vitest": "^0.34.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import type { Env, MiddlewareHandler } from 'hono' | ||
import { html } from 'hono/html' | ||
import type { DistSwaggerUIOptions } from './swagger/renderer' | ||
import { renderSwaggerUIOptions } from './swagger/renderer' | ||
import type { AssetURLs } from './swagger/resource' | ||
import { remoteAssets } from './swagger/resource' | ||
|
||
type OriginalSwaggerUIOptions = { | ||
version?: string | ||
/** | ||
* manuallySwaggerUIHtml is a string that is used to render SwaggerUI. | ||
* If this is set, all other options will be ignored except version. | ||
* The string will be inserted into the body of the HTML. | ||
* This is useful when you want to fully customize the UI. | ||
* | ||
* @example | ||
* ```ts | ||
* const swaggerUI = SwaggerUI({ | ||
* manuallySwaggerUIHtml: (asset) => ` | ||
* <div> | ||
* <div id="swagger-ui"></div> | ||
* ${asset.css.map((url) => `<link rel="stylesheet" href="${url}" />`)} | ||
* ${asset.js.map((url) => `<script src="${url}" crossorigin="anonymous"></script>`)} | ||
* <script> | ||
* window.onload = () => { | ||
* window.ui = SwaggerUIBundle({ | ||
* dom_id: '#swagger-ui', | ||
* url: 'https://petstore.swagger.io/v2/swagger.json', | ||
* }) | ||
* } | ||
* </script> | ||
* </div> | ||
* `, | ||
* }) | ||
* ``` | ||
*/ | ||
manuallySwaggerUIHtml?: (asset: AssetURLs) => string | ||
} | ||
|
||
type SwaggerUIOptions = OriginalSwaggerUIOptions & DistSwaggerUIOptions | ||
|
||
const SwaggerUI = (options: SwaggerUIOptions) => { | ||
const asset = remoteAssets({ version: options?.version }) | ||
delete options.version | ||
|
||
if (options.manuallySwaggerUIHtml) { | ||
return options.manuallySwaggerUIHtml(asset) | ||
} | ||
|
||
const optionsStrings = renderSwaggerUIOptions(options) | ||
|
||
return ` | ||
<div> | ||
<div id="swagger-ui"></div> | ||
${asset.css.map((url) => html`<link rel="stylesheet" href="${url}" />`)} | ||
${asset.js.map((url) => html`<script src="${url}" crossorigin="anonymous"></script>`)} | ||
<script> | ||
window.onload = () => { | ||
window.ui = SwaggerUIBundle({ | ||
dom_id: '#swagger-ui',${optionsStrings}, | ||
}) | ||
} | ||
</script> | ||
</div> | ||
` | ||
} | ||
|
||
const middleware = | ||
<E extends Env>(options: SwaggerUIOptions): MiddlewareHandler<E> => | ||
async (c) => { | ||
return c.html(/* html */ ` | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<meta name="description" content="SwaggerUI" /> | ||
<title>SwaggerUI</title> | ||
</head> | ||
<body> | ||
${SwaggerUI(options)} | ||
</body> | ||
</html> | ||
`) | ||
} | ||
|
||
export { middleware as swaggerUI, SwaggerUI } | ||
export { SwaggerUIOptions } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import type { SwaggerConfigs } from 'swagger-ui-dist' | ||
|
||
export type DistSwaggerUIOptions = { | ||
configUrl?: SwaggerConfigs['configUrl'] | ||
deepLinking?: SwaggerConfigs['deepLinking'] | ||
presets?: string[] | ||
plugins?: string[] | ||
spec?: SwaggerConfigs['spec'] | ||
url?: SwaggerConfigs['url'] | ||
urls?: SwaggerConfigs['urls'] | ||
layout?: SwaggerConfigs['layout'] | ||
docExpansion?: SwaggerConfigs['docExpansion'] | ||
maxDisplayedTags?: SwaggerConfigs['maxDisplayedTags'] | ||
operationsSorter?: string | ||
requestInterceptor?: string | ||
responseInterceptor?: string | ||
} | ||
|
||
const RENDER_TYPE = { | ||
STRING_ARRAY: 'string_array', | ||
STRING: 'string', | ||
JSON_STRING: 'json_string', | ||
RAW: 'raw', | ||
} as const | ||
|
||
const RENDER_TYPE_MAP = { | ||
configUrl: RENDER_TYPE.STRING, | ||
deepLinking: RENDER_TYPE.RAW, | ||
presets: RENDER_TYPE.STRING_ARRAY, | ||
plugins: RENDER_TYPE.STRING_ARRAY, | ||
spec: RENDER_TYPE.JSON_STRING, | ||
url: RENDER_TYPE.STRING, | ||
urls: RENDER_TYPE.JSON_STRING, | ||
layout: RENDER_TYPE.STRING, | ||
docExpansion: RENDER_TYPE.STRING, | ||
maxDisplayedTags: RENDER_TYPE.RAW, | ||
operationsSorter: RENDER_TYPE.RAW, | ||
requestInterceptor: RENDER_TYPE.RAW, | ||
responseInterceptor: RENDER_TYPE.RAW, | ||
} as const satisfies Record< | ||
keyof DistSwaggerUIOptions, | ||
(typeof RENDER_TYPE)[keyof typeof RENDER_TYPE] | ||
> | ||
|
||
export const renderSwaggerUIOptions = (options: DistSwaggerUIOptions) => { | ||
const optionsStrings = Object.entries(options) | ||
.map(([k, v]) => { | ||
const key = k as keyof typeof RENDER_TYPE_MAP | ||
if (RENDER_TYPE_MAP[key] === RENDER_TYPE.STRING) { | ||
return `${key}: '${v}'` | ||
} | ||
if (RENDER_TYPE_MAP[key] === RENDER_TYPE.STRING_ARRAY) { | ||
if (!Array.isArray(v)) return '' | ||
return `${key}: [${v.map((ve) => `${ve}`).join(',')}]` | ||
} | ||
if (RENDER_TYPE_MAP[key] === RENDER_TYPE.JSON_STRING) { | ||
return `${key}: ${JSON.stringify(v)}` | ||
} | ||
if (RENDER_TYPE_MAP[key] === RENDER_TYPE.RAW) { | ||
return `${key}: ${v}` | ||
} | ||
return '' | ||
}) | ||
.join(',') | ||
|
||
return optionsStrings | ||
} |
Oops, something went wrong.