diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fd29d8df..f40e3a80f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,6 +97,17 @@ jobs: with: name: coverage-bun path: coverage/ + + bun-windows: + name: 'Bun - Windows' + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: '1.1.16' + - run: bun run test:bun + fastly: name: 'Fastly Compute' runs-on: ubuntu-latest @@ -204,7 +215,6 @@ jobs: - run: echo "$COMPARISON" name: display comparison - perf-measures-type-check-on-main: runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' diff --git a/runtime-tests/bun/index.test.tsx b/runtime-tests/bun/index.test.tsx index 6dda78aba..7b303f839 100644 --- a/runtime-tests/bun/index.test.tsx +++ b/runtime-tests/bun/index.test.tsx @@ -152,13 +152,13 @@ describe('Serve Static Middleware', () => { it('Should return 200 response - /static/helloworld', async () => { const res = await app.request('http://localhost/static/helloworld') expect(res.status).toBe(200) - expect(await res.text()).toBe('Hi\n') + expect(await res.text()).toMatch(/Hi\r?\n/) }) it('Should return 200 response - /static/hello.world', async () => { const res = await app.request('http://localhost/static/hello.world') expect(res.status).toBe(200) - expect(await res.text()).toBe('Hi\n') + expect(await res.text()).toMatch(/Hi\r?\n/) }) it('Should return 200 response - /static-absolute-root/plain.txt', async () => { diff --git a/src/middleware/serve-static/index.ts b/src/middleware/serve-static/index.ts index 0f1837f22..9e44a40aa 100644 --- a/src/middleware/serve-static/index.ts +++ b/src/middleware/serve-static/index.ts @@ -29,6 +29,17 @@ const ENCODINGS_ORDERED_KEYS = Object.keys(ENCODINGS) as (keyof typeof ENCODINGS const DEFAULT_DOCUMENT = 'index.html' const defaultPathResolve = (path: string) => path +const isAbsolutePath = (path: string) => { + const isUnixAbsolutePath = path.startsWith('/') + const hasDriveLetter = /^[a-zA-Z]:\\/.test(path) + const isUncPath = /^\\\\[^\\]+\\[^\\]+/.test(path) + return isUnixAbsolutePath || hasDriveLetter || isUncPath +} + +const windowsPathToUnixPath = (path: string) => { + return path.replace(/^[a-zA-Z]:/, '').replace(/\\/g, '/') +} + /** * This middleware is not directly used by the user. Create a wrapper specifying `getContent()` by the environment such as Deno or Bun. */ @@ -43,9 +54,10 @@ export const serveStatic = ( let root: string if (options.root) { - if (options.root.startsWith('/')) { + if (isAbsolutePath(options.root)) { isAbsoluteRoot = true - root = new URL(`file://${options.root}`).pathname + root = windowsPathToUnixPath(options.root) + root = new URL(`file://${root}`).pathname } else { root = options.root }