diff --git a/packages/waku/src/adapters/aws-lambda.ts b/packages/waku/src/adapters/aws-lambda.ts index 672066ff07..5e22442408 100644 --- a/packages/waku/src/adapters/aws-lambda.ts +++ b/packages/waku/src/adapters/aws-lambda.ts @@ -22,7 +22,7 @@ export default createServerEntryAdapter( options?: { streaming?: boolean; bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -48,9 +48,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { distDir: config.distDir, diff --git a/packages/waku/src/adapters/bun.ts b/packages/waku/src/adapters/bun.ts index 0f51da8a3f..2dcfb02209 100644 --- a/packages/waku/src/adapters/bun.ts +++ b/packages/waku/src/adapters/bun.ts @@ -20,7 +20,7 @@ export default createServerEntryAdapter( { processRequest, processBuild, config, isBuild, notFoundHtml }, options?: { bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -52,9 +52,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { distDir: config.distDir, diff --git a/packages/waku/src/adapters/cloudflare.ts b/packages/waku/src/adapters/cloudflare.ts index e4649ad2d1..d50759fb1a 100644 --- a/packages/waku/src/adapters/cloudflare.ts +++ b/packages/waku/src/adapters/cloudflare.ts @@ -60,7 +60,7 @@ export default createServerEntryAdapter( handlers?: Record; assetsDir?: string; bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; internalPathToBuildStaticFiles?: string; }, @@ -85,9 +85,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { srcDir: config.srcDir, diff --git a/packages/waku/src/adapters/deno.ts b/packages/waku/src/adapters/deno.ts index 8bbada0c1d..5a7bbb0369 100644 --- a/packages/waku/src/adapters/deno.ts +++ b/packages/waku/src/adapters/deno.ts @@ -20,7 +20,9 @@ export default createServerEntryAdapter( { processRequest, processBuild, config, notFoundHtml }, options?: { bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { + app: HonoForDevAndBuild; + }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -50,9 +52,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { distDir: config.distDir, diff --git a/packages/waku/src/adapters/edge.ts b/packages/waku/src/adapters/edge.ts index f5d009851c..0ce726b044 100644 --- a/packages/waku/src/adapters/edge.ts +++ b/packages/waku/src/adapters/edge.ts @@ -20,7 +20,7 @@ export default createServerEntryAdapter( { processRequest, processBuild, notFoundHtml }, options?: { bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -43,9 +43,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); return { fetch: app.fetch, diff --git a/packages/waku/src/adapters/netlify.ts b/packages/waku/src/adapters/netlify.ts index 94af89eb7a..01639b6c29 100644 --- a/packages/waku/src/adapters/netlify.ts +++ b/packages/waku/src/adapters/netlify.ts @@ -19,7 +19,7 @@ export default createServerEntryAdapter( options?: { static?: boolean; bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -42,9 +42,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { distDir: config.distDir, diff --git a/packages/waku/src/adapters/node.ts b/packages/waku/src/adapters/node.ts index c940a8b893..ec1c5c2ce1 100644 --- a/packages/waku/src/adapters/node.ts +++ b/packages/waku/src/adapters/node.ts @@ -21,7 +21,7 @@ export default createServerEntryAdapter( { processRequest, processBuild, config, isBuild, notFoundHtml }, options?: { bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -53,9 +53,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { distDir: config.distDir, diff --git a/packages/waku/src/adapters/vercel.ts b/packages/waku/src/adapters/vercel.ts index 6f8f4a4d8e..552bbda098 100644 --- a/packages/waku/src/adapters/vercel.ts +++ b/packages/waku/src/adapters/vercel.ts @@ -23,7 +23,7 @@ export default createServerEntryAdapter( static?: boolean; assetsDir?: string; bodyLimit?: Parameters[0] | false; - middlewareFns?: (() => MiddlewareHandler)[]; + middlewareFns?: ((opts: { app: Hono }) => MiddlewareHandler)[]; middlewareModules?: Record Promise>; }, ) => { @@ -46,9 +46,9 @@ export default createServerEntryAdapter( } app.use(contextMiddleware()); for (const middlewareFn of middlewareFns) { - app.use(middlewareFn()); + app.use(middlewareFn({ app })); } - app.use(middlewareRunner(middlewareModules as never)); + app.use(middlewareRunner(middlewareModules as never, { app })); app.use(rscMiddleware({ processRequest })); const buildOptions: BuildOptions = { assetsDir: options?.assetsDir || 'assets', diff --git a/packages/waku/src/lib/hono/middleware.ts b/packages/waku/src/lib/hono/middleware.ts index ef9d59dc01..c0f8e66ee3 100644 --- a/packages/waku/src/lib/hono/middleware.ts +++ b/packages/waku/src/lib/hono/middleware.ts @@ -1,4 +1,5 @@ import type { MiddlewareHandler } from 'hono'; +import type { Hono } from 'hono/tiny'; import { INTERNAL_runWithContext } from '../context.js'; import type { Unstable_ProcessRequest as ProcessRequest } from '../types.js'; @@ -29,16 +30,17 @@ export function middlewareRunner( middlewareModules: Record< string, () => Promise<{ - default: () => MiddlewareHandler; + default: (opts: { app: Hono }) => MiddlewareHandler; }> >, + opts: { app: Hono }, ): MiddlewareHandler { let handlersPromise: Promise | undefined; return async (c, next) => { if (!handlersPromise) { handlersPromise = Promise.all( Object.values(middlewareModules).map((m) => - m().then((mod) => mod.default()), + m().then((mod) => mod.default(opts)), ), ); } diff --git a/packages/waku/tests/hono-middleware.test.ts b/packages/waku/tests/hono-middleware.test.ts index f4e41864b4..fba72cb2eb 100644 --- a/packages/waku/tests/hono-middleware.test.ts +++ b/packages/waku/tests/hono-middleware.test.ts @@ -6,12 +6,15 @@ describe('middlewareRunner', () => { test('preserves response returned by a middleware module', async () => { const app = new Hono(); app.use( - middlewareRunner({ - '/src/middleware/no-trailing-slash.ts': async () => ({ - default: () => async (c) => - c.redirect(new URL('/about', c.req.url).toString(), 301), - }), - }), + middlewareRunner( + { + '/src/middleware/no-trailing-slash.ts': async () => ({ + default: () => async (c) => + c.redirect(new URL('/about', c.req.url).toString(), 301), + }), + }, + { app }, + ), ); app.get('*', (c) => c.text('ok')); @@ -24,17 +27,20 @@ describe('middlewareRunner', () => { test('preserves response returned by an inner middleware module', async () => { const app = new Hono(); app.use( - middlewareRunner({ - '/src/middleware/outer.ts': async () => ({ - default: () => async (_c, next) => { - await next(); - }, - }), - '/src/middleware/no-trailing-slash.ts': async () => ({ - default: () => async (c) => - c.redirect(new URL('/about', c.req.url).toString(), 301), - }), - }), + middlewareRunner( + { + '/src/middleware/outer.ts': async () => ({ + default: () => async (_c, next) => { + await next(); + }, + }), + '/src/middleware/no-trailing-slash.ts': async () => ({ + default: () => async (c) => + c.redirect(new URL('/about', c.req.url).toString(), 301), + }), + }, + { app }, + ), ); app.get('*', (c) => c.text('ok')); @@ -47,17 +53,20 @@ describe('middlewareRunner', () => { test('keeps inner response even if outer middleware returns after next', async () => { const app = new Hono(); app.use( - middlewareRunner({ - '/src/middleware/outer.ts': async () => ({ - default: () => async (_c, next) => { - await next(); - return new Response('outer', { status: 202 }); - }, - }), - '/src/middleware/inner.ts': async () => ({ - default: () => async () => new Response('inner', { status: 201 }), - }), - }), + middlewareRunner( + { + '/src/middleware/outer.ts': async () => ({ + default: () => async (_c, next) => { + await next(); + return new Response('outer', { status: 202 }); + }, + }), + '/src/middleware/inner.ts': async () => ({ + default: () => async () => new Response('inner', { status: 201 }), + }), + }, + { app }, + ), ); app.get('*', (c) => c.text('ok')); @@ -66,4 +75,29 @@ describe('middlewareRunner', () => { expect(res.status).toBe(201); expect(await res.text()).toBe('inner'); }); + + test('passes { app } to each middleware module factory', async () => { + const app = new Hono(); + let receivedApp: unknown; + app.use( + middlewareRunner( + { + '/src/middleware/probe.ts': async () => ({ + default: ({ app }) => { + receivedApp = app; + return async (_c, next) => { + await next(); + }; + }, + }), + }, + { app }, + ), + ); + app.get('*', (c) => c.text('ok')); + + await app.request('http://example.com/'); + + expect(receivedApp).toBe(app); + }); });