diff --git a/.cspell.json b/.cspell.json index 21a4aa2ed5..75ccb46d8d 100644 --- a/.cspell.json +++ b/.cspell.json @@ -67,7 +67,9 @@ "subsubcomain", "noselect", "commitlint", - "eslintcache" + "eslintcache", + "hono", + "privkey" ], "ignorePaths": [ "CHANGELOG.md", diff --git a/examples/app/connect/README.md b/examples/app/connect/README.md new file mode 100644 index 0000000000..b2074b1718 --- /dev/null +++ b/examples/app/connect/README.md @@ -0,0 +1,23 @@ +# `app` Option + +Serve using [`connect`](https://github.com/senchalabs/connect) as an application. + +**webpack.config.js** + +```js +const connect = require("connect"); + +module.exports = { + // ... + devServer: { + server: { + app: () => connect(), + }, + }, +}; +``` + +## What Should Happen + +1. The script should open `https://localhost:8080/` in your default browser. +2. You should see the text on the page itself change to read `Success!`. diff --git a/examples/app/connect/app.js b/examples/app/connect/app.js new file mode 100644 index 0000000000..51cf4a396b --- /dev/null +++ b/examples/app/connect/app.js @@ -0,0 +1,6 @@ +"use strict"; + +const target = document.querySelector("#target"); + +target.classList.add("pass"); +target.innerHTML = "Success!"; diff --git a/examples/app/connect/webpack.config.js b/examples/app/connect/webpack.config.js new file mode 100644 index 0000000000..0b7dd7bc23 --- /dev/null +++ b/examples/app/connect/webpack.config.js @@ -0,0 +1,14 @@ +"use strict"; + +// our setup function adds behind-the-scenes bits to the config that all of our +// examples need +const connect = require("connect"); +const { setup } = require("../../util"); + +module.exports = setup({ + context: __dirname, + entry: "./app.js", + devServer: { + app: () => connect(), + }, +}); diff --git a/examples/app/hono/README.md b/examples/app/hono/README.md new file mode 100644 index 0000000000..54b9cbe199 --- /dev/null +++ b/examples/app/hono/README.md @@ -0,0 +1,23 @@ +# `app` Option + +Serve using [`hono`](https://github.com/honojs/hono) as an application. + +**webpack.config.js** + +```js +const connect = require("connect"); + +module.exports = { + // ... + devServer: { + server: { + app: () => connect(), + }, + }, +}; +``` + +## What Should Happen + +1. The script should open `https://localhost:8080/` in your default browser. +2. You should see the text on the page itself change to read `Success!`. diff --git a/examples/app/hono/app.js b/examples/app/hono/app.js new file mode 100644 index 0000000000..51cf4a396b --- /dev/null +++ b/examples/app/hono/app.js @@ -0,0 +1,6 @@ +"use strict"; + +const target = document.querySelector("#target"); + +target.classList.add("pass"); +target.innerHTML = "Success!"; diff --git a/examples/app/hono/ssl/localhost-cert.pem b/examples/app/hono/ssl/localhost-cert.pem new file mode 100644 index 0000000000..c8453042dc --- /dev/null +++ b/examples/app/hono/ssl/localhost-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUevWiuCfenWuq9KyC8aQ/tc1Io14wDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDQyNDE2MDYyMloXDTI0MDUy +NDE2MDYyMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA1v/lb9u9WkqkF7zjIKe2R+b4S0sQnWIfBFZ0ggtaOL0a +ntud/EuaGQgLtJgSwO2M2xIqKx+yoLhoM+273EJe0KmfJMxYNAkhwP9h6vrKnaQJ +mpAhoalfEGyCrnHHMKISAAn4Rlc8NXnULoFhHzNm8bdqvP33rCmsJ+tNYC5kwzyt +HvRNFyg9BOUfACiPW17opFH0rao3IfZrQ6yRbknef1pX1x2pbDAH14rCT/vXaTs6 +VGuqLE/wRsSt+7nMHy/PmXxMyb4G4/UflYtnKfmXpDRw+TDEGzvTZedtoOz+rrJC +e989R9qYGrlPfyfZbI+O348FV66I+jcD+/EUQs+HkwIDAQABo1MwUTAdBgNVHQ4E +FgQU6bk4LSwtVQEt7V/ev+Zj270zdAkwHwYDVR0jBBgwFoAU6bk4LSwtVQEt7V/e +v+Zj270zdAkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAUBgo +E3CZrrc/MaadFg1meNk+eKACmTsIa5cT6zi7MsvoKakXEd4bGd+iLifUzlAa1ygj +dQppfprb5t68I7oO9/lkh2DfKrXxW/RpdhB05KslUd8q/3XY5kyao5quzeiVoMHR +u+XYjoy2mTwdUC2uzFy6rkHsAkJy2vJJoDdlNsrKn6AZmh+voHHKrAtOL4gnanQV +wR1u8eBVfk2MKIl2pNSCA4bD16uZyp3+oqq097BEoVa1pR+l8nwbsh/YfALifq/d +P3yiN5+EqgiOIF9b8PZORe+Ry1O7uvPnU2ZRkVWPJ1S17Ms0lnr7IY3qjSBTuK66 +5uYi7ojrb5Vf0UL5oQ== +-----END CERTIFICATE----- diff --git a/examples/app/hono/ssl/localhost-privkey.pem b/examples/app/hono/ssl/localhost-privkey.pem new file mode 100644 index 0000000000..c260882984 --- /dev/null +++ b/examples/app/hono/ssl/localhost-privkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQDW/+Vv271aSqQX +vOMgp7ZH5vhLSxCdYh8EVnSCC1o4vRqe2538S5oZCAu0mBLA7YzbEiorH7KguGgz +7bvcQl7QqZ8kzFg0CSHA/2Hq+sqdpAmakCGhqV8QbIKucccwohIACfhGVzw1edQu +gWEfM2bxt2q8/fesKawn601gLmTDPK0e9E0XKD0E5R8AKI9bXuikUfStqjch9mtD +rJFuSd5/WlfXHalsMAfXisJP+9dpOzpUa6osT/BGxK37ucwfL8+ZfEzJvgbj9R+V +i2cp+ZekNHD5MMQbO9Nl522g7P6uskJ73z1H2pgauU9/J9lsj47fjwVXroj6NwP7 +8RRCz4eTAgMBAAECggEAA+zbFv43iEj5kvdfXC7DrK9iVBmUPZNXhqA/c0paxNNr +A4B182+76f4UHKF0IjKUEkHUJEJpY/bJ7DzIY76QdZXLMoRKjfSmuZvQAVa/0T33 +8Or1ujpZ4nZgsmegX9ptorOL5VjdYAqP3aN+DvBEzl/vYnDujyWZn4bzvDBMpaXS +39qW1MkcZ8UiP1fRad76+S57WnieBV+NRHYEAiDdMFKXLuw/igX/xOSZgq5Jh3I2 +hLS49S41dN1P9l9H2bPMw0CthNvMPPaemwKHz+84hSS+P4VJOWJzlGnXEdIFuqBR +GFBESQzcemfS9DDB22Yt06YujBCbwTVVAxj73lnKkQKBgQDvYXK36J9y/NphDAWi +Cwti5oE3eSfV0YazQwm+rRwC64wbpBFAm9ujwjUmaYBg75lBLF5nOOe8s1n95g5I +tLfFb+zuZh8NNLjhfNE9/SNmRnnMvbcaDHeIE2RMAz+PuLN/gFLmsVIwK2X1LRC2 +0vHjw9Yzh6JLiOajAchzhZiCEQKBgQDl7R6Wfggo8myETA8Uv5tWot3IcquRkEl/ +TRCyao2/79rAGexS7piwD7FPdSDOk1zfZFYUOMzyMjj60sGcPRPqRX6D0usEODLQ +TwsTJSCNgPnIOkqKkccwtqlTimbRIrPUSQfFPj56RzKKWdrJ/P3LPRjzkK7i3vLV +EGlAENaLYwKBgHKSOnzpWr+HY+IFBgErthRs7LWnSDifYxATauuXIQwIvvNP0G4S +6snzHss2vZonszstSDWxV8DKOq052eZUkIxv6H+l4wDIFiDeQ6uep73Ax3UF7EgM +ZX18gombGGXqagcBXSxK/GJPsynomtJWHi38Ql5BcZ0jdffY157q9zZxAoGAPZtD +Tt+GIDKUkP4wLEcKwDPzaPoQrngSuWFUz/ls8bi6zC4l/DKiBsqtn7Sqja8+ezzP +M6vkfiCm084UwmA7LdJhC8E/52mHc/k55m9UQZYFV3kG8AoPbSYESLYUxoSd2ouW +4WrEIs9g42EgFm8LMaG1Rc3GjlNejWhQSzI3yjECf3v7VoAcUwVfuVkwbm9W24vR +neFTF8QBl//fxIdxZwoj5SrSgMOjmZ3pXA/ZbFJ0pB4Rh5dmKTYqdpfXsOTiBuwB +XlqPVpN8UZEl3edpufLDyPldNej/9kEAkK5FS3YVyIQEg75739bCTlfzzCX1HdMx +q98XYm/n5LWYFezsAt0= +-----END PRIVATE KEY----- diff --git a/examples/app/hono/webpack.config.js b/examples/app/hono/webpack.config.js new file mode 100644 index 0000000000..cc11a8ce46 --- /dev/null +++ b/examples/app/hono/webpack.config.js @@ -0,0 +1,55 @@ +"use strict"; + +const wdm = require("webpack-dev-middleware"); +const { Hono } = require("hono"); +const { serve } = require("@hono/node-server"); +// eslint-disable-next-line import/extensions, import/no-unresolved +const { serveStatic } = require("@hono/node-server/serve-static"); +const { setup } = require("../../util"); + +// our setup function adds behind-the-scenes bits to the config that all of our +// examples need +module.exports = setup({ + context: __dirname, + entry: "./app.js", + devServer: { + // WARNING: + // + // You always need to set up middlewares which you required for your app, + // built-in middlewares (like `history-api-fallback`/etc) doesn't work by default with `hono` + setupMiddlewares: (_, devServer) => [ + { + name: "webpack-dev-middleware", + middleware: wdm.honoWrapper(devServer.compiler), + }, + { + name: "static", + path: "/.assets/*", + middleware: serveStatic({ + root: "../../.assets", + rewriteRequestPath: (item) => item.replace(/^\/\.assets\//, "/"), + }), + }, + ], + app: () => new Hono(), + server: (_, app) => + serve({ + fetch: app.fetch, + // + // Uncomment for `https` + // createServer: require('node:https').createServer, + // serverOptions: { + // key: fs.readFileSync("./ssl/localhost-privkey.pem"), + // cert: fs.readFileSync("./ssl/localhost-cert.pem"), + // }, + // + // Uncomment for `http2` + // createServer: require("node:http2").createSecureServer, + // serverOptions: { + // allowHTTP1: true, + // key: require("fs").readFileSync("./ssl/localhost-privkey.pem"), + // cert: require("fs").readFileSync("./ssl/localhost-cert.pem"), + // }, + }), + }, +}); diff --git a/examples/server/http2/README.md b/examples/server/http2/README.md new file mode 100644 index 0000000000..fd2ae94430 --- /dev/null +++ b/examples/server/http2/README.md @@ -0,0 +1,22 @@ +# HTTP2 server + +**webpack.config.js** + +```js +const connect = require("connect"); + +module.exports = { + // ... + devServer: { + server: { + server: "http2", + app: () => connect(), + }, + }, +}; +``` + +## What Should Happen + +1. The script should open `https://localhost:8080/` in your default browser. +2. You should see the text on the page itself change to read `Success!`. diff --git a/examples/server/http2/app.js b/examples/server/http2/app.js new file mode 100644 index 0000000000..51cf4a396b --- /dev/null +++ b/examples/server/http2/app.js @@ -0,0 +1,6 @@ +"use strict"; + +const target = document.querySelector("#target"); + +target.classList.add("pass"); +target.innerHTML = "Success!"; diff --git a/examples/server/http2/webpack.config.js b/examples/server/http2/webpack.config.js new file mode 100644 index 0000000000..4c29be511c --- /dev/null +++ b/examples/server/http2/webpack.config.js @@ -0,0 +1,16 @@ +"use strict"; + +// our setup function adds behind-the-scenes bits to the config that all of our +// examples need +const connect = require("connect"); +const { setup } = require("../../util"); + +module.exports = setup({ + context: __dirname, + entry: "./app.js", + devServer: { + server: "http2", + // Only `connect` supports `http2` + app: () => connect(), + }, +}); diff --git a/examples/util.js b/examples/util.js index d6f139e270..633939d890 100644 --- a/examples/util.js +++ b/examples/util.js @@ -4,6 +4,7 @@ const path = require("path"); const fs = require("graceful-fs"); +const mime = require("mime"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { marked } = require("marked"); @@ -25,9 +26,18 @@ module.exports = { const result = { ...defaults, ...config }; const onBeforeSetupMiddleware = ({ app }) => { - app.get("/.assets/*", (req, res) => { - const filename = path.join(__dirname, "/", req.path); - res.sendFile(filename); + app.use("/.assets/", (req, res, next) => { + if (req.method !== "GET" && req.method !== "HEAD") { + next(); + return; + } + + res.setHeader("Content-Type", mime.lookup(req.url)); + + const filename = path.join(__dirname, "/.assets/", req.url); + const stream = fs.createReadStream(filename); + + stream.pipe(res); }); }; const renderer = new marked.Renderer(); diff --git a/lib/Server.js b/lib/Server.js index ca16ec04d7..7958299bbf 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -103,12 +103,16 @@ const schema = require("./options.json"); */ /** - * @typedef {"http" | "https" | "spdy" | "http2" | string} ServerType + * @template {BasicApplication} [A=ExpressApplication] + * @template {BasicServer} [S=import("http").Server] + * @typedef {"http" | "https" | "spdy" | "http2" | string | function(ServerOptions, A): S} ServerType */ /** + * @template {BasicApplication} [A=ExpressApplication] + * @template {BasicServer} [S=import("http").Server] * @typedef {Object} ServerConfiguration - * @property {ServerType} [type] + * @property {ServerType} [type] * @property {ServerOptions} [options] */ @@ -197,10 +201,14 @@ const schema = require("./options.json"); */ /** - * @typedef {{ name?: string, path?: string, middleware: MiddlewareHandler } | MiddlewareHandler } Middleware + * @typedef {{ name?: string, path?: string, middleware: MiddlewareHandler }} MiddlewareObject */ -/** @typedef {import("net").Server} BasicServer */ +/** + * @typedef {MiddlewareObject | MiddlewareHandler } Middleware + */ + +/** @typedef {import("net").Server | import("tls").Server} BasicServer */ /** * @template {BasicApplication} [A=ExpressApplication] @@ -218,14 +226,14 @@ const schema = require("./options.json"); * @property {boolean | Record | BonjourOptions} [bonjour] * @property {string | string[] | WatchFiles | Array} [watchFiles] * @property {boolean | string | Static | Array} [static] - * @property {ServerType | ServerConfiguration} [server] + * @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 {Headers | ((req: Request, res: Response, context: DevMiddlewareContext | undefined) => Headers)} [headers] * @property {(devServer: Server) => void} [onListening] * @property {(middlewares: Middleware[], devServer: Server) => Middleware[]} [setupMiddlewares] */ @@ -641,9 +649,7 @@ class Server { if (typeof webSocketURL.protocol !== "undefined") { protocol = webSocketURL.protocol; } else { - protocol = - /** @type {ServerConfiguration} */ - (this.options.server).type === "http" ? "ws:" : "wss:"; + protocol = this.isTlsServer ? "wss:" : "ws:"; } searchParams.set("protocol", protocol); @@ -1111,18 +1117,24 @@ class Server { ? options.hot : true; - options.server = { - type: - // eslint-disable-next-line no-nested-ternary - typeof options.server === "string" - ? options.server - : typeof (options.server || {}).type === "string" - ? /** @type {ServerConfiguration} */ (options.server).type || "http" - : "http", - options: { - .../** @type {ServerConfiguration} */ (options.server || {}).options, - }, - }; + if ( + typeof options.server === "function" || + typeof options.server === "string" + ) { + options.server = { + type: options.server, + options: {}, + }; + } else { + const serverOptions = + /** @type {ServerConfiguration} */ + (options.server || {}); + + options.server = { + type: serverOptions.type || "http", + options: { ...serverOptions.options }, + }; + } const serverOptions = /** @type {ServerOptions} */ (options.server.options); @@ -1220,7 +1232,6 @@ class Server { if (!certificateExists) { this.logger.info("Generating SSL certificate..."); - // @ts-ignore const selfsigned = require("selfsigned"); const attributes = [{ name: "commonName", value: "localhost" }]; const pems = selfsigned.generate(attributes, { @@ -1714,6 +1725,11 @@ class Server { * @returns {Promise} */ async initialize() { + this.setupHooks(); + + await this.setupApp(); + await this.createServer(); + if (this.options.webSocketServer) { const compilers = /** @type {MultiCompiler} */ @@ -1759,16 +1775,9 @@ class Server { } } - this.setupHooks(); - await this.setupApp(); - this.setupHostHeaderCheck(); - this.setupDevMiddleware(); - // Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response - this.setupBuiltInRoutes(); this.setupWatchFiles(); this.setupWatchStaticFiles(); this.setupMiddlewares(); - this.createServer(); if (this.options.setupExitSignals) { const signals = ["SIGINT", "SIGTERM"]; @@ -1879,202 +1888,6 @@ class Server { ); } - /** - * @private - * @returns {void} - */ - setupHostHeaderCheck() { - /** @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), - headerName, - ) - ) { - next(); - return; - } - - res.statusCode = 403; - res.end("Invalid Host header"); - }); - } - - /** - * @private - * @returns {void} - */ - setupDevMiddleware() { - const webpackDevMiddleware = require("webpack-dev-middleware"); - - // middleware for serving webpack bundle - this.middleware = webpackDevMiddleware( - this.compiler, - this.options.devMiddleware, - ); - } - - /** - * @private - * @returns {void} - */ - setupBuiltInRoutes() { - const { app, middleware } = this; - - /** @type {A} */ - (app).use("/__webpack_dev_server__/sockjs.bundle.js", (req, res, next) => { - if (req.method !== "GET" && req.method !== "HEAD") { - next(); - return; - } - - const clientPath = path.join( - __dirname, - "..", - "client/modules/sockjs-client/index.js", - ); - - // Express send Etag and other headers by default, so let's keep them for compatibility reasons - // @ts-ignore - if (typeof res.sendFile === "function") { - // @ts-ignore - res.sendFile(clientPath); - return; - } - - let stats; - - try { - // TODO implement `inputFileSystem.createReadStream` in webpack - stats = fs.statSync(clientPath); - } catch (err) { - next(); - return; - } - - res.setHeader("Content-Type", "application/javascript; charset=UTF-8"); - res.setHeader("Content-Length", stats.size); - - if (req.method === "HEAD") { - res.end(); - return; - } - - fs.createReadStream(clientPath).pipe(res); - }); - - /** @type {A} */ - (app).use("/webpack-dev-server/invalidate", (req, res, next) => { - if (req.method !== "GET" && req.method !== "HEAD") { - next(); - return; - } - - this.invalidate(); - - res.end(); - }); - - /** @type {A} */ - (app).use("/webpack-dev-server/open-editor", (req, res, next) => { - if (req.method !== "GET" && req.method !== "HEAD") { - next(); - return; - } - - if (!req.url) { - next(); - return; - } - - const resolveUrl = new URL(req.url, `http://${req.headers.host}`); - const params = new URLSearchParams(resolveUrl.search); - const fileName = params.get("fileName"); - - if (typeof fileName === "string") { - // @ts-ignore - const launchEditor = require("launch-editor"); - - launchEditor(fileName); - } - - res.end(); - }); - - /** @type {A} */ - (app).use("/webpack-dev-server", (req, res, next) => { - if (req.method !== "GET" && req.method !== "HEAD") { - next(); - return; - } - - /** @type {import("webpack-dev-middleware").API}*/ - (middleware).waitUntilValid((stats) => { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - - // HEAD requests should not return body content - if (req.method === "HEAD") { - res.end(); - return; - } - - res.write( - '', - ); - - /** - * @type {StatsCompilation[]} - */ - const statsForPrint = - typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined" - ? /** @type {NonNullable} */ - (/** @type {MultiStats} */ (stats).toJson().children) - : [/** @type {Stats} */ (stats).toJson()]; - - res.write(`

Assets Report:

`); - - for (const [index, item] of statsForPrint.entries()) { - res.write("
"); - } - - res.end(""); - }); - }); - } - /** * @private * @returns {void} @@ -2115,8 +1928,34 @@ class Server { */ let middlewares = []; + // Register setup host header check for security + middlewares.push({ + name: "host-header-check", + /** + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {void} + */ + middleware: (req, res, next) => { + const headers = + /** @type {{ [key: string]: string | undefined }} */ + (req.headers); + const headerName = headers[":authority"] ? ":authority" : "host"; + + if (this.checkHeader(headers, headerName)) { + next(); + return; + } + + res.statusCode = 403; + res.end("Invalid Host header"); + }, + }); + const isHTTP2 = - /** @type {ServerConfiguration} */ (this.options.server).type === "http2"; + /** @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 @@ -2153,9 +1992,197 @@ class Server { middlewares.push({ name: "webpack-dev-middleware", - middleware: - /** @type {import("webpack-dev-middleware").Middleware}*/ - (this.middleware), + middleware: /** @type {MiddlewareHandler} */ (this.middleware), + }); + + // Should be after `webpack-dev-middleware`, otherwise other middlewares might rewrite response + middlewares.push({ + name: "webpack-dev-server-sockjs-bundle", + path: "/__webpack_dev_server__/sockjs.bundle.js", + /** + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {void} + */ + middleware: (req, res, next) => { + if (req.method !== "GET" && req.method !== "HEAD") { + next(); + return; + } + + const clientPath = path.join( + __dirname, + "..", + "client/modules/sockjs-client/index.js", + ); + + // Express send Etag and other headers by default, so let's keep them for compatibility reasons + if (typeof res.sendFile === "function") { + res.sendFile(clientPath); + return; + } + + let stats; + + try { + // TODO implement `inputFileSystem.createReadStream` in webpack + stats = fs.statSync(clientPath); + } catch (err) { + next(); + return; + } + + res.setHeader("Content-Type", "application/javascript; charset=UTF-8"); + res.setHeader("Content-Length", stats.size); + + if (req.method === "HEAD") { + res.end(); + return; + } + + fs.createReadStream(clientPath).pipe(res); + }, + }); + + middlewares.push({ + name: "webpack-dev-server-invalidate", + path: "/webpack-dev-server/invalidate", + /** + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {void} + */ + middleware: (req, res, next) => { + if (req.method !== "GET" && req.method !== "HEAD") { + next(); + return; + } + + this.invalidate(); + + res.end(); + }, + }); + + middlewares.push({ + name: "webpack-dev-server-open-editor", + path: "/webpack-dev-server/open-editor", + /** + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {void} + */ + middleware: (req, res, next) => { + if (req.method !== "GET" && req.method !== "HEAD") { + next(); + return; + } + + if (!req.url) { + next(); + return; + } + + const resolveUrl = new URL(req.url, `http://${req.headers.host}`); + const params = new URLSearchParams(resolveUrl.search); + const fileName = params.get("fileName"); + + if (typeof fileName === "string") { + // @ts-ignore + const launchEditor = require("launch-editor"); + + launchEditor(fileName); + } + + res.end(); + }, + }); + + middlewares.push({ + name: "webpack-dev-server-assets", + path: "/webpack-dev-server", + /** + * @param {Request} req + * @param {Response} res + * @param {NextFunction} next + * @returns {void} + */ + middleware: (req, res, next) => { + if (req.method !== "GET" && req.method !== "HEAD") { + next(); + return; + } + + if (!this.middleware) { + next(); + return; + } + + this.middleware.waitUntilValid((stats) => { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + + // HEAD requests should not return body content + if (req.method === "HEAD") { + res.end(); + return; + } + + res.write( + '', + ); + + /** + * @type {StatsCompilation[]} + */ + const statsForPrint = + typeof (/** @type {MultiStats} */ (stats).stats) !== "undefined" + ? /** @type {NonNullable} */ + (/** @type {MultiStats} */ (stats).toJson().children) + : [/** @type {Stats} */ (stats).toJson()]; + + res.write(`

Assets Report:

`); + + for (const [index, item] of statsForPrint.entries()) { + res.write("
"); + + const name = + // eslint-disable-next-line no-nested-ternary + typeof item.name !== "undefined" + ? item.name + : /** @type {MultiStats} */ (stats).stats + ? `unnamed[${index}]` + : "unnamed"; + + res.write(`

Compilation: ${name}

`); + res.write("
    "); + + const publicPath = + item.publicPath === "auto" ? "" : item.publicPath; + const assets = + /** @type {NonNullable} */ + (item.assets); + + for (const asset of assets) { + const assetName = asset.name; + const assetURL = `${publicPath}${assetName}`; + + res.write( + `
  • + ${assetName} +
  • `, + ); + } + + res.write("
"); + res.write("
"); + } + + res.end(""); + }); + }, }); if (this.options.proxy) { @@ -2293,6 +2320,7 @@ class Server { name: "http-proxy-middleware", middleware: handler, }); + // Also forward error requests to the proxy so it can handle them. middlewares.push({ name: "http-proxy-middleware-error-handler", @@ -2310,9 +2338,7 @@ class Server { middlewares.push({ name: "webpack-dev-middleware", - middleware: - /** @type {import("webpack-dev-middleware").Middleware}*/ - (this.middleware), + middleware: /** @type {MiddlewareHandler} */ (this.middleware), }); } @@ -2369,9 +2395,7 @@ class Server { // it is able to handle '/index.html' request after redirect middlewares.push({ name: "webpack-dev-middleware", - middleware: - /** @type {import("webpack-dev-middleware").Middleware}*/ - (this.middleware), + middleware: /** @type {MiddlewareHandler} */ (this.middleware), }); if (staticOptions.length > 0) { @@ -2448,6 +2472,32 @@ class Server { middlewares = this.options.setupMiddlewares(middlewares, this); } + // Lazy init webpack dev middleware + const lazyInitDevMiddleware = () => { + if (!this.middleware) { + const webpackDevMiddleware = require("webpack-dev-middleware"); + + // middleware for serving webpack bundle + /** @type {import("webpack-dev-middleware").API} */ + this.middleware = webpackDevMiddleware( + this.compiler, + this.options.devMiddleware, + ); + } + + return this.middleware; + }; + + for (const i of middlewares) { + if (i.name === "webpack-dev-middleware") { + const item = /** @type {MiddlewareObject} */ (i); + + if (typeof item.middleware === "undefined") { + item.middleware = lazyInitDevMiddleware(); + } + } + } + for (const middleware of middlewares) { if (typeof middleware === "function") { /** @type {A} */ @@ -2474,23 +2524,39 @@ class Server { /** * @private - * @returns {void} + * @returns {Promise} */ - createServer() { + async createServer() { const { type, options } = - /** @type {ServerConfiguration} */ + /** @type {ServerConfiguration} */ (this.options.server); - // eslint-disable-next-line import/no-dynamic-require - 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); + if (typeof type === "function") { + /** @type {S | undefined}*/ + this.server = await type( + /** @type {ServerOptions} */ + (options), + /** @type {A} */ + (this.app), + ); + } else { + // eslint-disable-next-line import/no-dynamic-require + const serverType = require(/** @type {string} */ (type)); + + /** @type {S | undefined}*/ + this.server = + type === "http2" + ? serverType.createSecureServer( + { ...options, allowHTTP1: true }, + this.app, + ) + : serverType.createServer(options, this.app); + } + + this.isTlsServer = + typeof ( + /** @type {import("tls").Server} */ (this.server).setSecureContext + ) !== "undefined"; /** @type {S} */ (this.server).on( @@ -2695,22 +2761,19 @@ class Server { */ runBonjour() { const { Bonjour } = require("bonjour-service"); + const type = this.isTlsServer ? "https" : "http"; + /** * @private * @type {Bonjour | undefined} */ this.bonjour = new Bonjour(); this.bonjour.publish({ - // @ts-expect-error name: `Webpack Dev Server ${os.hostname()}:${this.options.port}`, - // @ts-expect-error port: /** @type {number} */ (this.options.port), - // @ts-expect-error - type: - /** @type {ServerConfiguration} */ - (this.options.server).type === "http" ? "http" : "https", + type, subtypes: ["webpack"], - .../** @type {BonjourOptions} */ (this.options.bonjour), + .../** @type {Partial} */ (this.options.bonjour), }); } @@ -2790,23 +2853,15 @@ class Server { }; const useColor = getColorsOption(this.getCompilerOptions()); + const server = /** @type {S} */ (this.server); + if (this.options.ipc) { - this.logger.info( - `Project is running at: "${ - /** @type {S} */ - (this.server).address() - }"`, - ); + this.logger.info(`Project is running at: "${server.address()}"`); } else { - const protocol = - /** @type {ServerConfiguration} */ - (this.options.server).type === "http" ? "http" : "https"; + const protocol = this.isTlsServer ? "https" : "http"; const { address, port } = /** @type {import("net").AddressInfo} */ - ( - /** @type {S} */ - (this.server).address() - ); + (server.address()); /** * @param {string} newHostname * @returns {string} @@ -2814,7 +2869,7 @@ class Server { const prettyPrintURL = (newHostname) => url.format({ protocol, hostname: newHostname, port, pathname: "/" }); - let server; + let host; let localhost; let loopbackIPv4; let loopbackIPv6; @@ -2834,7 +2889,7 @@ class Server { } if (!isIP) { - server = prettyPrintURL(this.options.host); + host = prettyPrintURL(this.options.host); } } } @@ -2880,8 +2935,8 @@ class Server { this.logger.info("Project is running at:"); - if (server) { - this.logger.info(`Server: ${colors.info(useColor, server)}`); + if (host) { + this.logger.info(`Server: ${colors.info(useColor, host)}`); } if (localhost || loopbackIPv4 || loopbackIPv6) { @@ -2953,11 +3008,7 @@ class Server { if (this.options.bonjour) { const bonjourProtocol = /** @type {BonjourOptions} */ - (this.options.bonjour).type || - /** @type {ServerConfiguration} */ - (this.options.server).type === "http" - ? "http" - : "https"; + (this.options.bonjour).type || this.isTlsServer ? "https" : "http"; this.logger.info( `Broadcasting "${bonjourProtocol}" with subtype of "webpack" via ZeroConf DNS (Bonjour)`, @@ -2979,8 +3030,8 @@ class Server { headers = headers( req, res, - /** @type {import("webpack-dev-middleware").API}*/ - (this.middleware).context, + // eslint-disable-next-line no-undefined + this.middleware ? this.middleware.context : undefined, ); } @@ -3365,8 +3416,8 @@ class Server { new Promise((resolve) => { /** @type {S} */ (this.server).close(() => { - this.server = null; - + // eslint-disable-next-line no-undefined + this.server = undefined; resolve(); }); @@ -3385,7 +3436,6 @@ class Server { (this.middleware).close((error) => { if (error) { reject(error); - return; } @@ -3394,7 +3444,8 @@ class Server { }) ); - this.middleware = null; + // eslint-disable-next-line no-undefined + this.middleware = undefined; } } diff --git a/lib/options.json b/lib/options.json index 019731e26d..f5fa540624 100644 --- a/lib/options.json +++ b/lib/options.json @@ -515,6 +515,9 @@ { "$ref": "#/definitions/ServerEnum" }, + { + "$ref": "#/definitions/ServerFn" + }, { "$ref": "#/definitions/ServerString" }, @@ -528,6 +531,9 @@ "ServerType": { "enum": ["http", "https", "spdy", "http2"] }, + "ServerFn": { + "instanceof": "Function" + }, "ServerEnum": { "enum": ["http", "https", "spdy", "http2"], "cli": { @@ -551,6 +557,9 @@ }, { "$ref": "#/definitions/ServerString" + }, + { + "$ref": "#/definitions/ServerFn" } ] }, diff --git a/package-lock.json b/package-lock.json index 9af7190475..7065ff7b20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,24 +35,25 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.0", + "webpack-dev-middleware": "^7.4.2", "ws": "^8.18.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" }, "devDependencies": { - "@babel/cli": "^7.22.5", - "@babel/core": "^7.22.5", - "@babel/eslint-parser": "^7.22.5", - "@babel/plugin-transform-object-assign": "^7.22.5", - "@babel/plugin-transform-runtime": "^7.22.5", - "@babel/preset-env": "^7.22.5", - "@babel/runtime": "^7.22.5", - "@commitlint/cli": "^19.0.3", - "@commitlint/config-conventional": "^19.0.3", + "@babel/cli": "^7.25.6", + "@babel/core": "^7.25.2", + "@babel/eslint-parser": "^7.25.1", + "@babel/plugin-transform-object-assign": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.25.4", + "@babel/preset-env": "^7.25.4", + "@babel/runtime": "^7.25.6", + "@commitlint/cli": "^19.4.1", + "@commitlint/config-conventional": "^19.4.1", + "@hono/node-server": "^1.12.2", "@types/compression": "^1.7.2", - "@types/node": "^22.3.0", + "@types/node": "^22.5.2", "@types/node-forge": "^1.3.1", "@types/sockjs-client": "^1.5.1", "@types/trusted-types": "^2.0.2", @@ -61,28 +62,29 @@ "babel-loader": "^9.1.0", "body-parser": "^1.19.2", "connect": "^3.7.0", - "core-js": "^3.31.0", - "cspell": "^8.3.2", + "core-js": "^3.38.1", + "cspell": "^8.14.2", "css-loader": "^7.1.1", "eslint": "^8.43.0", "eslint-config-prettier": "^9.1.0", "eslint-config-webpack": "^1.2.5", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.30.0", "execa": "^5.1.1", + "hono": "^4.5.11", "html-webpack-plugin": "^5.5.3", "http-proxy": "^1.18.1", - "husky": "^9.1.3", + "husky": "^9.1.5", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "klona": "^2.0.4", "less": "^4.1.1", "less-loader": "^12.1.0", - "lint-staged": "^15.2.0", + "lint-staged": "^15.2.10", "marked": "^12.0.0", "memfs": "^4.6.0", "npm-run-all": "^4.1.5", "prettier": "^3.2.4", - "puppeteer": "^23.1.0", + "puppeteer": "^23.2.2", "readable-stream": "^4.5.2", "require-from-string": "^2.0.2", "rimraf": "^5.0.5", @@ -94,7 +96,7 @@ "tcp-port-used": "^1.0.2", "typescript": "^5.5.4", "wait-for-expect": "^3.0.2", - "webpack": "^5.91.0", + "webpack": "^5.94.0", "webpack-cli": "^5.0.1", "webpack-merge": "^6.0.1" }, @@ -131,9 +133,9 @@ } }, "node_modules/@babel/cli": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.24.8.tgz", - "integrity": "sha512-isdp+G6DpRyKc+3Gqxy2rjzgF7Zj9K0mzLNnxz+E/fgeag8qT3vVulX4gY9dGO1q0y+0lUv6V3a+uhUzMzrwXg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.25.6.tgz", + "integrity": "sha512-Z+Doemr4VtvSD2SNHTrkiFZ1LX+JI6tyRXAAOb4N9khIuPyoEPmTPJarPm8ljJV1D6bnMQjyHMWTT9NeKbQuXA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -153,7 +155,7 @@ }, "optionalDependencies": { "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", - "chokidar": "^3.4.0" + "chokidar": "^3.6.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -230,12 +232,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz", - "integrity": "sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -534,12 +536,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -1854,9 +1856,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", - "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1880,16 +1882,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1898,9 +1900,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -1918,13 +1920,13 @@ "dev": true }, "node_modules/@commitlint/cli": { - "version": "19.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.4.0.tgz", - "integrity": "sha512-sJX4J9UioVwZHq7JWM9tjT5bgWYaIN3rC4FP7YwfEwBYiIO+wMyRttRvQLNkow0vCdM0D67r9NEWU0Ui03I4Eg==", + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.4.1.tgz", + "integrity": "sha512-EerFVII3ZcnhXsDT9VePyIdCJoh3jEzygN1L37MjQXgPfGS6fJTWL/KHClVMod1d8w94lFC3l4Vh/y5ysVAz2A==", "dev": true, "dependencies": { "@commitlint/format": "^19.3.0", - "@commitlint/lint": "^19.2.2", + "@commitlint/lint": "^19.4.1", "@commitlint/load": "^19.4.0", "@commitlint/read": "^19.4.0", "@commitlint/types": "^19.0.3", @@ -2073,9 +2075,9 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz", - "integrity": "sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==", + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.4.1.tgz", + "integrity": "sha512-D5S5T7ilI5roybWGc8X35OBlRXLAwuTseH1ro0XgqkOWrhZU8yOwBOslrNmSDlTXhXLq8cnfhQyC42qaUCzlXA==", "dev": true, "dependencies": { "@commitlint/types": "^19.0.3", @@ -2175,14 +2177,14 @@ } }, "node_modules/@commitlint/lint": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.2.2.tgz", - "integrity": "sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==", + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.4.1.tgz", + "integrity": "sha512-Ws4YVAZ0jACTv6VThumITC1I5AG0UyXMGua3qcf55JmXIXm/ejfaVKykrqx7RyZOACKVAs8uDRIsEsi87JZ3+Q==", "dev": true, "dependencies": { "@commitlint/is-ignored": "^19.2.2", "@commitlint/parse": "^19.0.3", - "@commitlint/rules": "^19.0.3", + "@commitlint/rules": "^19.4.1", "@commitlint/types": "^19.0.3" }, "engines": { @@ -2413,9 +2415,9 @@ } }, "node_modules/@commitlint/rules": { - "version": "19.0.3", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.3.tgz", - "integrity": "sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==", + "version": "19.4.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.4.1.tgz", + "integrity": "sha512-AgctfzAONoVxmxOXRyxXIq7xEPrd7lK/60h2egp9bgGUMZK9v0+YqLOA+TH+KqCa63ZoCr8owP2YxoSSu7IgnQ==", "dev": true, "dependencies": { "@commitlint/ensure": "^19.0.3", @@ -2729,15 +2731,15 @@ "dev": true }, "node_modules/@cspell/dict-aws": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.3.tgz", - "integrity": "sha512-0C0RQ4EM29fH0tIYv+EgDQEum0QI6OrmjENC9u98pB8UcnYxGG/SqinuPxo+TgcEuInj0Q73MsBpJ1l5xUnrsw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.4.tgz", + "integrity": "sha512-6AWI/Kkf+RcX/J81VX8+GKLeTgHWEr/OMhGk3dHQzWK66RaqDJCGDqi7494ghZKcBB7dGa3U5jcKw2FZHL/u3w==", "dev": true }, "node_modules/@cspell/dict-bash": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.3.tgz", - "integrity": "sha512-tOdI3QVJDbQSwPjUkOiQFhYcu2eedmX/PtEpVWg0aFps/r6AyjUQINtTgpqMYnYuq8O1QUIQqnpx21aovcgZCw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.4.tgz", + "integrity": "sha512-W/AHoQcJYn3Vn/tUiXX2+6D/bhfzdDshwcbQWv9TdiNlXP9P6UJjDKWbxyA5ogJCsR2D0X9Kx11oV8E58siGKQ==", "dev": true }, "node_modules/@cspell/dict-companies": { @@ -2747,9 +2749,9 @@ "dev": true }, "node_modules/@cspell/dict-cpp": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.14.tgz", - "integrity": "sha512-DxmlkwDhfPvA2fcYHg46Ly84E3BWwKt2mxUZ41pdgqmzPXdsvoAXbTAgXsRHuHfoHzD6hB1xai9z2JYHeiMqKQ==", + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.16.tgz", + "integrity": "sha512-32fU5RkuOM55IRcxjByiSoKbjr+C4danDfYjHaQNRWdvjzJzci3fLDGA2wTXiclkgDODxGiV8LCTUwCz+3TNWA==", "dev": true }, "node_modules/@cspell/dict-cryptocurrencies": { @@ -2771,9 +2773,9 @@ "dev": true }, "node_modules/@cspell/dict-dart": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.3.tgz", - "integrity": "sha512-cLkwo1KT5CJY5N5RJVHks2genFkNCl/WLfj+0fFjqNR+tk3tBI1LY7ldr9piCtSFSm4x9pO1x6IV3kRUY1lLiw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.2.1.tgz", + "integrity": "sha512-yriKm7QkoPx3JPSSOcw6iX9gOb2N50bOo/wqWviqPYbhpMRh9Xiv6dkUy3+ot+21GuShZazO8X6U5+Vw67XEwg==", "dev": true }, "node_modules/@cspell/dict-data-science": { @@ -2795,9 +2797,9 @@ "dev": true }, "node_modules/@cspell/dict-dotnet": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.3.tgz", - "integrity": "sha512-q8+b8YWYv+9Q+AbU3mH/RHE9aovhCuGtMuNSsx+YnTofEhVQkJR3vdrYjhOBg3epIiZVUS83VP0vxPLPa+UTug==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.5.tgz", + "integrity": "sha512-gjg0L97ee146wX47dnA698cHm85e7EOpf9mVrJD8DmEaqoo/k1oPy2g7c7LgKxK9XnqwoXxhLNnngPrwXOoEtQ==", "dev": true }, "node_modules/@cspell/dict-elixir": { @@ -2861,9 +2863,9 @@ "dev": true }, "node_modules/@cspell/dict-golang": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.11.tgz", - "integrity": "sha512-BMFIDGh1HaFUe1cYBT1dotqyIQG2j3VkNntGQTBa/7i0aBnC5PBJDiAXnUeBHi0AVrz0hyAc7xtcK5KyKCEzwg==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.12.tgz", + "integrity": "sha512-LEPeoqd+4O+vceHF73S7D7+LYfrAjOvp4Dqzh4MT30ruzlQ77yHRSuYOJtrFN1GK5ntAt/ILSVOKg9sgsz1Llg==", "dev": true }, "node_modules/@cspell/dict-google": { @@ -2945,21 +2947,21 @@ "dev": true }, "node_modules/@cspell/dict-npm": { - "version": "5.0.18", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.18.tgz", - "integrity": "sha512-weMTyxWpzz19q4wv9n183BtFvdD5fCjtze+bFKpl+4rO/YlPhHL2cXLAeexJz/VDSBecwX4ybTZYoknd1h2J4w==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.4.tgz", + "integrity": "sha512-yzqVTY4P5neom4z9orV2IFOqDZ7fDotmisP7nwQkEmftoELgn5CUtNdnJhWDoDQQn6yrxOxA8jEqmyETIWzN4Q==", "dev": true }, "node_modules/@cspell/dict-php": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.8.tgz", - "integrity": "sha512-TBw3won4MCBQ2wdu7kvgOCR3dY2Tb+LJHgDUpuquy3WnzGiSDJ4AVelrZdE1xu7mjFJUr4q48aB21YT5uQqPZA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.10.tgz", + "integrity": "sha512-NfTZdp6kcZDF1PvgQ6cY0zE4FUO5rSwNmBH/iwCBuaLfJAFQ97rgjxo+D2bic4CFwNjyHutnHPtjJBRANO5XQw==", "dev": true }, "node_modules/@cspell/dict-powershell": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.5.tgz", - "integrity": "sha512-3JVyvMoDJesAATYGOxcUWPbQPUvpZmkinV3m8HL1w1RrjeMVXXuK7U1jhopSneBtLhkU+9HKFwgh9l9xL9mY2Q==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.8.tgz", + "integrity": "sha512-Eg64BccQp5oEJ+V/O2G27KaLWmuOL2AWMOs2470adUihOleRfW8j9XwAEGCS+JKSnDb2mksWA72Z6kDqH138IQ==", "dev": true }, "node_modules/@cspell/dict-public-licenses": { @@ -2969,9 +2971,9 @@ "dev": true }, "node_modules/@cspell/dict-python": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.4.tgz", - "integrity": "sha512-sCtLBqMreb+8zRW2bXvFsfSnRUVU6IFm4mT6Dc4xbz0YajprbaPPh/kOUTw5IJRP8Uh+FFb7Xp2iH03CNWRq/A==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.6.tgz", + "integrity": "sha512-Hkz399qDGEbfXi9GYa2hDl7GahglI86JmS2F1KP8sfjLXofUgtnknyC5NWc86nzHcP38pZiPqPbTigyDYw5y8A==", "dev": true, "dependencies": { "@cspell/dict-data-science": "^2.0.1" @@ -2984,9 +2986,9 @@ "dev": true }, "node_modules/@cspell/dict-ruby": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.2.tgz", - "integrity": "sha512-cIh8KTjpldzFzKGgrqUX4bFyav5lC52hXDKo4LbRuMVncs3zg4hcSf4HtURY+f2AfEZzN6ZKzXafQpThq3dl2g==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.3.tgz", + "integrity": "sha512-V1xzv9hN6u8r6SM4CkYdsxs4ov8gjXXo0Twfx5kWhLXbEVxTXDMt7ohLTqpy2XlF5mutixZdbHMeFiAww8v+Ug==", "dev": true }, "node_modules/@cspell/dict-rust": { @@ -3002,9 +3004,9 @@ "dev": true }, "node_modules/@cspell/dict-software-terms": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.0.9.tgz", - "integrity": "sha512-zh68RM83efPenrH0n/QLU5OwIg5fgJDxJiIo4ThQUgvaxihwd4R/iFVCDNJTdtjbn5eHqkjVXj8f5EKc3Y0OLA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.3.tgz", + "integrity": "sha512-5Wn5JG4IzCboX5pjISdkipsPKGaz1//iuBZdHl4US5x7mO4jOGXLpjzx6ZoPM4PXUlMEFz9NJRCDepAu8fXVtA==", "dev": true }, "node_modules/@cspell/dict-sql": { @@ -3026,9 +3028,9 @@ "dev": true }, "node_modules/@cspell/dict-terraform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.0.tgz", - "integrity": "sha512-Ak+vy4HP/bOgzf06BAMC30+ZvL9mzv21xLM2XtfnBLTDJGdxlk/nK0U6QT8VfFLqJ0ZZSpyOxGsUebWDCTr/zQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.1.tgz", + "integrity": "sha512-29lmUUnZgPh+ieZ5hunick8hzNIpNRtiJh9vAusNskPCrig3RTW6u7F+GG1a8uyslbzSw+Irjf40PTOan1OJJA==", "dev": true }, "node_modules/@cspell/dict-typescript": { @@ -3226,6 +3228,18 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@hono/node-server": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.12.2.tgz", + "integrity": "sha512-xjzhqhSWUE/OhN0g3KCNVzNsQMlFUAL+/8GgPUr3TKcU7cvgZVBGswFofJ8WwGEHTqobzze1lDpGJl9ZNckDhA==", + "dev": true, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -4377,6 +4391,12 @@ "node": ">=10" } }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -4623,9 +4643,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", - "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", + "version": "22.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz", + "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==", "dependencies": { "undici-types": "~6.19.2" } @@ -5601,9 +5621,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz", + "integrity": "sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw==", "dev": true, "optional": true, "dependencies": { @@ -5613,9 +5633,9 @@ } }, "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", + "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", "dev": true, "optional": true }, @@ -5630,12 +5650,13 @@ } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz", + "integrity": "sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg==", "dev": true, "optional": true, "dependencies": { + "b4a": "^1.6.6", "streamx": "^2.18.0" } }, @@ -6024,9 +6045,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.4.tgz", - "integrity": "sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", + "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", "dev": true, "dependencies": { "mitt": "3.0.1", @@ -8561,9 +8582,9 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, "node_modules/devtools-protocol": { - "version": "0.0.1312386", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", - "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", + "version": "0.0.1330662", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz", + "integrity": "sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==", "dev": true }, "node_modules/dezalgo": { @@ -9212,9 +9233,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.9.0.tgz", + "integrity": "sha512-McVbYmwA3NEKwRQY5g4aWMdcZE5xZxV8i8l7CqJSrameuGSQJtSWaL/LxTEzSKKaCcOhlpDR8XEfYXWPrdo/ZQ==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -9238,26 +9259,27 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", "array.prototype.flat": "^1.3.2", "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", "semver": "^6.3.1", "tsconfig-paths": "^3.15.0" }, @@ -11285,6 +11307,15 @@ "node": ">=8" } }, + "node_modules/hono": { + "version": "4.5.11", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.5.11.tgz", + "integrity": "sha512-62FcjLPtjAFwISVBUshryl+vbHOjg8rE4uIK/dxyR8GpLztunZpwFmfEvmJCUI7xoGh/Sr3CGCDPCmYxVw7wUQ==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -11952,9 +11983,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -14403,9 +14434,9 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.9.tgz", - "integrity": "sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==", + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", "dev": true, "dependencies": { "chalk": "~5.3.0", @@ -14414,7 +14445,7 @@ "execa": "~8.0.1", "lilconfig": "~3.1.2", "listr2": "~8.2.4", - "micromatch": "~4.0.7", + "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", "yaml": "~2.5.0" @@ -14975,9 +15006,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -16326,17 +16357,17 @@ } }, "node_modules/puppeteer": { - "version": "23.1.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.1.1.tgz", - "integrity": "sha512-giN4Ikwl5hkkouH/dVyxIPTPslWuqZ8fjALdSw5Cvt+r0LuDpLdfPxRADlB75YJ2UjPZhgok+xYBYk8ffzv4MA==", + "version": "23.2.2", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.2.2.tgz", + "integrity": "sha512-3RX5vEhlOE/9M7ZXxoJVvI7pbOF3fzFK4etwbdii7w+r58q+krK5l2FXWlA1ETnNs7ilM/KLFc0n6K8VUi99dA==", "dev": true, "hasInstallScript": true, "dependencies": { "@puppeteer/browsers": "2.3.1", - "chromium-bidi": "0.6.4", + "chromium-bidi": "0.6.5", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1312386", - "puppeteer-core": "23.1.1", + "devtools-protocol": "0.0.1330662", + "puppeteer-core": "23.2.2", "typed-query-selector": "^2.12.0" }, "bin": { @@ -16347,15 +16378,15 @@ } }, "node_modules/puppeteer-core": { - "version": "23.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.1.1.tgz", - "integrity": "sha512-OeTqNiYGF9qZtwZU4Yc88DDqFJs4TJ4rnK81jkillh6MwDeQodyisM9xe5lBmPhwiDy92s5J5DQtQLjCKHFQ3g==", + "version": "23.2.2", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.2.2.tgz", + "integrity": "sha512-MK2Kbdmro+nX9/pfGKm8TiU5G3CuS6BbcNfkcz42GWnHp7KYsJHrP6lPDepiyvwjti5Bt/4a8U3w+DoFpIcBHQ==", "dev": true, "dependencies": { "@puppeteer/browsers": "2.3.1", - "chromium-bidi": "0.6.4", + "chromium-bidi": "0.6.5", "debug": "^4.3.6", - "devtools-protocol": "0.0.1312386", + "devtools-protocol": "0.0.1330662", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -17876,9 +17907,9 @@ } }, "node_modules/streamx": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.19.0.tgz", - "integrity": "sha512-5z6CNR4gtkPbwlxyEqoDGDmWIzoNJqCBt4Eac1ICP9YaIT08ct712cFj0u1rx4F8luAuL+3Qc+RFIdI4OX00kg==", + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz", + "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==", "dev": true, "dependencies": { "fast-fifo": "^1.3.2", diff --git a/package.json b/package.json index e30ec812cb..d183ccde3c 100644 --- a/package.json +++ b/package.json @@ -72,21 +72,22 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.0", + "webpack-dev-middleware": "^7.4.2", "ws": "^8.18.0" }, "devDependencies": { - "@babel/cli": "^7.22.5", - "@babel/core": "^7.22.5", - "@babel/eslint-parser": "^7.22.5", - "@babel/plugin-transform-object-assign": "^7.22.5", - "@babel/plugin-transform-runtime": "^7.22.5", - "@babel/preset-env": "^7.22.5", - "@babel/runtime": "^7.22.5", - "@commitlint/cli": "^19.0.3", - "@commitlint/config-conventional": "^19.0.3", + "@babel/cli": "^7.25.6", + "@babel/core": "^7.25.2", + "@babel/eslint-parser": "^7.25.1", + "@babel/plugin-transform-object-assign": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.25.4", + "@babel/preset-env": "^7.25.4", + "@babel/runtime": "^7.25.6", + "@commitlint/cli": "^19.4.1", + "@commitlint/config-conventional": "^19.4.1", + "@hono/node-server": "^1.12.2", "@types/compression": "^1.7.2", - "@types/node": "^22.3.0", + "@types/node": "^22.5.2", "@types/node-forge": "^1.3.1", "@types/sockjs-client": "^1.5.1", "@types/trusted-types": "^2.0.2", @@ -95,28 +96,29 @@ "babel-loader": "^9.1.0", "body-parser": "^1.19.2", "connect": "^3.7.0", - "core-js": "^3.31.0", - "cspell": "^8.3.2", + "core-js": "^3.38.1", + "cspell": "^8.14.2", "css-loader": "^7.1.1", "eslint": "^8.43.0", "eslint-config-prettier": "^9.1.0", "eslint-config-webpack": "^1.2.5", - "eslint-plugin-import": "^2.23.2", + "eslint-plugin-import": "^2.30.0", "execa": "^5.1.1", + "hono": "^4.5.11", "html-webpack-plugin": "^5.5.3", "http-proxy": "^1.18.1", - "husky": "^9.1.3", + "husky": "^9.1.5", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "klona": "^2.0.4", "less": "^4.1.1", "less-loader": "^12.1.0", - "lint-staged": "^15.2.0", + "lint-staged": "^15.2.10", "marked": "^12.0.0", "memfs": "^4.6.0", "npm-run-all": "^4.1.5", "prettier": "^3.2.4", - "puppeteer": "^23.1.0", + "puppeteer": "^23.2.2", "readable-stream": "^4.5.2", "require-from-string": "^2.0.2", "rimraf": "^5.0.5", @@ -128,7 +130,7 @@ "tcp-port-used": "^1.0.2", "typescript": "^5.5.4", "wait-for-expect": "^3.0.2", - "webpack": "^5.91.0", + "webpack": "^5.94.0", "webpack-cli": "^5.0.1", "webpack-merge": "^6.0.1" }, diff --git a/test/__snapshots__/validate-options.test.js.snap.webpack5 b/test/__snapshots__/validate-options.test.js.snap.webpack5 index 6ce65c5be2..386cce734c 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" | "http2" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | function | 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" | "http2" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | function | 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" | "http2" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | function | 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" | "http2" | non-empty string | object { type?, options? } + "http" | "https" | "spdy" | "http2" | function | 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 7569392a9d..182d9ce3f3 100644 --- a/test/e2e/__snapshots__/app.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/app.test.js.snap.webpack5 @@ -28,7 +28,7 @@ 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`] = ` +exports[`app option should work using "connect" application and "http" 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...", @@ -36,11 +36,11 @@ exports[`app option should work using "connect (async)" application and "http2" ] `; -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" application and "http" 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" application and "http" 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`] = ` +exports[`app option should work using "connect" application and "http" server should handle GET request to index route (/): response text 1`] = ` " @@ -56,7 +56,7 @@ exports[`app option should work using "connect (async)" application and "http2" " `; -exports[`app option should work using "connect (async)" application and "https" server should handle GET request to index route (/): console messages 1`] = ` +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...", @@ -64,11 +64,11 @@ exports[`app option should work using "connect (async)" application and "https" ] `; -exports[`app option should work using "connect (async)" application and "https" 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 (/): page errors 1`] = `[]`; -exports[`app option should work using "connect (async)" application and "https" 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 status 1`] = `200`; -exports[`app option should work using "connect (async)" application and "https" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "connect" application and "http2" server should handle GET request to index route (/): response text 1`] = ` " @@ -84,7 +84,7 @@ exports[`app option should work using "connect (async)" application and "https" " `; -exports[`app option should work using "connect (async)" application and "spdy" server should handle GET request to index route (/): console messages 1`] = ` +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.", "[HMR] Waiting for update signal from WDS...", @@ -92,11 +92,11 @@ exports[`app option should work using "connect (async)" application and "spdy" s ] `; -exports[`app option should work using "connect (async)" application and "spdy" server should handle GET request to index route (/): page errors 1`] = `[]`; +exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): page errors 1`] = `[]`; -exports[`app option should work using "connect (async)" application and "spdy" server should handle GET request to index route (/): response status 1`] = `200`; +exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): response status 1`] = `200`; -exports[`app option should work using "connect (async)" application and "spdy" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): response text 1`] = ` " @@ -112,7 +112,7 @@ exports[`app option should work using "connect (async)" application and "spdy" s " `; -exports[`app option should work using "connect" application and "http" server should handle GET request to index route (/): console messages 1`] = ` +exports[`app option should work using "connect" application and "spdy" 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...", @@ -120,11 +120,11 @@ exports[`app option should work using "connect" application and "http" server sh ] `; -exports[`app option should work using "connect" application and "http" server should handle GET request to index route (/): page errors 1`] = `[]`; +exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): page errors 1`] = `[]`; -exports[`app option should work using "connect" application and "http" server should handle GET request to index route (/): response status 1`] = `200`; +exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): response status 1`] = `200`; -exports[`app option should work using "connect" application and "http" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): response text 1`] = ` " @@ -140,7 +140,7 @@ 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`] = ` +exports[`app option should work using "express" application and "http" 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...", @@ -148,11 +148,11 @@ exports[`app option should work using "connect" application and "http2" server s ] `; -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 "express" application and "http" 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 "express" application and "http" 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`] = ` +exports[`app option should work using "express" application and "http" server should handle GET request to index route (/): response text 1`] = ` " @@ -168,7 +168,7 @@ exports[`app option should work using "connect" application and "http2" server s " `; -exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): console messages 1`] = ` +exports[`app option should work using "express" 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.", "[HMR] Waiting for update signal from WDS...", @@ -176,11 +176,11 @@ exports[`app option should work using "connect" application and "https" server s ] `; -exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): page errors 1`] = `[]`; +exports[`app option should work using "express" application and "https" server should handle GET request to index route (/): page errors 1`] = `[]`; -exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): response status 1`] = `200`; +exports[`app option should work using "express" application and "https" server should handle GET request to index route (/): response status 1`] = `200`; -exports[`app option should work using "connect" application and "https" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "express" application and "https" server should handle GET request to index route (/): response text 1`] = ` " @@ -196,7 +196,7 @@ exports[`app option should work using "connect" application and "https" server s " `; -exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): console messages 1`] = ` +exports[`app option should work using "express" application and "spdy" 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...", @@ -204,11 +204,11 @@ exports[`app option should work using "connect" application and "spdy" server sh ] `; -exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): page errors 1`] = `[]`; +exports[`app option should work using "express" application and "spdy" server should handle GET request to index route (/): page errors 1`] = `[]`; -exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): response status 1`] = `200`; +exports[`app option should work using "express" application and "spdy" server should handle GET request to index route (/): response status 1`] = `200`; -exports[`app option should work using "connect" application and "spdy" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "express" application and "spdy" server should handle GET request to index route (/): response text 1`] = ` " @@ -224,7 +224,7 @@ exports[`app option should work using "connect" application and "spdy" server sh " `; -exports[`app option should work using "express" application and "http" server should handle GET request to index route (/): console messages 1`] = ` +exports[`app option should work using "hono" application and "[object Object]" 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...", @@ -232,11 +232,11 @@ exports[`app option should work using "express" application and "http" server sh ] `; -exports[`app option should work using "express" application and "http" server should handle GET request to index route (/): page errors 1`] = `[]`; +exports[`app option should work using "hono" application and "[object Object]" server should handle GET request to index route (/): page errors 1`] = `[]`; -exports[`app option should work using "express" application and "http" server should handle GET request to index route (/): response status 1`] = `200`; +exports[`app option should work using "hono" application and "[object Object]" server should handle GET request to index route (/): response status 1`] = `200`; -exports[`app option should work using "express" application and "http" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "hono" application and "[object Object]" server should handle GET request to index route (/): response text 1`] = ` " @@ -252,7 +252,7 @@ exports[`app option should work using "express" application and "http" server sh " `; -exports[`app option should work using "express" application and "https" server should handle GET request to index route (/): console messages 1`] = ` +exports[`app option should work using "hono" application and "custom server" 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...", @@ -260,11 +260,23 @@ exports[`app option should work using "express" application and "https" server s ] `; -exports[`app option should work using "express" application and "https" server should handle GET request to index route (/): page errors 1`] = `[]`; +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): console messages 2`] = ` +[ + "[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 "express" application and "https" server should handle GET request to index route (/): response status 1`] = `200`; +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): page errors 1`] = `[]`; -exports[`app option should work using "express" application and "https" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): page errors 2`] = `[]`; + +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): response status 1`] = `200`; + +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): response status 2`] = `200`; + +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): response text 1`] = ` " @@ -280,19 +292,7 @@ exports[`app option should work using "express" application and "https" server s " `; -exports[`app option should work using "express" application and "spdy" 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 "express" application and "spdy" server should handle GET request to index route (/): page errors 1`] = `[]`; - -exports[`app option should work using "express" application and "spdy" server should handle GET request to index route (/): response status 1`] = `200`; - -exports[`app option should work using "express" application and "spdy" server should handle GET request to index route (/): response text 1`] = ` +exports[`app option should work using "hono" application and "custom server" server should handle GET request to index route (/): response text 2`] = ` " diff --git a/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 b/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 index 2a5c43b68d..04a4b7bb9a 100644 --- a/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 @@ -2474,7 +2474,7 @@ exports[`overlay should show overlay when "Content-Security-Policy" is "default- border: none; " > - × + X
require("express")()], - ["connect", () => require("connect")()], - ["connect (async)", () => require("connect")()], + ["express", () => require("express")(), "http"], + ["express", () => require("express")(), "https"], + ["express", () => require("express")(), "spdy"], + ["connect", () => require("connect")(), "http"], + ["connect", () => require("connect")(), "https"], + ["connect", () => require("connect")(), "spdy"], + ["connect", () => require("connect")(), "http2"], + ["connect (async)", () => require("connect")(), "http"], + [ + "hono", + () => new (require("hono").Hono)(), + (_, app) => require("@hono/node-server").serve({ fetch: app.fetch }), + (_, devServer) => [ + { + name: "webpack-dev-middleware", + middleware: wdm.honoWrapper(devServer.compiler), + }, + ], + ], + [ + "hono", + () => new (require("hono").Hono)(), + (_, app) => + require("@hono/node-server").serve({ + fetch: app.fetch, + createServer: require("node:https").createServer, + serverOptions: { + key: fs.readFileSync( + path.resolve(__dirname, "../fixtures/ssl/localhost-privkey.pem"), + ), + cert: fs.readFileSync( + path.resolve(__dirname, "../fixtures/ssl/localhost-cert.pem"), + ), + }, + }), + (_, devServer) => [ + { + name: "webpack-dev-middleware", + middleware: wdm.honoWrapper(devServer.compiler), + }, + ], + ], + [ + "hono", + () => new (require("hono").Hono)(), + { + type: (options, app) => + require("@hono/node-server").serve({ + fetch: app.fetch, + createServer: require("node:http2").createSecureServer, + serverOptions: options, + }), + options: { + allowHTTP1: true, + key: fs.readFileSync( + path.resolve(__dirname, "../fixtures/ssl/localhost-privkey.pem"), + ), + cert: fs.readFileSync( + path.resolve(__dirname, "../fixtures/ssl/localhost-cert.pem"), + ), + }, + }, + (_, devServer) => [ + { + name: "webpack-dev-middleware", + middleware: wdm.honoWrapper(devServer.compiler), + }, + ], + ], ]; -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; - let browser; - let pageErrors; - let consoleMessages; - - describe(`should work using "${appName}" application and "${server}" server`, () => { - beforeEach(async () => { - compiler = webpack(config); - - devServer = new Server( - { - static: { - directory: staticDirectory, - watch: false, - }, - app, - server, - port, + for (const [appName, app, server, setupMiddlewares] of apps) { + let compiler; + let devServer; + let page; + let browser; + let pageErrors; + let consoleMessages; + + describe(`should work using "${appName}" application and "${typeof server === "function" ? "custom server" : server}" server`, () => { + beforeEach(async () => { + compiler = webpack(config); + + devServer = new Server( + { + static: { + directory: staticDirectory, + watch: false, }, - compiler, - ); - - await devServer.start(); - - ({ page, browser } = await runBrowser()); - - pageErrors = []; - consoleMessages = []; - }); + app, + server, + port, + setupMiddlewares: + typeof setupMiddlewares !== "undefined" + ? setupMiddlewares + : // eslint-disable-next-line no-undefined + undefined, + }, + compiler, + ); + + await devServer.start(); + + ({ page, browser } = await runBrowser()); + + pageErrors = []; + consoleMessages = []; + }); - afterEach(async () => { - await browser.close(); - await devServer.stop(); + afterEach(async () => { + await browser.close(); + await devServer.stop(); + await new Promise((resolve) => { + compiler.close(() => { + resolve(); + }); }); + }); - it("should handle GET request to index route (/)", async () => { - page - .on("console", (message) => { - consoleMessages.push(message); - }) - .on("pageerror", (error) => { - pageErrors.push(error); - }); - - const pageUrl = - server === "https" || server === "spdy" || server === "http2" - ? `https://127.0.0.1:${port}/` - : `http://127.0.0.1:${port}/`; - - const response = await page.goto(pageUrl, { - waitUntil: "networkidle0", + it("should handle GET request to index route (/)", async () => { + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); }); - const HTTPVersion = await page.evaluate( - () => performance.getEntries()[0].nextHopProtocol, - ); - - if (server === "spdy" || server === "http2") { - expect(HTTPVersion).toEqual("h2"); - } else { - expect(HTTPVersion).toEqual("http/1.1"); - } - - expect(response.status()).toMatchSnapshot("response status"); - expect(await response.text()).toMatchSnapshot("response text"); - expect( - consoleMessages.map((message) => message.text()), - ).toMatchSnapshot("console messages"); - expect(pageErrors).toMatchSnapshot("page errors"); + const pageUrl = devServer.isTlsServer + ? `https://127.0.0.1:${port}/` + : `http://127.0.0.1:${port}/`; + + const response = await page.goto(pageUrl, { + waitUntil: "networkidle0", }); + + const HTTPVersion = await page.evaluate( + () => performance.getEntries()[0].nextHopProtocol, + ); + + if ( + server === "spdy" || + server === "http2" || + (server.options && server.options.allowHTTP1) + ) { + expect(HTTPVersion).toEqual("h2"); + } else { + expect(HTTPVersion).toEqual("http/1.1"); + } + + expect(response.status()).toMatchSnapshot("response status"); + expect(await response.text()).toMatchSnapshot("response text"); + expect( + consoleMessages.map((message) => message.text()), + ).toMatchSnapshot("console messages"); + expect(pageErrors).toMatchSnapshot("page errors"); }); - } + }); } }); diff --git a/test/e2e/overlay.test.js b/test/e2e/overlay.test.js index 7e1efb1122..023b9a331a 100644 --- a/test/e2e/overlay.test.js +++ b/test/e2e/overlay.test.js @@ -1978,10 +1978,16 @@ describe("overlay", () => { }), ).toMatchSnapshot("page html"); expect( - await prettier.format(overlayHtml, { - parser: "html", - plugins: [prettierHTML, prettierCSS], - }), + await prettier.format( + overlayHtml.replace( + /", + ), + { + parser: "html", + plugins: [prettierHTML, prettierCSS], + }, + ), ).toMatchSnapshot("overlay html"); } catch (error) { throw error; diff --git a/test/e2e/web-socket-server-url.test.js b/test/e2e/web-socket-server-url.test.js index 122a9e205b..f0f33ea33b 100644 --- a/test/e2e/web-socket-server-url.test.js +++ b/test/e2e/web-socket-server-url.test.js @@ -2688,7 +2688,9 @@ describe("web socket server URL", () => { consoleMessages.map((message) => message.text()), ).toMatchSnapshot("console messages"); expect( - pageErrors.map((pageError) => pageError.message.split("\n")[0]), + pageErrors.map((pageError) => + pageError.message.split("\n")[0].replace("SyntaxError: ", ""), + ), ).toMatchSnapshot("page errors"); } catch (error) { throw error; diff --git a/test/fixtures/ssl/localhost-cert.pem b/test/fixtures/ssl/localhost-cert.pem new file mode 100644 index 0000000000..c8453042dc --- /dev/null +++ b/test/fixtures/ssl/localhost-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUevWiuCfenWuq9KyC8aQ/tc1Io14wDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDQyNDE2MDYyMloXDTI0MDUy +NDE2MDYyMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA1v/lb9u9WkqkF7zjIKe2R+b4S0sQnWIfBFZ0ggtaOL0a +ntud/EuaGQgLtJgSwO2M2xIqKx+yoLhoM+273EJe0KmfJMxYNAkhwP9h6vrKnaQJ +mpAhoalfEGyCrnHHMKISAAn4Rlc8NXnULoFhHzNm8bdqvP33rCmsJ+tNYC5kwzyt +HvRNFyg9BOUfACiPW17opFH0rao3IfZrQ6yRbknef1pX1x2pbDAH14rCT/vXaTs6 +VGuqLE/wRsSt+7nMHy/PmXxMyb4G4/UflYtnKfmXpDRw+TDEGzvTZedtoOz+rrJC +e989R9qYGrlPfyfZbI+O348FV66I+jcD+/EUQs+HkwIDAQABo1MwUTAdBgNVHQ4E +FgQU6bk4LSwtVQEt7V/ev+Zj270zdAkwHwYDVR0jBBgwFoAU6bk4LSwtVQEt7V/e +v+Zj270zdAkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAUBgo +E3CZrrc/MaadFg1meNk+eKACmTsIa5cT6zi7MsvoKakXEd4bGd+iLifUzlAa1ygj +dQppfprb5t68I7oO9/lkh2DfKrXxW/RpdhB05KslUd8q/3XY5kyao5quzeiVoMHR +u+XYjoy2mTwdUC2uzFy6rkHsAkJy2vJJoDdlNsrKn6AZmh+voHHKrAtOL4gnanQV +wR1u8eBVfk2MKIl2pNSCA4bD16uZyp3+oqq097BEoVa1pR+l8nwbsh/YfALifq/d +P3yiN5+EqgiOIF9b8PZORe+Ry1O7uvPnU2ZRkVWPJ1S17Ms0lnr7IY3qjSBTuK66 +5uYi7ojrb5Vf0UL5oQ== +-----END CERTIFICATE----- diff --git a/test/fixtures/ssl/localhost-privkey.pem b/test/fixtures/ssl/localhost-privkey.pem new file mode 100644 index 0000000000..c260882984 --- /dev/null +++ b/test/fixtures/ssl/localhost-privkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQDW/+Vv271aSqQX +vOMgp7ZH5vhLSxCdYh8EVnSCC1o4vRqe2538S5oZCAu0mBLA7YzbEiorH7KguGgz +7bvcQl7QqZ8kzFg0CSHA/2Hq+sqdpAmakCGhqV8QbIKucccwohIACfhGVzw1edQu +gWEfM2bxt2q8/fesKawn601gLmTDPK0e9E0XKD0E5R8AKI9bXuikUfStqjch9mtD +rJFuSd5/WlfXHalsMAfXisJP+9dpOzpUa6osT/BGxK37ucwfL8+ZfEzJvgbj9R+V +i2cp+ZekNHD5MMQbO9Nl522g7P6uskJ73z1H2pgauU9/J9lsj47fjwVXroj6NwP7 +8RRCz4eTAgMBAAECggEAA+zbFv43iEj5kvdfXC7DrK9iVBmUPZNXhqA/c0paxNNr +A4B182+76f4UHKF0IjKUEkHUJEJpY/bJ7DzIY76QdZXLMoRKjfSmuZvQAVa/0T33 +8Or1ujpZ4nZgsmegX9ptorOL5VjdYAqP3aN+DvBEzl/vYnDujyWZn4bzvDBMpaXS +39qW1MkcZ8UiP1fRad76+S57WnieBV+NRHYEAiDdMFKXLuw/igX/xOSZgq5Jh3I2 +hLS49S41dN1P9l9H2bPMw0CthNvMPPaemwKHz+84hSS+P4VJOWJzlGnXEdIFuqBR +GFBESQzcemfS9DDB22Yt06YujBCbwTVVAxj73lnKkQKBgQDvYXK36J9y/NphDAWi +Cwti5oE3eSfV0YazQwm+rRwC64wbpBFAm9ujwjUmaYBg75lBLF5nOOe8s1n95g5I +tLfFb+zuZh8NNLjhfNE9/SNmRnnMvbcaDHeIE2RMAz+PuLN/gFLmsVIwK2X1LRC2 +0vHjw9Yzh6JLiOajAchzhZiCEQKBgQDl7R6Wfggo8myETA8Uv5tWot3IcquRkEl/ +TRCyao2/79rAGexS7piwD7FPdSDOk1zfZFYUOMzyMjj60sGcPRPqRX6D0usEODLQ +TwsTJSCNgPnIOkqKkccwtqlTimbRIrPUSQfFPj56RzKKWdrJ/P3LPRjzkK7i3vLV +EGlAENaLYwKBgHKSOnzpWr+HY+IFBgErthRs7LWnSDifYxATauuXIQwIvvNP0G4S +6snzHss2vZonszstSDWxV8DKOq052eZUkIxv6H+l4wDIFiDeQ6uep73Ax3UF7EgM +ZX18gombGGXqagcBXSxK/GJPsynomtJWHi38Ql5BcZ0jdffY157q9zZxAoGAPZtD +Tt+GIDKUkP4wLEcKwDPzaPoQrngSuWFUz/ls8bi6zC4l/DKiBsqtn7Sqja8+ezzP +M6vkfiCm084UwmA7LdJhC8E/52mHc/k55m9UQZYFV3kG8AoPbSYESLYUxoSd2ouW +4WrEIs9g42EgFm8LMaG1Rc3GjlNejWhQSzI3yjECf3v7VoAcUwVfuVkwbm9W24vR +neFTF8QBl//fxIdxZwoj5SrSgMOjmZ3pXA/ZbFJ0pB4Rh5dmKTYqdpfXsOTiBuwB +XlqPVpN8UZEl3edpufLDyPldNej/9kEAkK5FS3YVyIQEg75739bCTlfzzCX1HdMx +q98XYm/n5LWYFezsAt0= +-----END PRIVATE KEY----- diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts index 4c882938ad..19e8e342fb 100644 --- a/types/lib/Server.d.ts +++ b/types/lib/Server.d.ts @@ -602,6 +602,9 @@ declare class Server< ServerType: { enum: string[]; }; + ServerFn: { + instanceof: string; + }; ServerEnum: { enum: string[]; cli: { @@ -1264,12 +1267,18 @@ declare class Server< * @private * @returns {void} */ - private setupHostHeaderCheck; + private setupWatchStaticFiles; /** * @private * @returns {void} */ - private setupDevMiddleware; + private setupWatchFiles; + /** + * @private + * @returns {void} + */ + private setupMiddlewares; + /** @type {import("webpack-dev-middleware").API} */ middleware: | import("webpack-dev-middleware").API< import("express").Request< @@ -1281,35 +1290,15 @@ declare class Server< >, import("express").Response> > - | null | undefined; /** * @private - * @returns {void} - */ - private setupBuiltInRoutes; - /** - * @private - * @returns {void} - */ - private setupWatchStaticFiles; - /** - * @private - * @returns {void} - */ - private setupWatchFiles; - /** - * @private - * @returns {void} - */ - private setupMiddlewares; - /** - * @private - * @returns {void} + * @returns {Promise} */ private createServer; - /** @type {S | null | undefined}*/ - server: S | null | undefined; + /** @type {S | undefined}*/ + server: S | undefined; + isTlsServer: boolean | undefined; /** * @private * @returns {void} @@ -1473,6 +1462,7 @@ declare namespace Server { ClientConfiguration, Headers, MiddlewareHandler, + MiddlewareObject, Middleware, BasicServer, Configuration, @@ -1589,9 +1579,27 @@ type NormalizedStatic = { staticOptions: ServeStaticOptions; watch: false | WatchOptions; }; -type ServerType = "http" | "https" | "spdy" | "http2" | string; -type ServerConfiguration = { - type?: string | undefined; +type ServerType< + A extends BasicApplication = import("express").Application, + S extends BasicServer = import("http").Server< + typeof import("http").IncomingMessage, + typeof import("http").ServerResponse + >, +> = + | "http" + | "https" + | "spdy" + | "http2" + | string + | ((arg0: ServerOptions, arg1: A) => S); +type ServerConfiguration< + A extends BasicApplication = import("express").Application, + S extends BasicServer = import("http").Server< + typeof import("http").IncomingMessage, + typeof import("http").ServerResponse + >, +> = { + type?: ServerType | undefined; options?: ServerOptions | undefined; }; type WebSocketServerConfiguration = { @@ -1683,14 +1691,13 @@ type MiddlewareHandler< > = T extends ExpressApplication ? ExpressRequestHandler | ExpressErrorRequestHandler : HandleFunction; -type Middleware = - | { - name?: string; - path?: string; - middleware: MiddlewareHandler; - } - | MiddlewareHandler; -type BasicServer = import("net").Server; +type MiddlewareObject = { + name?: string; + path?: string; + middleware: MiddlewareHandler; +}; +type Middleware = MiddlewareObject | MiddlewareHandler; +type BasicServer = import("net").Server | import("tls").Server; type Configuration< A extends BasicApplication = import("express").Application, S extends BasicServer = import("http").Server< @@ -1733,7 +1740,7 @@ type Configuration< | (string | WatchFiles)[] | undefined; static?: string | boolean | Static | (string | Static)[] | undefined; - server?: string | ServerConfiguration | undefined; + server?: ServerType | ServerConfiguration | undefined; app?: (() => Promise) | undefined; webSocketServer?: string | boolean | WebSocketServerConfiguration | undefined; proxy?: ProxyConfigArray | undefined; @@ -1745,7 +1752,7 @@ type Configuration< | (( req: Request, res: Response, - context: DevMiddlewareContext, + context: DevMiddlewareContext | undefined, ) => Headers) | undefined; onListening?: ((devServer: Server) => void) | undefined;