* 🎉 Create Bun Transpiler Middleware Package * ➕ Add @types/bun dev dependency * 🔧 Use Bun Testrunner * ✨ Implement Bun Transpiler Middleware * ✅ Add Tests for Bun Transpiler middleware * 📝 Add README to Bun Transpiler middleware * 📝 Add Changeset * 👷 Setup CI for Bun Transpiler middleware * 🔧 Configure Bun as external during Build * 🔧 Setup global build script for Bun Transpiler middlewarepull/347/head
parent
965d5a3f6d
commit
3f39be7a27
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@hono/bun-transpiler': minor
|
||||
---
|
||||
|
||||
Added @hono/bun-transpiler middleware
|
|
@ -0,0 +1,23 @@
|
|||
name: ci-bun-transpiler
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'packages/bun-transpiler/**'
|
||||
pull_request:
|
||||
branches: ['*']
|
||||
paths:
|
||||
- 'packages/bun-transpiler/**'
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./packages/bun-transpiler
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn build
|
||||
- run: yarn test
|
|
@ -27,6 +27,7 @@
|
|||
"build:oauth-providers": "yarn workspace @hono/oauth-providers build",
|
||||
"build:react-renderer": "yarn workspace @hono/react-renderer build",
|
||||
"build:auth-js": "yarn workspace @hono/auth-js build",
|
||||
"build:bun-transpiler": "yarn workspace @hono/bun-transpiler build",
|
||||
"build": "run-p 'build:*'",
|
||||
"lint": "eslint 'packages/**/*.{ts,tsx}'",
|
||||
"lint:fix": "eslint --fix 'packages/**/*.{ts,tsx}'",
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# Bun Transpiler middleware for Hono
|
||||
|
||||
The Bun Transpiler middleware is a Hono middleware designed to transpile content such as TypeScript or TSX. You can place your script written in TypeScript in a directory and serve it using `serveStatic`. When you apply this middleware, your script will automatically be served transpiled into JavaScript code.
|
||||
|
||||
This middleware works only with [Bun](https://bun.sh/).
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
npm i @hono/bun-transpiler
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```ts
|
||||
import { Hono } from 'hono'
|
||||
import { serveStatic } from 'hono/bun'
|
||||
import { bunTranspiler } from '@hono/bun-transpiler'
|
||||
|
||||
const app = new Hono()
|
||||
|
||||
app.get('/static/:scriptName{.+.tsx?}', bunTranspiler())
|
||||
app.get('/static/*', serveStatic({ root: './' }))
|
||||
|
||||
export default app
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- This middleware does not have a cache feature. If you want to cache the transpiled code, use [Cache Middleware](https://hono.dev/middleware/builtin/cache) or your own custom middleware.
|
||||
|
||||
## Author
|
||||
|
||||
Florian Kapaun <https://github.com/floriankapaun>
|
||||
|
||||
Heavily inspired by [esbuild-transpiler](https://github.com/honojs/middleware/tree/main/packages/esbuild-transpiler) by [Andres C. Rodriguez](https://github.com/acrodrig) and [Yusuke Wada](https://github.com/yusukebe).
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "@hono/bun-transpiler",
|
||||
"version": "0.1.1",
|
||||
"description": "Bun Transpiler Middleware for Hono",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "bun test",
|
||||
"build": "tsup ./src/index.ts --format esm,cjs --dts --external bun",
|
||||
"publint": "publint",
|
||||
"release": "yarn build && yarn test && yarn publint && yarn publish"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org",
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/honojs/middleware.git"
|
||||
},
|
||||
"homepage": "https://github.com/honojs/middleware",
|
||||
"peerDependencies": {
|
||||
"hono": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.0.0",
|
||||
"hono": "^3.11.7",
|
||||
"tsup": "^8.0.1",
|
||||
"vitest": "^1.0.4"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import { Hono } from 'hono'
|
||||
import { bunTranspiler } from '.'
|
||||
|
||||
const HOST = 'http://localhost'
|
||||
|
||||
const TS = 'const add = (a: number, b: number): number => a + b'
|
||||
const TS_TRANSPILED = 'const add=(a,b)=>a+b;'
|
||||
const TSX = 'const element = <h1>hello world</h1>'
|
||||
const TSX_TRANSPILED =
|
||||
'const element=jsxDEV("h1",{children:"hello world"},undefined,false,undefined,this);'
|
||||
const BAD = 'function { !!! !@#$ add(a: INT) return a + b + c; }'
|
||||
|
||||
describe('Bun Transpiler middleware', () => {
|
||||
const app = new Hono()
|
||||
|
||||
app.use('*', bunTranspiler())
|
||||
|
||||
app.get('/script.js', (c) => c.text(TS)) // Serve TS to test if it gets transpiled
|
||||
app.get('/script.ts', (c) => c.text(TS))
|
||||
app.get('/script.tsx', (c) => c.text(TSX))
|
||||
app.get('/bad.ts', (c) => c.text(BAD))
|
||||
|
||||
it('Should ignore non typescript content paths', async () => {
|
||||
const res = await app.request(`${HOST}/script.js`)
|
||||
expect(res).not.toBeNull()
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.text()).toBe(TS)
|
||||
})
|
||||
|
||||
it('Should transpile typescript', async () => {
|
||||
const res = await app.request(`${HOST}/script.ts`)
|
||||
expect(res).not.toBeNull()
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.text()).toBe(TS_TRANSPILED)
|
||||
expect(res.headers.get('content-type')).toBe('application/javascript')
|
||||
})
|
||||
|
||||
it('Should transpile TSX', async () => {
|
||||
const res = await app.request(`${HOST}/script.tsx`)
|
||||
expect(res).not.toBeNull()
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.text()).toBe(TSX_TRANSPILED)
|
||||
expect(res.headers.get('content-type')).toBe('application/javascript')
|
||||
})
|
||||
|
||||
it('Should return error on badly formed typescript', async () => {
|
||||
const res = await app.request(`${HOST}/bad.ts`)
|
||||
expect(res).not.toBeNull()
|
||||
expect(res.status).toBe(500)
|
||||
expect(await res.text()).toBe('Parse error')
|
||||
expect(res.headers.get('content-type')).toBe('text/plain')
|
||||
})
|
||||
})
|
|
@ -0,0 +1,54 @@
|
|||
import Bun from 'bun'
|
||||
import { createMiddleware } from 'hono/factory'
|
||||
|
||||
type BunTranspilerOptions = {
|
||||
extensions: string[]
|
||||
headers: HeadersInit
|
||||
transpilerOptions: Bun.TranspilerOptions
|
||||
}
|
||||
|
||||
export const bunTranspiler = (
|
||||
options: BunTranspilerOptions = {
|
||||
extensions: ['.ts', '.tsx'],
|
||||
headers: { 'content-type': 'application/javascript' },
|
||||
transpilerOptions: {
|
||||
minifyWhitespace: true,
|
||||
target: 'browser',
|
||||
},
|
||||
}
|
||||
) => {
|
||||
return createMiddleware(async (c, next) => {
|
||||
await next()
|
||||
const url = new URL(c.req.url)
|
||||
const { extensions, headers, transpilerOptions } = options
|
||||
|
||||
if (extensions.every((ext) => !url.pathname.endsWith(ext))) return
|
||||
|
||||
try {
|
||||
const loader = url.pathname.split('.').pop() as Bun.TranspilerOptions['loader']
|
||||
const transpiler = new Bun.Transpiler({
|
||||
loader,
|
||||
...transpilerOptions,
|
||||
})
|
||||
const transpiledCode = await transpiler.transformSync(await c.res.text())
|
||||
c.res = c.newResponse(transpiledCode, { headers })
|
||||
} catch (error) {
|
||||
console.warn(`Error transpiling ${url.pathname}: ${error}`)
|
||||
const errorHeaders = {
|
||||
...headers,
|
||||
'content-type': 'text/plain',
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
c.res = c.newResponse(error.message, {
|
||||
status: 500,
|
||||
headers: errorHeaders,
|
||||
})
|
||||
} else {
|
||||
c.res = c.newResponse('Malformed Input', {
|
||||
status: 500,
|
||||
headers: errorHeaders,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
}
|
29
yarn.lock
29
yarn.lock
|
@ -1379,6 +1379,19 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@hono/bun-transpiler@workspace:packages/bun-transpiler":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@hono/bun-transpiler@workspace:packages/bun-transpiler"
|
||||
dependencies:
|
||||
"@types/bun": "npm:^1.0.0"
|
||||
hono: "npm:^3.11.7"
|
||||
tsup: "npm:^8.0.1"
|
||||
vitest: "npm:^1.0.4"
|
||||
peerDependencies:
|
||||
hono: "*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@hono/clerk-auth@workspace:packages/clerk-auth":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@hono/clerk-auth@workspace:packages/clerk-auth"
|
||||
|
@ -3284,6 +3297,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/bun@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@types/bun@npm:1.0.0"
|
||||
dependencies:
|
||||
bun-types: "npm:1.0.18"
|
||||
checksum: 01784df20c982da2a5e41f4e84733436472e6d897bdb01c5b4154cf8bc38ff102718ce2d66d6e34ca2217683499c5a3767e542147efbdf510baaf4b72a854008
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/cacheable-request@npm:^6.0.1":
|
||||
version: 6.0.3
|
||||
resolution: "@types/cacheable-request@npm:6.0.3"
|
||||
|
@ -5055,6 +5077,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bun-types@npm:1.0.18":
|
||||
version: 1.0.18
|
||||
resolution: "bun-types@npm:1.0.18"
|
||||
checksum: 62fca2f8ee86b32def4065bf0490720c0e3bd3afe8b4fd70177dccda08e799dfe6317279c791ade502376ead3197ed7627ec6b8fa103466d398e968f70f05178
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bundle-require@npm:^4.0.0":
|
||||
version: 4.0.2
|
||||
resolution: "bundle-require@npm:4.0.2"
|
||||
|
|
Loading…
Reference in New Issue