feat(tsyringe): add @hono/tsyringe middleware (#785)
parent
68753ca8b2
commit
932d651aaf
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@hono/tsyringe': major
|
||||
---
|
||||
|
||||
add tsyringe middleware support
|
|
@ -0,0 +1,25 @@
|
|||
name: ci-tsyringe
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'packages/tsyringe/**'
|
||||
pull_request:
|
||||
branches: ['*']
|
||||
paths:
|
||||
- 'packages/tsyringe/**'
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./packages/tsyringe
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn build
|
||||
- run: yarn test
|
|
@ -0,0 +1,57 @@
|
|||
# tsyringe middleware for Hono
|
||||
|
||||
The [tsyringe](https://github.com/microsoft/tsyringe) middleware provides a way to use dependency injection in [Hono](https://hono.dev/).
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import "reflect-metadata" // tsyringe requires reflect-metadata or polyfill
|
||||
import { container, inject, injectable } from 'tsyringe'
|
||||
import { tsyringe } from '@hono/tsyringe'
|
||||
import { Hono } from 'hono'
|
||||
|
||||
@injectable()
|
||||
class Hello {
|
||||
constructor(@inject('name') private name: string) {}
|
||||
|
||||
greet() {
|
||||
return `Hello, ${this.name}!`
|
||||
}
|
||||
}
|
||||
|
||||
const app = new Hono()
|
||||
|
||||
app.use('*', tsyringe((container) => {
|
||||
container.register('name', { useValue: 'world' })
|
||||
}))
|
||||
|
||||
app.get('/', (c) => {
|
||||
const hello = container.resolve(Hello)
|
||||
return c.text(hello.greet())
|
||||
})
|
||||
|
||||
export default app
|
||||
```
|
||||
|
||||
### With providers
|
||||
|
||||
```ts
|
||||
const app = new Hono()
|
||||
|
||||
app.use('/tenant/:name/*', async (c, next) => {
|
||||
await tsyringe((container) => {
|
||||
// Allowing to inject `c.var` or `c.req.param` in the providers
|
||||
const tenantName = c.req.param('name')
|
||||
|
||||
container.register(Config, { useFactory: () => new Config(tenantName) })
|
||||
})(c, next)
|
||||
})
|
||||
```
|
||||
|
||||
## Author
|
||||
|
||||
Aotokitsuruya <https://github.com/elct9620>
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "@hono/tsyringe",
|
||||
"version": "0.1.0",
|
||||
"description": "The tsyringe dependency injection middleware for Hono",
|
||||
"type": "module",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest --run",
|
||||
"build": "tsup ./src/index.ts --format esm,cjs --dts",
|
||||
"publint": "publint",
|
||||
"release": "yarn build && yarn test && yarn publint && yarn publish"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": ">=4.*",
|
||||
"tsyringe": ">=4.*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"hono": "^4.4.12",
|
||||
"prettier": "^3.3.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"tsup": "^8.1.0",
|
||||
"tsyringe": "^4.8.0",
|
||||
"vitest": "^1.6.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import 'reflect-metadata'
|
||||
import { injectable, inject } from 'tsyringe'
|
||||
import { Hono } from 'hono'
|
||||
import { tsyringe } from '../src'
|
||||
|
||||
class Config {
|
||||
constructor(public readonly tenantName: string) {}
|
||||
}
|
||||
|
||||
@injectable()
|
||||
class TenantService {
|
||||
constructor(@inject(Config) public readonly config: Config) {}
|
||||
|
||||
get message() {
|
||||
return `Hello, ${this.config.tenantName}!`
|
||||
}
|
||||
}
|
||||
|
||||
describe('tsyringe middleware', () => {
|
||||
const app = new Hono()
|
||||
|
||||
app.use(
|
||||
'/hello/*',
|
||||
tsyringe((container) => container.register('foo', { useValue: 'Hello!' }))
|
||||
)
|
||||
app.get('/hello/foo', (c) => {
|
||||
const message = c.var.resolve<string>('foo')
|
||||
return c.text(message)
|
||||
})
|
||||
|
||||
app.use('/tenant/:name/*', async (c, next) => {
|
||||
await tsyringe((container) => {
|
||||
const tenantName = c.req.param('name')
|
||||
|
||||
container.register(Config, { useFactory: () => new Config(tenantName) })
|
||||
})(c, next)
|
||||
})
|
||||
|
||||
app.get('/tenant/:name/message', (c) => {
|
||||
const tenantService = c.var.resolve(TenantService)
|
||||
return c.text(tenantService.message)
|
||||
})
|
||||
|
||||
it('Should be hello message', async () => {
|
||||
const res = await app.request('http://localhost/hello/foo')
|
||||
expect(res).not.toBeNull()
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.text()).toBe('Hello!')
|
||||
})
|
||||
|
||||
it('Should be tenant message', async () => {
|
||||
const res = await app.request('http://localhost/tenant/foo/message')
|
||||
expect(res).not.toBeNull()
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.text()).toBe('Hello, foo!')
|
||||
})
|
||||
})
|
|
@ -0,0 +1,19 @@
|
|||
import { container, DependencyContainer, InjectionToken } from 'tsyringe'
|
||||
import type { Context, MiddlewareHandler } from 'hono'
|
||||
import { createMiddleware } from 'hono/factory'
|
||||
|
||||
declare module 'hono' {
|
||||
interface ContextVariableMap {
|
||||
resolve: <T>(token: InjectionToken<T>) => T
|
||||
}
|
||||
}
|
||||
|
||||
export type Provider = (container: DependencyContainer) => void
|
||||
export const tsyringe = (...providers: Provider[]): MiddlewareHandler => {
|
||||
return createMiddleware(async (c, next) => {
|
||||
const childContainer = container.createChildContainer()
|
||||
providers.forEach((provider) => provider(childContainer))
|
||||
c.set('resolve', <T>(token: InjectionToken<T>) => childContainer.resolve(token))
|
||||
await next()
|
||||
})
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/// <reference types="vitest" />
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
},
|
||||
})
|
36
yarn.lock
36
yarn.lock
|
@ -2863,6 +2863,22 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@hono/tsyringe@workspace:packages/tsyringe":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@hono/tsyringe@workspace:packages/tsyringe"
|
||||
dependencies:
|
||||
hono: "npm:^4.4.12"
|
||||
prettier: "npm:^3.3.3"
|
||||
reflect-metadata: "npm:^0.2.2"
|
||||
tsup: "npm:^8.1.0"
|
||||
tsyringe: "npm:^4.8.0"
|
||||
vitest: "npm:^1.6.0"
|
||||
peerDependencies:
|
||||
hono: ">=4.*"
|
||||
tsyringe: ">=4.*"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@hono/typebox-validator@workspace:packages/typebox-validator":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@hono/typebox-validator@workspace:packages/typebox-validator"
|
||||
|
@ -16823,6 +16839,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:^3.3.3":
|
||||
version: 3.3.3
|
||||
resolution: "prettier@npm:3.3.3"
|
||||
bin:
|
||||
prettier: bin/prettier.cjs
|
||||
checksum: b85828b08e7505716324e4245549b9205c0cacb25342a030ba8885aba2039a115dbcf75a0b7ca3b37bc9d101ee61fab8113fc69ca3359f2a226f1ecc07ad2e26
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pretty-format@npm:^28.0.0, pretty-format@npm:^28.1.3":
|
||||
version: 28.1.3
|
||||
resolution: "pretty-format@npm:28.1.3"
|
||||
|
@ -19522,7 +19547,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^1.8.1, tslib@npm:^1.9.0":
|
||||
"tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3":
|
||||
version: 1.14.1
|
||||
resolution: "tslib@npm:1.14.1"
|
||||
checksum: 69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2
|
||||
|
@ -19830,6 +19855,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tsyringe@npm:^4.8.0":
|
||||
version: 4.8.0
|
||||
resolution: "tsyringe@npm:4.8.0"
|
||||
dependencies:
|
||||
tslib: "npm:^1.9.3"
|
||||
checksum: e13810e8ff39c4093acd0649bc5db3c164825827631e1522cd9d5ca8694a018447fa1c24f059ea54e93b1020767b1131b9dc9ce598dabfc9aa41c11544bbfe19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tty-table@npm:^4.1.5":
|
||||
version: 4.2.3
|
||||
resolution: "tty-table@npm:4.2.3"
|
||||
|
|
Loading…
Reference in New Issue