Skip to content

Commit

Permalink
feat(serve-static): mimes option for serve-static (#2094)
Browse files Browse the repository at this point in the history
* feat(serve-static): `mime` option for serve-static

* undo getExtension()

* add mime and serve-static tests

* reaname mimes to baseMime

* rename mime to mimes and add getMimeType() callback

Co-Authored-By: Yusuke Wada <yusuke@kamawada.com>

* update test description

---------

Co-authored-by: Yusuke Wada <yusuke@kamawada.com>
  • Loading branch information
ryuapp and yusukebe authored Jan 29, 2024
1 parent b24f9e3 commit d74d95d
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 8 deletions.
1 change: 1 addition & 0 deletions deno_dist/adapter/deno/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { open } = Deno
export type ServeStaticOptions<E extends Env = Env> = {
root?: string
path?: string
mimes?: Record<string, string>
rewriteRequestPath?: (path: string) => string
onNotFound?: (path: string, c: Context<E>) => void | Promise<void>
}
Expand Down
6 changes: 3 additions & 3 deletions deno_dist/utils/mime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const getMimeType = (filename: string): string | undefined => {
export const getMimeType = (filename: string, mimes = baseMimes): string | undefined => {
const regexp = /\.([a-zA-Z0-9]+?)$/
const match = filename.match(regexp)
if (!match) {
Expand All @@ -12,10 +12,10 @@ export const getMimeType = (filename: string): string | undefined => {
}

export const getExtension = (mimeType: string): string | undefined => {
return Object.keys(mimes).find((ext) => mimes[ext] === mimeType)
return Object.keys(baseMimes).find((ext) => baseMimes[ext] === mimeType)
}

const mimes: Record<string, string> = {
const baseMimes: Record<string, string> = {
aac: 'audio/aac',
abw: 'application/x-abiword',
arc: 'application/x-freearc',
Expand Down
8 changes: 7 additions & 1 deletion src/adapter/bun/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { file } = Bun
export type ServeStaticOptions<E extends Env = Env> = {
root?: string
path?: string
mimes?: Record<string, string>
rewriteRequestPath?: (path: string) => string
onNotFound?: (path: string, c: Context<E>) => void | Promise<void>
}
Expand Down Expand Up @@ -45,7 +46,12 @@ export const serveStatic = <E extends Env = Env>(
if (existsSync(path)) {
const content = file(path)
if (content) {
const mimeType = getMimeType(path)
let mimeType: string | undefined = undefined
if (options.mimes) {
mimeType = getMimeType(path, options.mimes) ?? getMimeType(path)
} else {
mimeType = getMimeType(path)
}
if (mimeType) {
c.header('Content-Type', mimeType)
}
Expand Down
34 changes: 34 additions & 0 deletions src/adapter/cloudflare-workers/serve-static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const store: Record<string, string> = {
'static-no-root/plain.abcdef.txt': 'That is plain.txt',
'assets/static/options/foo.abcdef.txt': 'With options',
'assets/.static/plain.abcdef.txt': 'In the dot',
'assets/static/video/morning-routine.abcdef.m3u8': 'Good morning',
'assets/static/video/morning-routine1.abcdef.ts': 'Good',
'assets/static/video/introduction.abcdef.mp4': 'Let me introduce myself',
}
const manifest = JSON.stringify({
'assets/static/plain.txt': 'assets/static/plain.abcdef.txt',
Expand Down Expand Up @@ -123,6 +126,37 @@ describe('With `file` options', () => {
})
})

describe('With `mimes` options', () => {
const mimes = {
m3u8: 'application/vnd.apple.mpegurl',
ts: 'video/mp2t',
}
const manifest = {
'assets/static/video/morning-routine.m3u8': 'assets/static/video/morning-routine.abcdef.m3u8',
'assets/static/video/morning-routine1.ts': 'assets/static/video/morning-routine1.abcdef.ts',
'assets/static/video/introduction.mp4': 'assets/static/video/introduction.abcdef.mp4',
}

const app = new Hono()
app.use('/static/*', serveStatic({ root: './assets', mimes, manifest }))

it('Should return content-type of m3u8', async () => {
const res = await app.request('http://localhost/static/video/morning-routine.m3u8')
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('application/vnd.apple.mpegurl')
})
it('Should return content-type of ts', async () => {
const res = await app.request('http://localhost/static/video/morning-routine1.ts')
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('video/mp2t')
})
it('Should return content-type of default on Hono', async () => {
const res = await app.request('http://localhost/static/video/introduction.mp4')
expect(res.status).toBe(200)
expect(res.headers.get('Content-Type')).toBe('video/mp4')
})
})

describe('With middleware', () => {
const app = new Hono()
const md1 = async (c: Context, next: Next) => {
Expand Down
8 changes: 7 additions & 1 deletion src/adapter/cloudflare-workers/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getContentFromKVAsset } from './utils'
export type ServeStaticOptions<E extends Env = Env> = {
root?: string
path?: string
mimes?: Record<string, string>
manifest: object | string
namespace?: KVNamespace
rewriteRequestPath?: (path: string) => string
Expand Down Expand Up @@ -48,7 +49,12 @@ export const serveStatic = <E extends Env = Env>(
})

if (content) {
const mimeType = getMimeType(path)
let mimeType: string | undefined = undefined
if (options.mimes) {
mimeType = getMimeType(path, options.mimes) ?? getMimeType(path)
} else {
mimeType = getMimeType(path)
}
if (mimeType) {
c.header('Content-Type', mimeType)
}
Expand Down
1 change: 1 addition & 0 deletions src/adapter/deno/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { open } = Deno
export type ServeStaticOptions<E extends Env = Env> = {
root?: string
path?: string
mimes?: Record<string, string>
rewriteRequestPath?: (path: string) => string
onNotFound?: (path: string, c: Context<E>) => void | Promise<void>
}
Expand Down
11 changes: 11 additions & 0 deletions src/utils/mime.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { getMimeType, getExtension } from './mime'

const mime = {
m3u8: 'application/vnd.apple.mpegurl',
ts: 'video/mp2t',
}

describe('mime', () => {
it('getMimeType', () => {
expect(getMimeType('hello.txt')).toBe('text/plain; charset=utf-8')
Expand All @@ -11,6 +16,12 @@ describe('mime', () => {
expect(getMimeType('indexjs.abcd')).toBeUndefined()
})

it('getMimeType with custom mime', () => {
expect(getMimeType('morning-routine.m3u8', mime)).toBe('application/vnd.apple.mpegurl')
expect(getMimeType('morning-routine1.ts', mime)).toBe('video/mp2t')
expect(getMimeType('readme.txt', mime)).toBeUndefined()
})

it('getExtension', () => {
expect(getExtension('audio/aac')).toBe('aac')
expect(getExtension('application/x-abiword')).toBe('abw')
Expand Down
6 changes: 3 additions & 3 deletions src/utils/mime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const getMimeType = (filename: string): string | undefined => {
export const getMimeType = (filename: string, mimes = baseMimes): string | undefined => {
const regexp = /\.([a-zA-Z0-9]+?)$/
const match = filename.match(regexp)
if (!match) {
Expand All @@ -12,10 +12,10 @@ export const getMimeType = (filename: string): string | undefined => {
}

export const getExtension = (mimeType: string): string | undefined => {
return Object.keys(mimes).find((ext) => mimes[ext] === mimeType)
return Object.keys(baseMimes).find((ext) => baseMimes[ext] === mimeType)
}

const mimes: Record<string, string> = {
const baseMimes: Record<string, string> = {
aac: 'audio/aac',
abw: 'application/x-abiword',
arc: 'application/x-freearc',
Expand Down

0 comments on commit d74d95d

Please sign in to comment.