Skip to content

Commit

Permalink
feat(serve-static): support absolute root
Browse files Browse the repository at this point in the history
  • Loading branch information
yusukebe committed Sep 17, 2024
1 parent dfbd717 commit 9e761e2
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/adapter/bun/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export const serveStatic = <E extends Env = Env>(
): MiddlewareHandler => {
return async function serveStatic(c, next) {
const getContent = async (path: string) => {
path = `./${path}`
path = path.startsWith('/') ? path : `./${path}`
// @ts-ignore
const file = Bun.file(path)
return (await file.exists()) ? file : null
}
const pathResolve = (path: string) => {
return `./${path}`
return path.startsWith('/') ? path : `./${path}`
}
const isDir = async (path: string) => {
let isDir
Expand Down
5 changes: 5 additions & 0 deletions src/middleware/serve-static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getMimeType } from '../../utils/mime'
export type ServeStaticOptions<E extends Env = Env> = {
root?: string
path?: string
allowAbsoluteRoot?: boolean
precompressed?: boolean
mimes?: Record<string, string>
rewriteRequestPath?: (path: string) => string
Expand Down Expand Up @@ -50,11 +51,14 @@ export const serveStatic = <E extends Env = Env>(
filename = options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename
const root = options.root

const allowAbsoluteRoot = options.allowAbsoluteRoot ?? false

// If it was Directory, force `/` on the end.
if (!filename.endsWith('/') && options.isDir) {
const path = getFilePathWithoutDefaultDocument({
filename,
root,
allowAbsoluteRoot,
})
if (path && (await options.isDir(path))) {
filename += '/'
Expand All @@ -64,6 +68,7 @@ export const serveStatic = <E extends Env = Env>(
let path = getFilePath({
filename,
root,
allowAbsoluteRoot,
defaultDocument: DEFAULT_DOCUMENT,
})

Expand Down
23 changes: 23 additions & 0 deletions src/utils/filepath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ describe('getFilePath', () => {
expect(getFilePath({ filename: 'filename.suffix_index' })).toBe('filename.suffix_index')
expect(getFilePath({ filename: 'filename.suffix-index' })).toBe('filename.suffix-index')
})

it('Should return file path correctly with allowAbsoluteRoot', async () => {
const allowAbsoluteRoot = true
expect(getFilePath({ filename: 'foo.txt', allowAbsoluteRoot })).toBe('/foo.txt')
expect(getFilePath({ filename: 'foo.txt', root: '/p', allowAbsoluteRoot })).toBe('/p/foo.txt')
expect(getFilePath({ filename: 'foo', root: '/p', allowAbsoluteRoot })).toBe(
'/p/foo/index.html'
)
expect(getFilePath({ filename: 'foo.txt', root: '/p/../p2', allowAbsoluteRoot })).toBe(
'/p2/foo.txt'
)
expect(getFilePath({ filename: 'foo', root: '/p/bar', allowAbsoluteRoot })).toBe(
'/p/bar/foo/index.html'
)

expect(
getFilePath({ filename: 'foo.txt', root: slashToBackslash('/p'), allowAbsoluteRoot })
).toBe('/p/foo.txt')

expect(
getFilePath({ filename: 'foo.txt', root: slashToBackslash('/p/../p2'), allowAbsoluteRoot })
).toBe('/p2/foo.txt')
})
})

function slashToBackslash(filename: string) {
Expand Down
16 changes: 15 additions & 1 deletion src/utils/filepath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type FilePathOptions = {
filename: string
root?: string
defaultDocument?: string
allowAbsoluteRoot?: boolean
}

export const getFilePath = (options: FilePathOptions): string | undefined => {
Expand All @@ -23,6 +24,7 @@ export const getFilePath = (options: FilePathOptions): string | undefined => {

const path = getFilePathWithoutDefaultDocument({
root: options.root,
allowAbsoluteRoot: options.allowAbsoluteRoot,
filename,
})

Expand All @@ -42,6 +44,9 @@ export const getFilePathWithoutDefaultDocument = (
// /foo.html => foo.html
filename = filename.replace(/^\.?[\/\\]/, '')

// assets\foo => assets/foo
root = root.replace(/\\/, '/')

// foo\bar.txt => foo/bar.txt
filename = filename.replace(/\\/, '/')

Expand All @@ -50,7 +55,16 @@ export const getFilePathWithoutDefaultDocument = (

// ./assets/foo.html => assets/foo.html
let path = root ? root + '/' + filename : filename
path = path.replace(/^\.?\//, '')

if (!options.allowAbsoluteRoot) {
path = path.replace(/^\.?\//, '')
} else {
// assets => /assets
path = path.replace(/^(?!\/)/, '/')
// Using URL to normalize the path.
const url = new URL(`file://${path}`)
path = url.pathname
}

return path
}

0 comments on commit 9e761e2

Please sign in to comment.