Skip to content

Commit 93127dc

Browse files
authored
fix(types): expose middleware on koa and hono wrappers (#2489)
1 parent 3c38a7f commit 93127dc

5 files changed

Lines changed: 77 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack-dev-middleware": patch
3+
---
4+
5+
Expose the existing `devMiddleware` property in the Koa and Hono wrapper return types, preserving custom request and response types.

‎src/index.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ wdm.hapiWrapper = hapiWrapper;
10061006
* @param {Compiler | MultiCompiler} compiler compiler
10071007
* @param {Options<RequestInternal, ResponseInternal>=} options options
10081008
* @param {boolean=} usePlugin whether to use as webpack plugin
1009-
* @returns {(ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void} kow wrapper
1009+
* @returns {((ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void) & { devMiddleware: API<RequestInternal, ResponseInternal> }} koa wrapper
10101010
*/
10111011
function koaWrapper(compiler, options = {}, usePlugin = false) {
10121012
const devMiddleware = wdm(compiler, options, usePlugin);
@@ -1149,7 +1149,7 @@ wdm.koaWrapper = koaWrapper;
11491149
* @param {Compiler | MultiCompiler} compiler compiler
11501150
* @param {Options<RequestInternal, ResponseInternal>=} options options
11511151
* @param {boolean=} usePlugin true when need to use as a plugin, otherwise false
1152-
* @returns {(ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void} hono wrapper
1152+
* @returns {((ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void) & { devMiddleware: API<RequestInternal, ResponseInternal> }} hono wrapper
11531153
*/
11541154
function honoWrapper(compiler, options = {}, usePlugin = false) {
11551155
const devMiddleware = wdm(compiler, options, usePlugin);

‎test/fixtures/types/wrappers.cts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { IncomingMessage, ServerResponse } from "node:http";
2+
import webpack = require("webpack");
3+
import middleware = require("../../..");
4+
5+
const compiler = webpack({ mode: "development" });
6+
const koa = middleware.koaWrapper(compiler);
7+
const hono = middleware.honoWrapper(compiler);
8+
9+
for (const wrapper of [koa, hono]) {
10+
const api: ReturnType<typeof middleware> = wrapper.devMiddleware;
11+
api.waitUntilValid(() => {});
12+
api.invalidate(() => {});
13+
api.close(() => {});
14+
api.context.options.publicPath = "/assets/";
15+
// @ts-expect-error The underlying API must not become untyped.
16+
wrapper.devMiddleware.missingMethod();
17+
// @ts-expect-error Invalid options must still be rejected.
18+
wrapper.devMiddleware.context.options.publicPath = 123;
19+
}
20+
21+
interface CustomRequest extends IncomingMessage {
22+
customRequest: string;
23+
}
24+
interface CustomResponse extends ServerResponse {
25+
customResponse: string;
26+
}
27+
28+
const customKoa = middleware.koaWrapper<CustomRequest, CustomResponse>(compiler);
29+
const customHono = middleware.honoWrapper<CustomRequest, CustomResponse>(compiler);
30+
for (const wrapper of [customKoa, customHono]) {
31+
const api: middleware.API<CustomRequest, CustomResponse> = wrapper.devMiddleware;
32+
wrapper.devMiddleware.context.options.headers = (req, res) => ({
33+
"X-Request": req.customRequest,
34+
"X-Response": res.customResponse,
35+
});
36+
// @ts-expect-error Custom request properties remain typed.
37+
wrapper.devMiddleware.context.options.headers = (req) => ({ "X-Invalid": req.missingProperty });
38+
}

‎test/wrapper-types.test.js‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import path from "node:path";
2+
import ts from "typescript";
3+
4+
describe("wrapper types", () => {
5+
it("exposes each wrapper's middleware API and preserves request/response types", () => {
6+
const program = ts.createProgram(
7+
[path.resolve(__dirname, "fixtures/types/wrappers.cts")],
8+
{
9+
strict: true,
10+
noEmit: true,
11+
skipLibCheck: true,
12+
module: ts.ModuleKind.NodeNext,
13+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
14+
types: ["node"],
15+
},
16+
);
17+
const diagnostics = ts.getPreEmitDiagnostics(program);
18+
expect(
19+
diagnostics.map((diagnostic) =>
20+
ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
21+
),
22+
).toEqual([]);
23+
});
24+
});

‎types/index.d.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ declare function hapiWrapper<
105105
* @param {Compiler | MultiCompiler} compiler compiler
106106
* @param {Options<RequestInternal, ResponseInternal>=} options options
107107
* @param {boolean=} usePlugin whether to use as webpack plugin
108-
* @returns {(ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void} kow wrapper
108+
* @returns {((ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void) & { devMiddleware: API<RequestInternal, ResponseInternal> }} koa wrapper
109109
*/
110110
declare function koaWrapper<
111111
RequestInternal extends IncomingMessage = import("node:http").IncomingMessage,
@@ -114,14 +114,16 @@ declare function koaWrapper<
114114
compiler: Compiler | MultiCompiler,
115115
options?: Options<RequestInternal, ResponseInternal> | undefined,
116116
usePlugin?: boolean | undefined,
117-
): (ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void;
117+
): ((ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void) & {
118+
devMiddleware: API<RequestInternal, ResponseInternal>;
119+
};
118120
/**
119121
* @template {IncomingMessage} [RequestInternal=IncomingMessage]
120122
* @template {ServerResponse} [ResponseInternal=ServerResponse]
121123
* @param {Compiler | MultiCompiler} compiler compiler
122124
* @param {Options<RequestInternal, ResponseInternal>=} options options
123125
* @param {boolean=} usePlugin true when need to use as a plugin, otherwise false
124-
* @returns {(ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void} hono wrapper
126+
* @returns {((ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void) & { devMiddleware: API<RequestInternal, ResponseInternal> }} hono wrapper
125127
*/
126128
declare function honoWrapper<
127129
RequestInternal extends IncomingMessage = import("node:http").IncomingMessage,
@@ -130,7 +132,9 @@ declare function honoWrapper<
130132
compiler: Compiler | MultiCompiler,
131133
options?: Options<RequestInternal, ResponseInternal> | undefined,
132134
usePlugin?: boolean | undefined,
133-
): (ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void;
135+
): ((ctx: EXPECTED_ANY, next: EXPECTED_FUNCTION) => Promise<void> | void) & {
136+
devMiddleware: API<RequestInternal, ResponseInternal>;
137+
};
134138
type Schema = import("schema-utils/declarations/validate").Schema;
135139
type Compiler = import("webpack").Compiler;
136140
type MultiCompiler = import("webpack").MultiCompiler;

0 commit comments

Comments
 (0)