diff --git a/README.md b/README.md index 67ad116..9bd77f7 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ import { serveStatic } from '@hono/node-server/serve-static' app.use('/static/*', serveStatic({ root: './' })) ``` -Note that `root` must be _relative_ to the current working directory from which the app was started. Absolute paths are not supported. +Note that `root` must be _relative_ to the current working directory from which the app was started. This can cause confusion when running your application locally. diff --git a/src/serve-static.ts b/src/serve-static.ts index faea8bd..005c426 100644 --- a/src/serve-static.ts +++ b/src/serve-static.ts @@ -44,8 +44,8 @@ const createStreamBody = (stream: ReadStream) => { return body } -const addCurrentDirPrefix = (path: string) => { - return `./${path}` +const addDirPrefix = (path: string, absolute: boolean) => { + return absolute ? '/' + path : `./${path}` } const getStats = (path: string) => { @@ -57,6 +57,18 @@ const getStats = (path: string) => { } export const serveStatic = (options: ServeStaticOptions = { root: '' }): MiddlewareHandler => { + let isAbsoluteRoot = false + let root: string + + if (options.root) { + if (options.root.startsWith('/')) { + isAbsoluteRoot = true + root = new URL(`file://${options.root}`).pathname + } else { + root = options.root + } + } + return async (c, next) => { // Do nothing if Response is already set if (c.finalized) { @@ -67,11 +79,11 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew let path = getFilePathWithoutDefaultDocument({ filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename, - root: options.root, + root, }) if (path) { - path = addCurrentDirPrefix(path) + path = addDirPrefix(path, isAbsoluteRoot) } else { return next() } @@ -81,12 +93,12 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew if (stats && stats.isDirectory()) { path = getFilePath({ filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename) : filename, - root: options.root, + root, defaultDocument: options.index ?? 'index.html', }) if (path) { - path = addCurrentDirPrefix(path) + path = addDirPrefix(path, isAbsoluteRoot) } else { return next() } diff --git a/test/assets/static-absolute-root-with-dots/hello.txt b/test/assets/static-absolute-root-with-dots/hello.txt new file mode 100644 index 0000000..35fd066 --- /dev/null +++ b/test/assets/static-absolute-root-with-dots/hello.txt @@ -0,0 +1 @@ +Hello with absolute root with dots \ No newline at end of file diff --git a/test/assets/static-absolute-root/hello.txt b/test/assets/static-absolute-root/hello.txt new file mode 100644 index 0000000..378a9e2 --- /dev/null +++ b/test/assets/static-absolute-root/hello.txt @@ -0,0 +1 @@ +Hello with absolute root \ No newline at end of file diff --git a/test/serve-static.test.ts b/test/serve-static.test.ts index 3735098..06dfab7 100644 --- a/test/serve-static.test.ts +++ b/test/serve-static.test.ts @@ -1,5 +1,6 @@ import { Hono } from 'hono' import request from 'supertest' +import path from 'path' import { serveStatic } from './../src/serve-static' import { createAdaptorServer } from './../src/server' @@ -43,6 +44,16 @@ describe('Serve Static Middleware', () => { }) ) + app.all( + '/static-absolute-root/*', + serveStatic({ root: path.join(path.dirname(__filename), 'assets') }) + ) + + app.all( + '/static-absolute-root-with-dots/*', + serveStatic({ root: path.join(path.dirname(__filename), 'assets') + '/../assets' }) + ) + const server = createAdaptorServer(app) it('Should return index.html', async () => { @@ -196,4 +207,20 @@ describe('Serve Static Middleware', () => { expect(res.headers['vary']).toBeUndefined() expect(res.text).toBe('Hello Not Compressed') }) + + it('Should return 200 with an absolute root - /static-absolute-root/hello.txt', async () => { + const res = await request(server).get('/static-absolute-root/hello.txt') + expect(res.status).toBe(200) + expect(res.headers['content-type']).toBe('text/plain; charset=utf-8') + expect(res.headers['content-length']).toBe('24') + expect(res.text).toBe('Hello with absolute root') + }) + + it('Should return 200 with an absolute root with dots - /static-absolute-root-with-dots/hello.txt', async () => { + const res = await request(server).get('/static-absolute-root-with-dots/hello.txt') + expect(res.status).toBe(200) + expect(res.headers['content-type']).toBe('text/plain; charset=utf-8') + expect(res.headers['content-length']).toBe('34') + expect(res.text).toBe('Hello with absolute root with dots') + }) })