Skip to content

Commit 2e27893

Browse files
committed
🔧 fix: 1.3.0-exp.1
1 parent 65b20fc commit 2e27893

File tree

8 files changed

+826
-263
lines changed

8 files changed

+826
-263
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# 1.3.0-exp.1 - 1 May 2025
2+
Improvement:
3+
- use static response for documentation page
4+
- plugin is no longer async
5+
- model should be synced globally
6+
- use `parse` instead of `type` to determine content type
7+
8+
# 1.3.0-exp.0 - 23 Apr 2025
9+
Change:
10+
- Add support for Elysia 1.3
11+
112
# 1.2.2 - 22 Feb 2024
213
Bug fix:
314
- [#185](https://github.com/elysiajs/elysia-swagger/pull/185) Fix path issue in Scalar config

bun.lock

Lines changed: 512 additions & 0 deletions
Large diffs are not rendered by default.

bun.lockb

-86.7 KB
Binary file not shown.

example/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const schema = t.Object({
55
test: t.Literal('hello')
66
})
77

8-
const app = new Elysia({ precompile: true })
8+
const app = new Elysia()
99
.use(
1010
swagger({
1111
provider: 'scalar',
@@ -45,10 +45,15 @@ const app = new Elysia({ precompile: true })
4545
.get(
4646
'/',
4747
() => {
48-
return { test: 'hello' }
48+
return { test: 'hello' as const }
4949
},
5050
{
5151
response: 'schema'
5252
}
5353
)
54+
.post('/json', ({ body }) => body, {
55+
parse: 'formdata',
56+
body: 'schema',
57+
response: 'schema'
58+
})
5459
.listen(3000)

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@elysiajs/swagger",
3-
"version": "1.2.2",
3+
"version": "1.3.0-exp.1",
44
"description": "Plugin for Elysia to auto-generate Swagger page",
55
"author": {
66
"name": "saltyAom",
@@ -57,12 +57,12 @@
5757
"release": "npm run build && npm run test && npm publish --access public"
5858
},
5959
"peerDependencies": {
60-
"elysia": ">= 1.2.0"
60+
"elysia": ">= 1.3.0-exp.71"
6161
},
6262
"devDependencies": {
6363
"@apidevtools/swagger-parser": "^10.1.0",
6464
"@types/bun": "1.1.14",
65-
"elysia": ">= 1.2.0",
65+
"elysia": "1.3.0-exp.71",
6666
"eslint": "9.6.0",
6767
"tsup": "^8.1.0",
6868
"typescript": "^5.5.3"

src/index.ts

Lines changed: 136 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -15,38 +15,23 @@ import type { ElysiaSwaggerConfig } from './types'
1515
*
1616
* @see https://github.com/elysiajs/elysia-swagger
1717
*/
18-
export const swagger = async <Path extends string = '/swagger'>(
19-
{
20-
provider = 'scalar',
21-
scalarVersion = 'latest',
22-
scalarCDN = '',
23-
scalarConfig = {},
24-
documentation = {},
25-
version = '5.9.0',
26-
excludeStaticFile = true,
27-
path = '/swagger' as Path,
28-
exclude = [],
29-
swaggerOptions = {},
30-
theme = `https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`,
31-
autoDarkMode = true,
32-
excludeMethods = ['OPTIONS'],
33-
excludeTags = []
34-
}: ElysiaSwaggerConfig<Path> = {
35-
provider: 'scalar',
36-
scalarVersion: 'latest',
37-
scalarCDN: '',
38-
scalarConfig: {},
39-
documentation: {},
40-
version: '5.9.0',
41-
excludeStaticFile: true,
42-
path: '/swagger' as Path,
43-
exclude: [],
44-
swaggerOptions: {},
45-
autoDarkMode: true,
46-
excludeMethods: ['OPTIONS'],
47-
excludeTags: []
48-
}
49-
) => {
18+
export const swagger = <Path extends string = '/swagger'>({
19+
provider = 'scalar',
20+
scalarVersion = 'latest',
21+
scalarCDN = '',
22+
scalarConfig = {},
23+
documentation = {},
24+
version = '5.9.0',
25+
excludeStaticFile = true,
26+
path = '/swagger' as Path,
27+
specPath = `${path}/json`,
28+
exclude = [],
29+
swaggerOptions = {},
30+
theme = `https://unpkg.com/swagger-ui-dist@${version}/swagger-ui.css`,
31+
autoDarkMode = true,
32+
excludeMethods = ['OPTIONS'],
33+
excludeTags = []
34+
}: ElysiaSwaggerConfig<Path> = {}) => {
5035
const schema = {}
5136
let totalRoutes = 0
5237

@@ -62,126 +47,142 @@ export const swagger = async <Path extends string = '/swagger'>(
6247

6348
const relativePath = path.startsWith('/') ? path.slice(1) : path
6449

65-
const openAPISpecUrl = relativePath === '' ? `/json` : `/${relativePath}/json`
66-
6750
const app = new Elysia({ name: '@elysiajs/swagger' })
6851

69-
app.get(path, function documentation({request}) {
70-
const combinedSwaggerOptions = {
71-
url: openAPISpecUrl,
72-
dom_id: '#swagger-ui',
73-
...swaggerOptions
74-
}
75-
76-
const stringifiedSwaggerOptions = JSON.stringify(
77-
combinedSwaggerOptions,
78-
(key, value) => {
79-
if (typeof value == 'function') return undefined
80-
81-
return value
52+
const page = new Response(
53+
provider === 'swagger-ui'
54+
? SwaggerUIRender(
55+
info,
56+
version,
57+
theme,
58+
JSON.stringify(
59+
{
60+
url: specPath,
61+
dom_id: '#swagger-ui',
62+
...swaggerOptions
63+
},
64+
(_, value) =>
65+
typeof value === 'function' ? undefined : value
66+
),
67+
autoDarkMode
68+
)
69+
: ScalarRender(
70+
info,
71+
scalarVersion,
72+
{
73+
spec: {
74+
...scalarConfig.spec,
75+
url: specPath
76+
},
77+
...scalarConfig,
78+
// so we can showcase the elysia theme
79+
// @ts-expect-error
80+
_integration: 'elysiajs'
81+
} satisfies ReferenceConfiguration,
82+
scalarCDN
83+
),
84+
{
85+
headers: {
86+
'content-type': 'text/html; charset=utf8'
8287
}
83-
)
84-
85-
const scalarConfiguration: ReferenceConfiguration = {
86-
spec: {
87-
...scalarConfig.spec,
88-
url: `${new URL(request.url).pathname.replace(/\/$/, "")}/json`
89-
},
90-
...scalarConfig,
91-
// so we can showcase the elysia theme
92-
// @ts-expect-error
93-
_integration: 'elysiajs'
9488
}
89+
)
9590

96-
return new Response(
97-
provider === 'swagger-ui'
98-
? SwaggerUIRender(
99-
info,
100-
version,
101-
theme,
102-
stringifiedSwaggerOptions,
103-
autoDarkMode
91+
app.get(path, page, {
92+
detail: {
93+
hide: true
94+
}
95+
}).get(
96+
specPath,
97+
function openAPISchema() {
98+
// @ts-expect-error Private property
99+
const routes = app.getGlobalRoutes() as InternalRoute[]
100+
101+
if (routes.length !== totalRoutes) {
102+
const ALLOWED_METHODS = [
103+
'GET',
104+
'PUT',
105+
'POST',
106+
'DELETE',
107+
'OPTIONS',
108+
'HEAD',
109+
'PATCH',
110+
'TRACE'
111+
]
112+
totalRoutes = routes.length
113+
114+
// forEach create a clone of a route (can't use for-of)
115+
routes.forEach((route: InternalRoute) => {
116+
if (route.hooks?.detail?.hide === true) return
117+
if (excludeMethods.includes(route.method)) return
118+
if (
119+
ALLOWED_METHODS.includes(route.method) === false &&
120+
route.method !== 'ALL'
104121
)
105-
: ScalarRender(info, scalarVersion, scalarConfiguration, scalarCDN),
106-
{
107-
headers: {
108-
'content-type': 'text/html; charset=utf8'
109-
}
110-
}
111-
)
112-
}).get(path === '/' ? '/json' : `${path}/json`, function openAPISchema() {
113-
// @ts-expect-error Private property
114-
const routes = app.getGlobalRoutes() as InternalRoute[]
115-
116-
if (routes.length !== totalRoutes) {
117-
const ALLOWED_METHODS = ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH', 'TRACE']
118-
totalRoutes = routes.length
119-
120-
// forEach create a clone of a route (can't use for-of)
121-
routes.forEach((route: InternalRoute) => {
122-
if (route.hooks?.detail?.hide === true) return
123-
// TODO: route.hooks?.detail?.hide !== false add ability to hide: false to prevent excluding
124-
if (excludeMethods.includes(route.method)) return
125-
if (ALLOWED_METHODS.includes(route.method) === false && route.method !== 'ALL') return
126-
127-
if (route.method === 'ALL') {
128-
ALLOWED_METHODS.forEach((method) => {
122+
return
123+
124+
if (route.method === 'ALL')
125+
ALLOWED_METHODS.forEach((method) => {
126+
registerSchemaPath({
127+
schema,
128+
hook: route.hooks,
129+
method,
130+
path: route.path,
131+
// @ts-ignore
132+
models: app.getGlobalDefinitions?.().type,
133+
contentType: route.hooks.type
134+
})
135+
})
136+
else
129137
registerSchemaPath({
130138
schema,
131139
hook: route.hooks,
132-
method,
140+
method: route.method,
133141
path: route.path,
134142
// @ts-ignore
135-
models: app.definitions?.type,
143+
models: app.getGlobalDefinitions?.().type,
136144
contentType: route.hooks.type
137145
})
138-
})
139-
return
140-
}
141-
142-
registerSchemaPath({
143-
schema,
144-
hook: route.hooks,
145-
method: route.method,
146-
path: route.path,
147-
// @ts-ignore
148-
models: app.definitions?.type,
149-
contentType: route.hooks.type
150146
})
151-
})
152-
}
147+
}
153148

154-
return {
155-
openapi: '3.0.3',
156-
...{
157-
...documentation,
158-
tags: documentation.tags?.filter(
159-
(tag) => !excludeTags?.includes(tag?.name)
160-
),
161-
info: {
162-
title: 'Elysia Documentation',
163-
description: 'Development documentation',
164-
version: '0.0.0',
165-
...documentation.info
166-
}
167-
},
168-
paths: {
169-
...filterPaths(schema, relativePath, {
170-
excludeStaticFile,
171-
exclude: Array.isArray(exclude) ? exclude : [exclude]
172-
}),
173-
...documentation.paths
174-
},
175-
components: {
176-
...documentation.components,
177-
schemas: {
178-
// @ts-ignore
179-
...app.definitions?.type,
180-
...documentation.components?.schemas
149+
return {
150+
openapi: '3.0.3',
151+
...{
152+
...documentation,
153+
tags: documentation.tags?.filter(
154+
(tag) => !excludeTags?.includes(tag?.name)
155+
),
156+
info: {
157+
title: 'Elysia Documentation',
158+
description: 'Development documentation',
159+
version: '0.0.0',
160+
...documentation.info
161+
}
162+
},
163+
paths: {
164+
...filterPaths(schema, {
165+
excludeStaticFile,
166+
exclude: Array.isArray(exclude) ? exclude : [exclude]
167+
}),
168+
...documentation.paths
169+
},
170+
components: {
171+
...documentation.components,
172+
schemas: {
173+
// @ts-ignore
174+
...app.getGlobalDefinitions?.().type,
175+
...documentation.components?.schemas
176+
}
181177
}
178+
} satisfies OpenAPIV3.Document
179+
},
180+
{
181+
detail: {
182+
hide: true
182183
}
183-
} satisfies OpenAPIV3.Document
184-
})
184+
}
185+
)
185186

186187
return app
187188
}

0 commit comments

Comments
 (0)