Skip to content

Commit 4032231

Browse files
committed
🎉 feat: 0.7.1
1 parent 57cbb4f commit 4032231

File tree

9 files changed

+159
-94
lines changed

9 files changed

+159
-94
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
# 0.7.1 - 23 Sep 2023
2+
Improvement:
3+
- [#10](https://github.com/elysiajs/elysia-static/issues/10) onError is not triggered when page is not found and file resolution is dynamic
4+
- [#7](https://github.com/elysiajs/elysia-static/issues/8) add `headers` config
5+
- using new plugin model instead of functional callback
6+
7+
Change:
8+
- Minimum support for Elysia is now set to 0.7.9
9+
10+
Bug fix:
11+
- [#8](https://github.com/elysiajs/elysia-static/issues/8) set `noUncheckedIndexedAccess` to `true`
12+
13+
# 0.7.0 - 20 Sep 2023
14+
- Add support for Elysia 0.7
15+
16+
# 0.7.0-beta.0 - 18 Sep 2023
17+
- Add support for Elysia 0.7
18+
19+
# 0.6.0 - 6 Aug 2023
20+
- Add support for Elysia 0.6
21+
22+
# 0.6.0-rc.0 - 6 Aug 2023
23+
- Add support for Elysia 0.6
124
# 0.5.0 - 15 May 2023
225
- Add support for Elysia 0.5
326
- Add CommonJS support

bun.lockb

71.3 KB
Binary file not shown.

example/a.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { Elysia } from 'elysia'
22
import { staticPlugin } from '../src'
33

4+
const a = async () => {
5+
return new Elysia()
6+
}
7+
48
const app = new Elysia()
9+
.use(a())
510
.use(
611
staticPlugin({
712
ignorePatterns: [/takodachi.png$/]
@@ -13,12 +18,4 @@ const app = new Elysia()
1318
ignorePatterns: [/takodachi.png$/]
1419
})
1520
)
16-
.listen(8080)
17-
18-
// const file1 = await app.handle(req('/public/takodachi.png'))
19-
// const file2 = await app.handle(req('/public/tako.png'))
20-
// const blob1 = await file1.blob()
21-
// const blob2 = await file2.blob()
22-
23-
// expect(await blob1.text()).toBe('NOT_FOUND')
24-
// expect(await blob2.text()).toEqual(takodachi)
21+
.listen(300)

example/bench.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

example/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import { Elysia } from 'elysia'
22

33
import { staticPlugin } from '../src/index'
44

5+
const a = async () => {
6+
return new Elysia()
7+
}
8+
59
const app = new Elysia()
610
.use(
711
staticPlugin({
@@ -14,9 +18,8 @@ const app = new Elysia()
1418
prefix: '/public'
1519
})
1620
)
17-
.listen(8080)
21+
.listen(3000)
1822

1923
await app.modules
2024

21-
// @ts-ignore
22-
console.log(app.router.history)
25+
console.log(app.routes)

package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@elysiajs/static",
3-
"version": "0.5.3",
3+
"version": "0.7.1",
44
"author": {
55
"name": "saltyAom",
66
"url": "https://github.com/SaltyAom",
@@ -13,15 +13,14 @@
1313
"main": "./dist/index.js",
1414
"devDependencies": {
1515
"@types/node": "^20.1.4",
16-
"bun-types": "^0.5.8",
17-
"elysia": "0.5.12",
16+
"bun-types": "^1.0.2",
17+
"elysia": "^0.7.9",
1818
"eslint": "^8.40.0",
19-
"mitata": "^0.1.6",
20-
"rimraf": "4.3",
21-
"typescript": "^5.0.4"
19+
"rimraf": "4.4.1",
20+
"typescript": "^5.2.2"
2221
},
2322
"peerDependencies": {
24-
"elysia": ">= 0.5.12"
23+
"elysia": ">= 0.7.9"
2524
},
2625
"exports": {
2726
"bun": "./dist/index.js",
@@ -41,7 +40,7 @@
4140
"license": "MIT",
4241
"scripts": {
4342
"dev": "bun run --hot example/index.ts",
44-
"test": "bun wiptest && npm run test:node",
43+
"test": "bun test && npm run test:node",
4544
"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",
4645
"build": "rimraf dist && tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json",
4746
"release": "npm run build && npm run test && npm publish --access public"

src/index.ts

Lines changed: 70 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { Elysia } from 'elysia'
1+
import { NotFoundError, Elysia } from 'elysia'
22

33
import { readdir, stat } from 'fs/promises'
44
import { resolve, resolve as resolveFn, join } from 'path'
55

6-
const getFiles = async (dir: string): Promise<string[]> => {
6+
const listFiles = async (dir: string): Promise<string[]> => {
77
const files = await readdir(dir)
88

99
const all = await Promise.all(
@@ -12,7 +12,7 @@ const getFiles = async (dir: string): Promise<string[]> => {
1212
const stats = await stat(file)
1313

1414
return stats && stats.isDirectory()
15-
? await getFiles(file)
15+
? await listFiles(file)
1616
: [resolve(dir, file)]
1717
})
1818
)
@@ -28,7 +28,8 @@ export const staticPlugin = async <Prefix extends string = '/prefix'>(
2828
alwaysStatic = false,
2929
ignorePatterns = ['.DS_Store', '.git', '.env'],
3030
noExtension = false,
31-
resolve = resolveFn
31+
resolve = resolveFn,
32+
headers = {}
3233
}: {
3334
/**
3435
* @default "public"
@@ -74,17 +75,22 @@ export const staticPlugin = async <Prefix extends string = '/prefix'>(
7475
* Nodejs resolve function
7576
*/
7677
resolve?: (...pathSegments: string[]) => string
78+
/**
79+
* Set headers
80+
*/
81+
headers?: Record<string, string> | undefined
7782
} = {
7883
assets: 'public',
7984
prefix: '/public' as Prefix,
8085
staticLimit: 1024,
8186
alwaysStatic: process.env.NODE_ENV === 'production',
8287
ignorePatterns: [],
8388
noExtension: false,
84-
resolve: resolveFn
89+
resolve: resolveFn,
90+
headers: {}
8591
}
8692
) => {
87-
const files = await getFiles(resolveFn(assets))
93+
const files = await listFiles(resolveFn(assets))
8894

8995
if (prefix === '/') prefix = '' as Prefix
9096

@@ -97,58 +103,71 @@ export const staticPlugin = async <Prefix extends string = '/prefix'>(
97103
})
98104
}
99105

100-
return (app: Elysia) => {
101-
if (
102-
alwaysStatic ||
103-
(process.env.ENV === 'production' && files.length <= staticLimit)
104-
)
105-
for (let i = 0; i < files.length; i++) {
106-
const file = files[i]
107-
if (shouldIgnore(file)) continue
106+
const app = new Elysia({
107+
name: 'static',
108+
seed: {
109+
assets,
110+
prefix,
111+
staticLimit,
112+
alwaysStatic,
113+
ignorePatterns,
114+
noExtension,
115+
resolve: resolve.toString()
116+
}
117+
})
118+
119+
if (
120+
alwaysStatic ||
121+
(process.env.ENV === 'production' && files.length <= staticLimit)
122+
)
123+
for (let i = 0; i < files.length; i++) {
124+
const file = files[i]
125+
if (!file || shouldIgnore(file)) continue
108126

109-
const response = () => new Response(Bun.file(file))
110-
let fileName = file
111-
.replace(resolve(), '')
112-
.replace(`${assets}/`, '')
127+
const response = Object.keys(headers).length
128+
? () =>
129+
new Response(Bun.file(file), {
130+
headers
131+
})
132+
: () => new Response(Bun.file(file))
113133

114-
if (noExtension) {
115-
const temp = fileName.split('.')
116-
temp.splice(-1)
134+
let fileName = file.replace(resolve(), '').replace(`${assets}/`, '')
117135

118-
fileName = temp.join('.')
119-
}
136+
if (noExtension) {
137+
const temp = fileName.split('.')
138+
temp.splice(-1)
120139

121-
app.get(join(prefix, fileName), response)
140+
fileName = temp.join('.')
122141
}
123-
else {
124-
if (
125-
// @ts-ignore
126-
!app.routes.find(
127-
({ method, path }) =>
128-
path === `${prefix}/*` && method === 'GET'
129-
)
130-
)
131-
app.get(`${prefix}/*`, (c) => {
132-
const file = `${assets}/${(c.params as any)['*']}`
133-
134-
if (shouldIgnore(file))
135-
return new Response('NOT_FOUND', {
136-
status: 404
137-
})
138-
139-
return stat(file)
140-
.then((status) => new Response(Bun.file(file)))
141-
.catch(
142-
(error) =>
143-
new Response('NOT_FOUND', {
144-
status: 404
145-
})
146-
)
147-
})
148-
}
149142

150-
return app
143+
app.get(join(prefix, fileName), response)
144+
}
145+
else {
146+
if (
147+
// @ts-ignore
148+
!app.routes.find(
149+
({ method, path }) => path === `${prefix}/*` && method === 'GET'
150+
)
151+
)
152+
app.onError(() => {}).get(`${prefix}/*`, async ({ params }) => {
153+
const file = `${assets}/${(params as any)['*']}`
154+
155+
if (shouldIgnore(file)) throw new NotFoundError()
156+
157+
return stat(file)
158+
.then(
159+
(status) =>
160+
new Response(Bun.file(file), {
161+
headers
162+
})
163+
)
164+
.catch((error) => {
165+
throw new NotFoundError()
166+
})
167+
})
151168
}
169+
170+
return app
152171
}
153172

154173
export default staticPlugin

test/index.test.ts

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('Static Plugin', () => {
9595
})
9696

9797
it('ignore string pattern', async () => {
98-
const app = new Elysia().use(
98+
const app = new Elysia({ forceErrorEncapsulation: true }).use(
9999
staticPlugin({
100100
ignorePatterns: ['public/takodachi.png']
101101
})
@@ -104,17 +104,15 @@ describe('Static Plugin', () => {
104104
await app.modules
105105

106106
const res = await app.handle(req('/public/takodachi.png'))
107-
const blob = await res.blob()
108-
expect(await blob.text()).toBe('NOT_FOUND')
107+
expect(res.status).toBe(404)
109108
})
110109

111110
it('ignore regex pattern', async () => {
112-
const app = new Elysia()
113-
.use(
114-
staticPlugin({
115-
ignorePatterns: [/takodachi.png$/]
116-
})
117-
)
111+
const app = new Elysia().use(
112+
staticPlugin({
113+
ignorePatterns: [/takodachi.png$/]
114+
})
115+
)
118116

119117
const file = await app.handle(req('/public/takodachi.png'))
120118

@@ -155,4 +153,43 @@ describe('Static Plugin', () => {
155153

156154
expect(res).toBe(takodachi)
157155
})
156+
157+
it('return custom headers', async () => {
158+
const app = new Elysia().use(
159+
staticPlugin({
160+
alwaysStatic: true,
161+
noExtension: true,
162+
headers: {
163+
['x-powered-by']: 'Takodachi'
164+
}
165+
})
166+
)
167+
168+
await app.modules
169+
170+
const res = await app.handle(req('/public/takodachi'))
171+
172+
expect(res.headers.get('x-powered-by')).toBe('Takodachi')
173+
expect(res.status).toBe(200)
174+
})
175+
176+
it('call onError when using dynamic mode', async () => {
177+
let called = false
178+
179+
const app = new Elysia()
180+
.onError(({ code }) => {
181+
if (code === 'NOT_FOUND') called = true
182+
})
183+
.use(
184+
staticPlugin({
185+
alwaysStatic: false
186+
})
187+
)
188+
189+
await app.modules
190+
191+
const res = await app.handle(req('/public/not-found'))
192+
193+
expect(called).toBe(true)
194+
})
158195
})

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
9191
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
9292
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
93-
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
93+
"noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
9494
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
9595
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
9696
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */

0 commit comments

Comments
 (0)