diff --git a/lib/Server.js b/lib/Server.js index 1a7b572d9b..ba85abd41e 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -31,6 +31,7 @@ const schema = require("./options.json"); /** @typedef {import("ipaddr.js").IPv4} IPv4 */ /** @typedef {import("ipaddr.js").IPv6} IPv6 */ /** @typedef {import("net").Socket} Socket */ +/** @typedef {import("http").Server} HTTPServer*/ /** @typedef {import("http").IncomingMessage} IncomingMessage */ /** @typedef {import("http").ServerResponse} ServerResponse */ /** @typedef {import("open").Options} OpenOptions */ @@ -101,9 +102,13 @@ const schema = require("./options.json"); * @property {false | WatchOptions} watch */ +/** + * @typedef {"http" | "https" | "spdy" | "http2" | string} ServerType + */ + /** * @typedef {Object} ServerConfiguration - * @property {"http" | "https" | "spdy" | string} [type] + * @property {ServerType} [type] * @property {ServerOptions} [options] */ @@ -195,8 +200,11 @@ const schema = require("./options.json"); * @typedef {{ name?: string, path?: string, middleware: MiddlewareHandler } | MiddlewareHandler } Middleware */ +/** @typedef {import("net").Server} BasicServer */ + /** - * @template {BasicApplication} [T=ExpressApplication] + * @template {BasicApplication} [A=ExpressApplication] + * @template {BasicServer} [S=import("http").Server] * @typedef {Object} Configuration * @property {boolean | string} [ipc] * @property {Host} [host] @@ -210,17 +218,16 @@ const schema = require("./options.json"); * @property {boolean | Record | BonjourOptions} [bonjour] * @property {string | string[] | WatchFiles | Array} [watchFiles] * @property {boolean | string | Static | Array} [static] - * @property {boolean | ServerOptions} [https] - * @property {"http" | "https" | "spdy" | string | ServerConfiguration} [server] - * @property {() => Promise} [app] + * @property {ServerType | ServerConfiguration} [server] + * @property {() => Promise} [app] * @property {boolean | "sockjs" | "ws" | string | WebSocketServerConfiguration} [webSocketServer] * @property {ProxyConfigArray} [proxy] * @property {boolean | string | Open | Array} [open] * @property {boolean} [setupExitSignals] * @property {boolean | ClientConfiguration} [client] * @property {Headers | ((req: Request, res: Response, context: DevMiddlewareContext) => Headers)} [headers] - * @property {(devServer: Server) => void} [onListening] - * @property {(middlewares: Middleware[], devServer: Server) => Middleware[]} [setupMiddlewares] + * @property {(devServer: Server) => void} [onListening] + * @property {(middlewares: Middleware[], devServer: Server) => Middleware[]} [setupMiddlewares] */ if (!process.env.WEBPACK_SERVE) { @@ -298,11 +305,12 @@ function useFn(route, fn) { */ /** - * @template {BasicApplication} [T=ExpressApplication] + * @template {BasicApplication} [A=ExpressApplication] + * @template {BasicServer} [S=HTTPServer] */ class Server { /** - * @param {Configuration} options + * @param {Configuration} options * @param {Compiler | MultiCompiler} compiler */ constructor(options = {}, compiler) { @@ -387,7 +395,7 @@ class Server { if (gatewayOrFamily === "v4" || gatewayOrFamily === "v6") { let host; - Object.values(os.networkInterfaces()) + const networks = Object.values(os.networkInterfaces()) .flatMap((networks) => networks ?? []) .filter((network) => { if (!network || !network.address) { @@ -411,14 +419,16 @@ class Server { } return network.address; - }) - .forEach((network) => { - host = network.address; - if (host.includes(":")) { - host = `[${host}]`; - } }); + for (const network of networks) { + host = network.address; + + if (host.includes(":")) { + host = `[${host}]`; + } + } + return host; } @@ -1096,30 +1106,26 @@ class Server { ? /** @type {ServerConfiguration} */ (options.server).type || "http" : "http", options: { - .../** @type {ServerOptions} */ (options.https), .../** @type {ServerConfiguration} */ (options.server || {}).options, }, }; + const serverOptions = /** @type {ServerOptions} */ (options.server.options); + if ( options.server.type === "spdy" && - typeof (/** @type {ServerOptions} */ (options.server.options).spdy) === - "undefined" + typeof serverOptions.spdy === "undefined" ) { - /** @type {ServerOptions} */ - (options.server.options).spdy = { - protocols: ["h2", "http/1.1"], - }; + serverOptions.spdy = { protocols: ["h2", "http/1.1"] }; } - if (options.server.type === "https" || options.server.type === "spdy") { - if ( - typeof ( - /** @type {ServerOptions} */ (options.server.options).requestCert - ) === "undefined" - ) { - /** @type {ServerOptions} */ - (options.server.options).requestCert = false; + if ( + options.server.type === "https" || + options.server.type === "http2" || + options.server.type === "spdy" + ) { + if (typeof serverOptions.requestCert === "undefined") { + serverOptions.requestCert = false; } const httpsProperties = @@ -1127,19 +1133,13 @@ class Server { (["ca", "cert", "crl", "key", "pfx"]); for (const property of httpsProperties) { - if ( - typeof ( - /** @type {ServerOptions} */ (options.server.options)[property] - ) === "undefined" - ) { + if (typeof serverOptions[property] === "undefined") { // eslint-disable-next-line no-continue continue; } /** @type {any} */ - const value = - /** @type {ServerOptions} */ - (options.server.options)[property]; + const value = serverOptions[property]; /** * @param {string | Buffer | undefined} item * @returns {string | Buffer | undefined} @@ -1167,17 +1167,14 @@ class Server { }; /** @type {any} */ - (options.server.options)[property] = Array.isArray(value) + (serverOptions)[property] = Array.isArray(value) ? value.map((item) => readFile(item)) : readFile(value); } let fakeCert; - if ( - !(/** @type {ServerOptions} */ (options.server.options).key) || - !(/** @type {ServerOptions} */ (options.server.options).cert) - ) { + if (!serverOptions.key || !serverOptions.cert) { const certificateDir = Server.findCacheDir(); const certificatePath = path.join(certificateDir, "server.pem"); let certificateExists; @@ -1290,14 +1287,8 @@ class Server { this.logger.info(`SSL certificate: ${certificatePath}`); } - /** @type {ServerOptions} */ - (options.server.options).key = - /** @type {ServerOptions} */ - (options.server.options).key || fakeCert; - /** @type {ServerOptions} */ - (options.server.options).cert = - /** @type {ServerOptions} */ - (options.server.options).cert || fakeCert; + serverOptions.key = serverOptions.key || fakeCert; + serverOptions.cert = serverOptions.cert || fakeCert; } if (typeof options.ipc === "boolean") { @@ -1359,15 +1350,15 @@ class Server { */ const result = []; - options.open.forEach((item) => { + for (const item of options.open) { if (typeof item === "string") { result.push({ target: item, options: defaultOpenOptions }); - - return; + // eslint-disable-next-line no-continue + continue; } result.push(...getOpenItemsFromObject(item)); - }); + } /** @type {NormalizedOpen[]} */ (options.open) = result; @@ -1600,8 +1591,9 @@ class Server { } /** + * @template T * @private - * @returns {string} + * @returns {T} */ getServerTransport() { let implementation; @@ -1631,9 +1623,8 @@ class Server { try { // eslint-disable-next-line import/no-dynamic-require implementation = require( - /** @type {WebSocketServerConfiguration} */ ( - this.options.webSocketServer - ).type, + /** @type {WebSocketServerConfiguration} */ + (this.options.webSocketServer).type, ); } catch (error) { implementationFound = false; @@ -1641,9 +1632,9 @@ class Server { } break; case "function": - implementation = /** @type {WebSocketServerConfiguration} */ ( - this.options.webSocketServer - ).type; + implementation = + /** @type {WebSocketServerConfiguration} */ + (this.options.webSocketServer).type; break; default: implementationFound = false; @@ -1714,7 +1705,7 @@ class Server { /** @type {MultiCompiler} */ (this.compiler).compilers || [this.compiler]; - compilers.forEach((compiler) => { + for (const compiler of compilers) { this.addAdditionalEntries(compiler); const webpack = compiler.webpack || require("webpack"); @@ -1739,7 +1730,7 @@ class Server { plugin.apply(compiler); } } - }); + } if ( this.options.client && @@ -1796,15 +1787,18 @@ class Server { // Proxy WebSocket without the initial http request // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade - /** @type {RequestHandler[]} */ - (this.webSocketProxies).forEach((webSocketProxy) => { - /** @type {import("http").Server} */ + const webSocketProxies = + /** @type {RequestHandler[]} */ + (this.webSocketProxies); + + for (const webSocketProxy of webSocketProxies) { + /** @type {S} */ (this.server).on( "upgrade", /** @type {RequestHandler & { upgrade: NonNullable }} */ (webSocketProxy).upgrade, ); - }, this); + } } /** @@ -1812,7 +1806,7 @@ class Server { * @returns {Promise} */ async setupApp() { - /** @type {T | undefined}*/ + /** @type {A | undefined}*/ this.app = typeof this.options.app === "function" ? await this.options.app() @@ -1871,13 +1865,14 @@ class Server { * @returns {void} */ setupHostHeaderCheck() { - /** @type {T} */ + /** @type {A} */ (this.app).use((req, res, next) => { + const headerName = req.headers[":authority"] ? ":authority" : "host"; if ( this.checkHeader( /** @type {{ [key: string]: string | undefined }} */ (req.headers), - "host", + headerName, ) ) { next(); @@ -1910,7 +1905,7 @@ class Server { setupBuiltInRoutes() { const { app, middleware } = this; - /** @type {T} */ + /** @type {A} */ (app).use("/__webpack_dev_server__/sockjs.bundle.js", (req, res, next) => { if (req.method !== "GET" && req.method !== "HEAD") { next(); @@ -1952,7 +1947,7 @@ class Server { fs.createReadStream(clientPath).pipe(res); }); - /** @type {T} */ + /** @type {A} */ (app).use("/webpack-dev-server/invalidate", (req, res, next) => { if (req.method !== "GET" && req.method !== "HEAD") { next(); @@ -1964,7 +1959,7 @@ class Server { res.end(); }); - /** @type {T} */ + /** @type {A} */ (app).use("/webpack-dev-server/open-editor", (req, res, next) => { if (req.method !== "GET" && req.method !== "HEAD") { next(); @@ -1990,7 +1985,7 @@ class Server { res.end(); }); - /** @type {T} */ + /** @type {A} */ (app).use("/webpack-dev-server", (req, res, next) => { if (req.method !== "GET" && req.method !== "HEAD") { next(); @@ -2011,17 +2006,18 @@ class Server { '', ); + /** + * @type {StatsCompilation[]} + */ const statsForPrint = typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined" - ? /** @type {MultiStats} */ (stats).toJson().children + ? /** @type {NonNullable} */ + (/** @type {MultiStats} */ (stats).toJson().children) : [/** @type {Stats} */ (stats).toJson()]; res.write(`

Assets Report:

`); - /** - * @type {StatsCompilation[]} - */ - (statsForPrint).forEach((item, index) => { + for (const [index, item] of statsForPrint.entries()) { res.write("
"); const name = @@ -2036,10 +2032,11 @@ class Server { res.write("
    "); const publicPath = item.publicPath === "auto" ? "" : item.publicPath; + const assets = + /** @type {NonNullable} */ + (item.assets); - for (const asset of /** @type {NonNullable} */ ( - item.assets - )) { + for (const asset of assets) { const assetName = asset.name; const assetURL = `${publicPath}${assetName}`; @@ -2052,7 +2049,7 @@ class Server { res.write("
"); res.write("
"); - }); + } res.end(""); }); @@ -2064,13 +2061,14 @@ class Server { * @returns {void} */ setupWatchStaticFiles() { - if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) { - /** @type {NormalizedStatic[]} */ - (this.options.static).forEach((staticOption) => { - if (staticOption.watch) { - this.watchFiles(staticOption.directory, staticOption.watch); + const watchFiles = /** @type {NormalizedStatic[]} */ (this.options.static); + + if (watchFiles.length > 0) { + for (const item of watchFiles) { + if (item.watch) { + this.watchFiles(item.directory, item.watch); } - }); + } } } @@ -2079,13 +2077,12 @@ class Server { * @returns {void} */ setupWatchFiles() { - const { watchFiles } = this.options; + const watchFiles = /** @type {WatchFiles[]} */ (this.options.watchFiles); - if (/** @type {WatchFiles[]} */ (watchFiles).length > 0) { - /** @type {WatchFiles[]} */ - (watchFiles).forEach((item) => { + if (watchFiles.length > 0) { + for (const item of watchFiles) { this.watchFiles(item.paths, item.options); - }); + } } } @@ -2099,8 +2096,30 @@ class Server { */ let middlewares = []; + const isHTTP2 = + /** @type {ServerConfiguration} */ (this.options.server).type === "http2"; + + if (isHTTP2) { + // TODO patch for https://github.com/pillarjs/finalhandler/pull/45, need remove then will be resolved + middlewares.push({ + name: "http2-status-message-patch", + middleware: + /** @type {NextHandleFunction} */ + (_req, res, next) => { + Object.defineProperty(res, "statusMessage", { + get() { + return ""; + }, + set() {}, + }); + + next(); + }, + }); + } + // compress is placed last and uses unshift so that it will be the first middleware used - if (this.options.compress) { + if (this.options.compress && !isHTTP2) { const compression = require("compression"); middlewares.push({ name: "compression", middleware: compression() }); @@ -2278,10 +2297,13 @@ class Server { }); } - if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) { + const staticOptions = /** @type {NormalizedStatic[]} */ - (this.options.static).forEach((staticOption) => { - staticOption.publicPath.forEach((publicPath) => { + (this.options.static); + + if (staticOptions.length > 0) { + for (const staticOption of staticOptions) { + for (const publicPath of staticOption.publicPath) { middlewares.push({ name: "express-static", path: publicPath, @@ -2290,8 +2312,8 @@ class Server { staticOption.staticOptions, ), }); - }); - }); + } + } } if (this.options.historyApiFallback) { @@ -2333,10 +2355,9 @@ class Server { (this.middleware), }); - if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) { - /** @type {NormalizedStatic[]} */ - (this.options.static).forEach((staticOption) => { - staticOption.publicPath.forEach((publicPath) => { + if (staticOptions.length > 0) { + for (const staticOption of staticOptions) { + for (const publicPath of staticOption.publicPath) { middlewares.push({ name: "express-static", path: publicPath, @@ -2345,17 +2366,16 @@ class Server { staticOption.staticOptions, ), }); - }); - }); + } + } } } - if (/** @type {NormalizedStatic[]} */ (this.options.static).length > 0) { + if (staticOptions.length > 0) { const serveIndex = require("serve-index"); - /** @type {NormalizedStatic[]} */ - (this.options.static).forEach((staticOption) => { - staticOption.publicPath.forEach((publicPath) => { + for (const staticOption of staticOptions) { + for (const publicPath of staticOption.publicPath) { if (staticOption.serveIndex) { middlewares.push({ name: "serve-index", @@ -2380,8 +2400,8 @@ class Server { }, }); } - }); - }); + } + } } // Register this middleware always as the last one so that it's only used as a @@ -2409,28 +2429,28 @@ class Server { middlewares = this.options.setupMiddlewares(middlewares, this); } - middlewares.forEach((middleware) => { + for (const middleware of middlewares) { if (typeof middleware === "function") { - /** @type {T} */ + /** @type {A} */ (this.app).use( /** @type {NextHandleFunction | HandleFunction} */ (middleware), ); } else if (typeof middleware.path !== "undefined") { - /** @type {T} */ + /** @type {A} */ (this.app).use( middleware.path, /** @type {SimpleHandleFunction | NextHandleFunction} */ (middleware.middleware), ); } else { - /** @type {T} */ + /** @type {A} */ (this.app).use( /** @type {NextHandleFunction | HandleFunction} */ (middleware.middleware), ); } - }); + } } /** @@ -2438,18 +2458,22 @@ class Server { * @returns {void} */ createServer() { - const { type, options } = /** @type {ServerConfiguration} */ ( - this.options.server - ); + const { type, options } = + /** @type {ServerConfiguration} */ + (this.options.server); - /** @type {import("http").Server | undefined | null} */ // eslint-disable-next-line import/no-dynamic-require - this.server = require(/** @type {string} */ (type)).createServer( - options, - this.app, - ); - - /** @type {import("http").Server} */ + const serverType = require(/** @type {string} */ (type)); + /** @type {S | null | undefined}*/ + this.server = + type === "http2" + ? serverType.createSecureServer( + { ...options, allowHTTP1: true }, + this.app, + ) + : serverType.createServer(options, this.app); + + /** @type {S} */ (this.server).on( "connection", /** @@ -2466,7 +2490,7 @@ class Server { }, ); - /** @type {import("http").Server} */ + /** @type {S} */ (this.server).on( "error", /** @@ -2484,9 +2508,8 @@ class Server { */ createWebSocketServer() { /** @type {WebSocketServerImplementation | undefined | null} */ - this.webSocketServer = new /** @type {any} */ (this.getServerTransport())( - this, - ); + this.webSocketServer = new (this.getServerTransport())(this); + /** @type {WebSocketServerImplementation} */ (this.webSocketServer).implementation.on( "connection", @@ -2751,7 +2774,7 @@ class Server { if (this.options.ipc) { this.logger.info( `Project is running at: "${ - /** @type {import("http").Server} */ + /** @type {S} */ (this.server).address() }"`, ); @@ -2762,7 +2785,7 @@ class Server { const { address, port } = /** @type {import("net").AddressInfo} */ ( - /** @type {import("http").Server} */ + /** @type {S} */ (this.server).address() ); /** @@ -2946,6 +2969,8 @@ class Server { */ const allHeaders = []; + allHeaders.push({ key: "X_TEST", value: "TEST" }); + if (!Array.isArray(headers)) { // eslint-disable-next-line guard-for-in for (const name in headers) { @@ -2956,14 +2981,9 @@ class Server { headers = allHeaders; } - headers.forEach( - /** - * @param {{key: string, value: any}} header - */ - (header) => { - res.setHeader(header.key, header.value); - }, - ); + for (const { key, value } of headers) { + res.setHeader(key, value); + } } next(); @@ -3237,7 +3257,7 @@ class Server { await /** @type {Promise} */ ( new Promise((resolve) => { - /** @type {import("http").Server} */ + /** @type {S} */ (this.server).listen(listenOptions, () => { resolve(); }); @@ -3323,7 +3343,7 @@ class Server { if (this.server) { await /** @type {Promise} */ ( new Promise((resolve) => { - /** @type {import("http").Server} */ + /** @type {S} */ (this.server).close(() => { this.server = null; diff --git a/lib/options.json b/lib/options.json index 50e29376df..019731e26d 100644 --- a/lib/options.json +++ b/lib/options.json @@ -526,10 +526,10 @@ "description": "Allows to set server and options (by default 'http')." }, "ServerType": { - "enum": ["http", "https", "spdy"] + "enum": ["http", "https", "spdy", "http2"] }, "ServerEnum": { - "enum": ["http", "https", "spdy"], + "enum": ["http", "https", "spdy", "http2"], "cli": { "exclude": true } diff --git a/test/__snapshots__/validate-options.test.js.snap.webpack5 b/test/__snapshots__/validate-options.test.js.snap.webpack5 index 098192f990..6ce65c5be2 100644 --- a/test/__snapshots__/validate-options.test.js.snap.webpack5 +++ b/test/__snapshots__/validate-options.test.js.snap.webpack5 @@ -533,7 +533,7 @@ exports[`options validate should throw an error on the "server" option with '{"t exports[`options validate should throw an error on the "server" option with '{"type":"https","options":{"ca":true}}' value 1`] = ` "ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.server should be one of these: - "http" | "https" | "spdy" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | non-empty string | object { type?, options? } -> Allows to set server and options (by default 'http'). -> Read more at https://webpack.js.org/configuration/dev-server/#devserverserver Details: @@ -550,7 +550,7 @@ exports[`options validate should throw an error on the "server" option with '{"t exports[`options validate should throw an error on the "server" option with '{"type":"https","options":{"cert":true}}' value 1`] = ` "ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.server should be one of these: - "http" | "https" | "spdy" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | non-empty string | object { type?, options? } -> Allows to set server and options (by default 'http'). -> Read more at https://webpack.js.org/configuration/dev-server/#devserverserver Details: @@ -567,7 +567,7 @@ exports[`options validate should throw an error on the "server" option with '{"t exports[`options validate should throw an error on the "server" option with '{"type":"https","options":{"key":10}}' value 1`] = ` "ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.server should be one of these: - "http" | "https" | "spdy" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | non-empty string | object { type?, options? } -> Allows to set server and options (by default 'http'). -> Read more at https://webpack.js.org/configuration/dev-server/#devserverserver Details: @@ -590,7 +590,7 @@ exports[`options validate should throw an error on the "server" option with '{"t exports[`options validate should throw an error on the "server" option with '{"type":"https","options":{"pfx":10}}' value 1`] = ` "ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.server should be one of these: - "http" | "https" | "spdy" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | non-empty string | object { type?, options? } -> Allows to set server and options (by default 'http'). -> Read more at https://webpack.js.org/configuration/dev-server/#devserverserver Details: diff --git a/test/e2e/__snapshots__/app.test.js.snap.webpack5 b/test/e2e/__snapshots__/app.test.js.snap.webpack5 index 9ac3495c9b..7569392a9d 100644 --- a/test/e2e/__snapshots__/app.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/app.test.js.snap.webpack5 @@ -28,6 +28,34 @@ exports[`app option should work using "connect (async)" application and "http" s " `; +exports[`app option should work using "connect (async)" application and "http2" server should handle GET request to index route (/): console messages 1`] = ` +[ + "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", + "[HMR] Waiting for update signal from WDS...", + "Hey.", +] +`; + +exports[`app option should work using "connect (async)" application and "http2" server should handle GET request to index route (/): page errors 1`] = `[]`; + +exports[`app option should work using "connect (async)" application and "http2" server should handle GET request to index route (/): response status 1`] = `200`; + +exports[`app option should work using "connect (async)" application and "http2" server should handle GET request to index route (/): response text 1`] = ` +" + + + + + webpack-dev-server + + +

webpack-dev-server is running...

+ + + +" +`; + exports[`app option should work using "connect (async)" application and "https" server should handle GET request to index route (/): console messages 1`] = ` [ "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", @@ -112,6 +140,34 @@ exports[`app option should work using "connect" application and "http" server sh " `; +exports[`app option should work using "connect" application and "http2" server should handle GET request to index route (/): console messages 1`] = ` +[ + "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", + "[HMR] Waiting for update signal from WDS...", + "Hey.", +] +`; + +exports[`app option should work using "connect" application and "http2" server should handle GET request to index route (/): page errors 1`] = `[]`; + +exports[`app option should work using "connect" application and "http2" server should handle GET request to index route (/): response status 1`] = `200`; + +exports[`app option should work using "connect" application and "http2" server should handle GET request to index route (/): response text 1`] = ` +" + + + + + webpack-dev-server + + +

webpack-dev-server is running...

+ + + +" +`; + exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): console messages 1`] = ` [ "[webpack-dev-server] Server started: Hot Module Replacement enabled, Live Reloading enabled, Progress disabled, Overlay enabled.", diff --git a/test/e2e/app.test.js b/test/e2e/app.test.js index 05e6c23a79..c7a73f4f77 100644 --- a/test/e2e/app.test.js +++ b/test/e2e/app.test.js @@ -15,14 +15,19 @@ const staticDirectory = path.resolve( const apps = [ ["express", () => require("express")()], ["connect", () => require("connect")()], - ["connect (async)", async () => require("express")()], + ["connect (async)", () => require("connect")()], ]; -const servers = ["http", "https", "spdy"]; +const servers = ["http", "https", "spdy", "http2"]; describe("app option", () => { for (const [appName, app] of apps) { for (const server of servers) { + if (appName === "express" && server === "http2") { + // eslint-disable-next-line no-continue + continue; + } + let compiler; let devServer; let page; @@ -82,9 +87,7 @@ describe("app option", () => { () => performance.getEntries()[0].nextHopProtocol, ); - const isSpdy = server === "spdy"; - - if (isSpdy) { + if (server === "spdy" || server === "http2") { expect(HTTPVersion).toEqual("h2"); } else { expect(HTTPVersion).toEqual("http/1.1"); diff --git a/test/fixtures/watch-files-config/webpack.config.js b/test/fixtures/watch-files-config/webpack.config.js index c31d06071d..2f64765a90 100644 --- a/test/fixtures/watch-files-config/webpack.config.js +++ b/test/fixtures/watch-files-config/webpack.config.js @@ -4,6 +4,7 @@ const HTMLGeneratorPlugin = require("../../helpers/html-generator-plugin"); module.exports = { mode: "development", + devtool: false, context: __dirname, stats: "none", entry: "./foo.js", diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts index 0f0ed815d0..05e3aec532 100644 --- a/types/lib/Server.d.ts +++ b/types/lib/Server.d.ts @@ -4,10 +4,15 @@ export = Server; * @property {typeof useFn} use */ /** - * @template {BasicApplication} [T=ExpressApplication] + * @template {BasicApplication} [A=ExpressApplication] + * @template {BasicServer} [S=HTTPServer] */ declare class Server< - T extends BasicApplication = import("express").Application, + A extends BasicApplication = import("express").Application, + S extends BasicServer = import("http").Server< + typeof import("http").IncomingMessage, + typeof import("http").ServerResponse + >, > { static get schema(): { title: string; @@ -1160,11 +1165,11 @@ declare class Server< */ private static isWebTarget; /** - * @param {Configuration} options + * @param {Configuration} options * @param {Compiler | MultiCompiler} compiler */ constructor( - options: Configuration | undefined, + options: Configuration | undefined, compiler: Compiler | MultiCompiler, ); compiler: import("webpack").Compiler | import("webpack").MultiCompiler; @@ -1172,7 +1177,7 @@ declare class Server< * @type {ReturnType} * */ logger: ReturnType; - options: Configuration; + options: Configuration; /** * @type {FSWatcher[]} */ @@ -1217,8 +1222,9 @@ declare class Server< */ private getClientTransport; /** + * @template T * @private - * @returns {string} + * @returns {T} */ private getServerTransport; /** @@ -1236,8 +1242,8 @@ declare class Server< * @returns {Promise} */ private setupApp; - /** @type {T | undefined}*/ - app: T | undefined; + /** @type {A | undefined}*/ + app: A | undefined; /** * @private * @param {Stats | MultiStats} statsObj @@ -1302,8 +1308,8 @@ declare class Server< * @returns {void} */ private createServer; - /** @type {import("http").Server | undefined | null} */ - server: import("http").Server | undefined | null; + /** @type {S | null | undefined}*/ + server: S | null | undefined; /** * @private * @returns {void} @@ -1426,6 +1432,7 @@ declare namespace Server { IPv4, IPv6, Socket, + HTTPServer, IncomingMessage, ServerResponse, OpenOptions, @@ -1449,6 +1456,7 @@ declare namespace Server { WatchFiles, Static, NormalizedStatic, + ServerType, ServerConfiguration, WebSocketServerConfiguration, ClientConnection, @@ -1466,6 +1474,7 @@ declare namespace Server { Headers, MiddlewareHandler, Middleware, + BasicServer, Configuration, BasicApplication, }; @@ -1496,6 +1505,7 @@ type ServeStaticOptions = import("serve-static").ServeStaticOptions; type IPv4 = import("ipaddr.js").IPv4; type IPv6 = import("ipaddr.js").IPv6; type Socket = import("net").Socket; +type HTTPServer = import("http").Server; type IncomingMessage = import("http").IncomingMessage; type ServerResponse = import("http").ServerResponse; type OpenOptions = import("open").Options; @@ -1579,6 +1589,7 @@ type NormalizedStatic = { staticOptions: ServeStaticOptions; watch: false | WatchOptions; }; +type ServerType = "http" | "https" | "spdy" | "http2" | string; type ServerConfiguration = { type?: string | undefined; options?: ServerOptions | undefined; @@ -1679,68 +1690,69 @@ type Middleware = middleware: MiddlewareHandler; } | MiddlewareHandler; -type Configuration = - { - ipc?: string | boolean | undefined; - host?: string | undefined; - port?: Port | undefined; - hot?: boolean | "only" | undefined; - liveReload?: boolean | undefined; - devMiddleware?: - | DevMiddlewareOptions< - import("express").Request< - import("express-serve-static-core").ParamsDictionary, - any, - any, - qs.ParsedQs, - Record - >, - import("express").Response> - > - | undefined; - compress?: boolean | undefined; - allowedHosts?: string | string[] | undefined; - historyApiFallback?: - | boolean - | import("connect-history-api-fallback").Options - | undefined; - bonjour?: - | boolean - | Record - | import("bonjour-service").Service - | undefined; - watchFiles?: - | string - | string[] - | WatchFiles - | (string | WatchFiles)[] - | undefined; - static?: string | boolean | Static | (string | Static)[] | undefined; - https?: boolean | ServerOptions | undefined; - server?: string | ServerConfiguration | undefined; - app?: (() => Promise) | undefined; - webSocketServer?: - | string - | boolean - | WebSocketServerConfiguration - | undefined; - proxy?: ProxyConfigArray | undefined; - open?: string | boolean | Open | (string | Open)[] | undefined; - setupExitSignals?: boolean | undefined; - client?: boolean | ClientConfiguration | undefined; - headers?: - | Headers - | (( - req: Request, - res: Response, - context: DevMiddlewareContext, - ) => Headers) - | undefined; - onListening?: ((devServer: Server) => void) | undefined; - setupMiddlewares?: - | ((middlewares: Middleware[], devServer: Server) => Middleware[]) - | undefined; - }; +type BasicServer = import("net").Server; +type Configuration< + A extends BasicApplication = import("express").Application, + S extends BasicServer = import("http").Server< + typeof import("http").IncomingMessage, + typeof import("http").ServerResponse + >, +> = { + ipc?: string | boolean | undefined; + host?: string | undefined; + port?: Port | undefined; + hot?: boolean | "only" | undefined; + liveReload?: boolean | undefined; + devMiddleware?: + | DevMiddlewareOptions< + import("express").Request< + import("express-serve-static-core").ParamsDictionary, + any, + any, + qs.ParsedQs, + Record + >, + import("express").Response> + > + | undefined; + compress?: boolean | undefined; + allowedHosts?: string | string[] | undefined; + historyApiFallback?: + | boolean + | import("connect-history-api-fallback").Options + | undefined; + bonjour?: + | boolean + | Record + | import("bonjour-service").Service + | undefined; + watchFiles?: + | string + | string[] + | WatchFiles + | (string | WatchFiles)[] + | undefined; + static?: string | boolean | Static | (string | Static)[] | undefined; + server?: string | ServerConfiguration | undefined; + app?: (() => Promise
) | undefined; + webSocketServer?: string | boolean | WebSocketServerConfiguration | undefined; + proxy?: ProxyConfigArray | undefined; + open?: string | boolean | Open | (string | Open)[] | undefined; + setupExitSignals?: boolean | undefined; + client?: boolean | ClientConfiguration | undefined; + headers?: + | Headers + | (( + req: Request, + res: Response, + context: DevMiddlewareContext, + ) => Headers) + | undefined; + onListening?: ((devServer: Server) => void) | undefined; + setupMiddlewares?: + | ((middlewares: Middleware[], devServer: Server) => Middleware[]) + | undefined; +}; type BasicApplication = { use: typeof useFn; };