Skip to content

Commit

Permalink
Merge branch 'main' into v4
Browse files Browse the repository at this point in the history
  • Loading branch information
yusukebe committed Jan 19, 2024
2 parents 1fbd27f + cce2b3c commit 24723ff
Show file tree
Hide file tree
Showing 9 changed files with 440 additions and 25 deletions.
21 changes: 13 additions & 8 deletions deno_dist/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ type ContextOptions<E extends Env> = {

export const TEXT_PLAIN = 'text/plain; charset=UTF-8'

const setHeaders = (headers: Headers, map: Record<string, string> = {}) => {
Object.entries(map).forEach(([key, value]) => headers.set(key, value))
return headers
}

export class Context<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
E extends Env = any,
Expand Down Expand Up @@ -240,24 +245,24 @@ export class Context<
}

if (arg && typeof arg !== 'number') {
this.res = new Response(data, arg)
const headers = setHeaders(new Headers(arg.headers), this.#preparedHeaders)
return new Response(data, {
headers,
status: arg.status,
})
}

const status = typeof arg === 'number' ? arg : arg ? arg.status : this.#status
const status = typeof arg === 'number' ? arg : this.#status
this.#preparedHeaders ??= {}

this.#headers ??= new Headers()
for (const [k, v] of Object.entries(this.#preparedHeaders)) {
this.#headers.set(k, v)
}
setHeaders(this.#headers, this.#preparedHeaders)

if (this.#res) {
this.#res.headers.forEach((v, k) => {
this.#headers?.set(k, v)
})
for (const [k, v] of Object.entries(this.#preparedHeaders)) {
this.#headers.set(k, v)
}
setHeaders(this.#headers, this.#preparedHeaders)
}

headers ??= {}
Expand Down
3 changes: 2 additions & 1 deletion deno_dist/hono-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ class Hono<
handlers.map((handler) => {
this.addRoute(METHOD_NAME_ALL, this.#path, handler)
})
return this
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return this as any
}

const strict = options.strict ?? true
Expand Down
186 changes: 183 additions & 3 deletions deno_dist/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -705,12 +705,192 @@ export interface MiddlewareHandlerInterface<
S extends Schema = {},
BasePath extends string = '/'
> {
//// app.get(...handlers[])
//// app.use(...handlers[])
<E2 extends Env = E>(
...handlers: MiddlewareHandler<E2, MergePath<BasePath, ExtractKey<S>>>[]
): Hono<E, S, BasePath>
): Hono<IntersectNonAnyTypes<[E, E2]>, S, BasePath>

// app.use(handler)
<E2 extends Env = E>(handler: MiddlewareHandler<E2, MergePath<BasePath, ExtractKey<S>>>): Hono<
IntersectNonAnyTypes<[E, E2]>,
S,
BasePath
>

// app.use(handler x2)
<
E2 extends Env = E,
E3 extends Env = IntersectNonAnyTypes<[E, E2]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [MiddlewareHandler<E2, P>, MiddlewareHandler<E3, P>]
): Hono<IntersectNonAnyTypes<[E, E2, E3]>, S, BasePath>

// app.use(handler x3)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [MiddlewareHandler<E2, P>, MiddlewareHandler<E3, P>, MiddlewareHandler<E4, P>]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4]>, S, BasePath>

// app.use(handler x4)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5]>, S, BasePath>

// app.use(handler x5)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = E,
E6 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>,
MiddlewareHandler<E6, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>, S, BasePath>

// app.use(handler x6)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = E,
E6 extends Env = E,
E7 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>,
MiddlewareHandler<E6, P>,
MiddlewareHandler<E7, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>, S, BasePath>

// app.use(handler x7)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = E,
E6 extends Env = E,
E7 extends Env = E,
E8 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>,
MiddlewareHandler<E6, P>,
MiddlewareHandler<E7, P>,
MiddlewareHandler<E8, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>, S, BasePath>

// app.use(handler x8)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = E,
E6 extends Env = E,
E7 extends Env = E,
E8 extends Env = E,
E9 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>,
MiddlewareHandler<E6, P>,
MiddlewareHandler<E7, P>,
MiddlewareHandler<E8, P>,
MiddlewareHandler<E9, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>, S, BasePath>

// app.use(handler x9)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = E,
E6 extends Env = E,
E7 extends Env = E,
E8 extends Env = E,
E9 extends Env = E,
E10 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>,
MiddlewareHandler<E6, P>,
MiddlewareHandler<E7, P>,
MiddlewareHandler<E8, P>,
MiddlewareHandler<E9, P>,
MiddlewareHandler<E10, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>, S, BasePath>

// app.use(handler x10)
<
E2 extends Env = E,
E3 extends Env = E,
E4 extends Env = E,
E5 extends Env = E,
E6 extends Env = E,
E7 extends Env = E,
E8 extends Env = E,
E9 extends Env = E,
E10 extends Env = E,
E11 extends Env = IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10]>,
P extends string = MergePath<BasePath, ExtractKey<S>>
>(
...handlers: [
MiddlewareHandler<E2, P>,
MiddlewareHandler<E3, P>,
MiddlewareHandler<E4, P>,
MiddlewareHandler<E5, P>,
MiddlewareHandler<E6, P>,
MiddlewareHandler<E7, P>,
MiddlewareHandler<E8, P>,
MiddlewareHandler<E9, P>,
MiddlewareHandler<E10, P>,
MiddlewareHandler<E11, P>
]
): Hono<IntersectNonAnyTypes<[E, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11]>, S, BasePath>

//// app.get(path, ...handlers[])
//// app.use(path, ...handlers[])
<P extends string, E2 extends Env = E>(
path: P,
...handlers: MiddlewareHandler<E2, MergePath<BasePath, P>>[]
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -524,4 +524,4 @@
"engines": {
"node": ">=16.0.0"
}
}
}
21 changes: 13 additions & 8 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ type ContextOptions<E extends Env> = {

export const TEXT_PLAIN = 'text/plain; charset=UTF-8'

const setHeaders = (headers: Headers, map: Record<string, string> = {}) => {
Object.entries(map).forEach(([key, value]) => headers.set(key, value))
return headers
}

export class Context<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
E extends Env = any,
Expand Down Expand Up @@ -240,24 +245,24 @@ export class Context<
}

