1- import type { ReadStream } from 'fs'
2- import { createReadStream , existsSync , lstatSync } from 'fs'
1+ import type { ReadStream , Stats } from 'fs'
2+ import { createReadStream , lstatSync } from 'fs'
33import type { Context , MiddlewareHandler } from 'hono'
4- import { getFilePath } from 'hono/utils/filepath'
4+ import { getFilePath , getFilePathWithoutDefaultDocument } from 'hono/utils/filepath'
55import { getMimeType } from 'hono/utils/mime'
66
77export type ServeStaticOptions = {
@@ -33,6 +33,18 @@ const createStreamBody = (stream: ReadStream) => {
3333 return body
3434}
3535
36+ const addCurrentDirPrefix = ( path : string ) => {
37+ return `./${ path } `
38+ }
39+
40+ const getStats = ( path : string ) => {
41+ let stats : Stats | undefined
42+ try {
43+ stats = lstatSync ( path )
44+ } catch { }
45+ return stats
46+ }
47+
3648export const serveStatic = ( options : ServeStaticOptions = { root : '' } ) : MiddlewareHandler => {
3749 return async ( c , next ) => {
3850 // Do nothing if Response is already set
@@ -41,19 +53,37 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew
4153 }
4254
4355 const filename = options . path ?? decodeURIComponent ( c . req . path )
44- let path = getFilePath ( {
56+
57+ let path = getFilePathWithoutDefaultDocument ( {
4558 filename : options . rewriteRequestPath ? options . rewriteRequestPath ( filename ) : filename ,
4659 root : options . root ,
47- defaultDocument : options . index ?? 'index.html' ,
4860 } )
4961
50- if ( ! path ) {
62+ if ( path ) {
63+ path = addCurrentDirPrefix ( path )
64+ } else {
5165 return next ( )
5266 }
5367
54- path = `./${ path } `
68+ let stats = getStats ( path )
69+
70+ if ( stats && stats . isDirectory ( ) ) {
71+ path = getFilePath ( {
72+ filename : options . rewriteRequestPath ? options . rewriteRequestPath ( filename ) : filename ,
73+ root : options . root ,
74+ defaultDocument : options . index ?? 'index.html' ,
75+ } )
76+
77+ if ( path ) {
78+ path = addCurrentDirPrefix ( path )
79+ } else {
80+ return next ( )
81+ }
82+
83+ stats = getStats ( path )
84+ }
5585
56- if ( ! existsSync ( path ) ) {
86+ if ( ! stats ) {
5787 await options . onNotFound ?.( path , c )
5888 return next ( )
5989 }
@@ -63,8 +93,7 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew
6393 c . header ( 'Content-Type' , mimeType )
6494 }
6595
66- const stat = lstatSync ( path )
67- const size = stat . size
96+ const size = stats . size
6897
6998 if ( c . req . method == 'HEAD' || c . req . method == 'OPTIONS' ) {
7099 c . header ( 'Content-Length' , size . toString ( ) )
@@ -80,11 +109,11 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew
80109 }
81110
82111 c . header ( 'Accept-Ranges' , 'bytes' )
83- c . header ( 'Date' , stat . birthtime . toUTCString ( ) )
112+ c . header ( 'Date' , stats . birthtime . toUTCString ( ) )
84113
85114 const parts = range . replace ( / b y t e s = / , '' ) . split ( '-' , 2 )
86115 const start = parts [ 0 ] ? parseInt ( parts [ 0 ] , 10 ) : 0
87- let end = parts [ 1 ] ? parseInt ( parts [ 1 ] , 10 ) : stat . size - 1
116+ let end = parts [ 1 ] ? parseInt ( parts [ 1 ] , 10 ) : stats . size - 1
88117 if ( size < end - start + 1 ) {
89118 end = size - 1
90119 }
@@ -93,7 +122,7 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew
93122 const stream = createReadStream ( path , { start, end } )
94123
95124 c . header ( 'Content-Length' , chunksize . toString ( ) )
96- c . header ( 'Content-Range' , `bytes ${ start } -${ end } /${ stat . size } ` )
125+ c . header ( 'Content-Range' , `bytes ${ start } -${ end } /${ stats . size } ` )
97126
98127 return c . body ( createStreamBody ( stream ) , 206 )
99128 }
0 commit comments