if (arg && typeof arg !== 'number') {
this.res = new Response(data, arg)
const headers = setHeaders(new Headers(arg.headers), this.#preparedHeaders)
return new Response(data, {
headers,
status: arg.status,
})
}

const status = typeof arg === 'number' ? arg : arg ? arg.status : this.#status
const status = typeof arg === 'number' ? arg : this.#status
this.#preparedHeaders ??= {}

this.#headers ??= new Headers()
for (const [k, v] of Object.entries(this.#preparedHeaders)) {
this.#headers.set(k, v)
}
setHeaders(this.#headers, this.#preparedHeaders)

if (this.#res) {
this.#res.headers.forEach((v, k) => {
this.#headers?.set(k, v)
})
for (const [k, v] of Object.entries(this.#preparedHeaders)) {
this.#headers.set(k, v)
}
setHeaders(this.#headers, this.#preparedHeaders)
}

headers ??= {}
Expand Down
3 changes: 2 additions & 1 deletion src/hono-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ class Hono<
handlers.map((handler) => {
this.addRoute(METHOD_NAME_ALL, this.#path, handler)
})
return this
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return this as any
}

const strict = options.strict ?? true
Expand Down
20 changes: 20 additions & 0 deletions src/hono.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,26 @@ describe('Request methods with custom middleware', () => {
})
})

describe('Middleware + c.json(0, requestInit)', () => {
const app = new Hono()
app.use('/', async (c, next) => {
await next()
})
app.get('/', (c) => {
return c.json(0, {
status: 200,
headers: {
foo: 'bar',
},
})
})
it('Should return a correct headers', async () => {
const res = await app.request('/')
expect(res.headers.get('content-type')).toMatch(/^application\/json/)
expect(res.headers.get('foo')).toBe('bar')
})
})

describe('Hono with `app.route`', () => {
describe('Basic', () => {
const app = new Hono()
Expand Down
23 changes: 23 additions & 0 deletions src/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1054,3 +1054,26 @@ describe('Env types with chained routes - test only types', () => {
)
})
})

describe('Env types with `use` middleware - test only types', () => {
const app = new Hono()

const mw1 = createMiddleware<{ Variables: { foo1: string } }>(async () => {})
const mw2 = createMiddleware<{ Variables: { foo2: string } }>(async () => {})

it('Should not throw a type error', () => {
app
.use(mw1)
.use(mw2)
.get('/', (c) => {
expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
return c.json({ success: true })
})
app.use(mw1, mw2).get('/', (c) => {
expectTypeOf(c.get('foo1')).toEqualTypeOf<string>()
expectTypeOf(c.get('foo2')).toEqualTypeOf<string>()
return c.json({ success: true })
})
})
})
Loading

0 comments on commit 24723ff

Please sign in to comment.