diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ad381ad..8e24ab891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [7.4.2](https://github.com/webpack/webpack-dev-middleware/compare/v7.4.1...v7.4.2) (2024-08-21) + + +### Bug Fixes + +* no crash when headers are already sent ([#1929](https://github.com/webpack/webpack-dev-middleware/issues/1929)) ([c20f1d9](https://github.com/webpack/webpack-dev-middleware/commit/c20f1d98dff9b51931fae44a44fbc53387768673)) + ### [7.4.1](https://github.com/webpack/webpack-dev-middleware/compare/v7.4.0...v7.4.1) (2024-08-20) diff --git a/package-lock.json b/package-lock.json index 93b8f1850..b4e9bad9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "webpack-dev-middleware", - "version": "7.4.1", + "version": "7.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "webpack-dev-middleware", - "version": "7.4.1", + "version": "7.4.2", "license": "MIT", "dependencies": { "colorette": "^2.0.10", @@ -2555,9 +2555,9 @@ } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.14.1.tgz", - "integrity": "sha512-yM5cDCbkGttCWBQuSseECHGFF2h1RpX/ZI1I+evuFBW+eYMJm2JeNLDFyuAu7TzCsNtA+PZMs3ctqIP9xg9hHg==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.14.2.tgz", + "integrity": "sha512-Kv2Utj/RTSxfufGXkkoTZ/3ErCsYWpCijtDFr/FwSsM7mC0PzLpdlcD9xjtgrJO5Kwp7T47iTG21U4Mwddyi8Q==", "dev": true, "dependencies": { "@cspell/dict-ada": "^4.0.2", @@ -2618,30 +2618,30 @@ } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.14.1.tgz", - "integrity": "sha512-eJpgmocT+DY+uy9+sHCz6Ir8YVg7b/hnf5N7dITHlI8dnzgoScTZG2nZhVhJozrgb44B1dZuJzVR1DBLKgZY8A==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.14.2.tgz", + "integrity": "sha512-TZavcnNIZKX1xC/GNj80RgFVKHCT4pHT0qm9jCsQFH2QJfyCrUlkEvotKGSQ04lAyCwWg6Enq95qhouF8YbKUQ==", "dev": true, "dependencies": { - "@cspell/cspell-types": "8.14.1" + "@cspell/cspell-types": "8.14.2" }, "engines": { "node": ">=18" } }, "node_modules/@cspell/cspell-pipe": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.14.1.tgz", - "integrity": "sha512-KSFu/yyoJekezT9Ex5vgyI0a9tpRVXV4KEfOfL1gH/xbWBTiYx+RyEWEefebMxlMp7tdJiNI7HI0vvJ6YdUdsA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.14.2.tgz", + "integrity": "sha512-aWMoXZAXEre0/M9AYWOW33YyOJZ06i4vvsEpWBDWpHpWQEmsR/7cMMgld8Pp3wlEjIUclUAKTYmrZ61PFWU/og==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@cspell/cspell-resolver": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.14.1.tgz", - "integrity": "sha512-MMr3L1yrhITH8eG0fvXNEMo94g4MGSIeHzKKvq40fr39Oox/1MBxYAbWiI2NQ/Bxnbq854SY8pfwTSKjyNEGig==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.14.2.tgz", + "integrity": "sha512-pSyBsAvslaN0dx0pHdvECJEuFDDBJGAD6G8U4BVbIyj2OPk0Ox0HrZIj6csYxxoJERAgNO/q7yCPwa4j9NNFXg==", "dev": true, "dependencies": { "global-directory": "^4.0.1" @@ -2651,18 +2651,18 @@ } }, "node_modules/@cspell/cspell-service-bus": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.14.1.tgz", - "integrity": "sha512-uKrrCLvEkmAPB4vjUw9GM+B3TV2VsWpV6L3wkcQ9+zn9iPYgYk2WkvSHlOunnZ4u1TzcTdd88ZQtMjati3DLCg==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.14.2.tgz", + "integrity": "sha512-WUF7xf3YgXYIqjmBwLcVugYIrYL4WfXchgSo9rmbbnOcAArzsK+HKfzb4AniZAJ1unxcIQ0JnVlRmnCAKPjjLg==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@cspell/cspell-types": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.14.1.tgz", - "integrity": "sha512-E7tgF6867gsjttITAXF+8nS4BjZ4JQW4Gbrse1RP7jdW7y1biXipBfZxngsEbnR209MrZAnSobS40m9ih7gWfQ==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.14.2.tgz", + "integrity": "sha512-MRY8MjBNOKGMDSkxAKueYAgVL43miO+lDcLCBBP+7cNXqHiUFMIZteONcGp3kJT0dWS04dN6lKAXvaNF0aWcng==", "dev": true, "engines": { "node": ">=18" @@ -2990,9 +2990,9 @@ "dev": true }, "node_modules/@cspell/dynamic-import": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.14.1.tgz", - "integrity": "sha512-zGnymwieuFigqz839cKCF9JB90nUm50SMqopWClMl4LFOpqpuCucn/Slh4CLGu2vri4iqCvRweDxZCsi/5qYiw==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.14.2.tgz", + "integrity": "sha512-5MbqtIligU7yPwHWU/5yFCgMvur4i1bRAF1Cy8y2dDtHsa204S/w/SaXs+51EFLp2eNbCiBisCBrwJFT7R1RxA==", "dev": true, "dependencies": { "import-meta-resolve": "^4.1.0" @@ -3002,27 +3002,27 @@ } }, "node_modules/@cspell/filetypes": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.14.1.tgz", - "integrity": "sha512-jOcTFzHJ3c1uTZLm3BvLrZ8TakXLIimsFGwvk/qTA1EYgUPC2a0TypGCxR0NCHmpMlZvfAT5iAksDIiCHq1yjg==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.14.2.tgz", + "integrity": "sha512-ZevArA0mWeVTTqHicxCPZIAeCibpY3NwWK/x6d1Lgu7RPk/daoGAM546Q2SLChFu+r10tIH7pRG212A6Q9ihPA==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@cspell/strong-weak-map": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.14.1.tgz", - "integrity": "sha512-idQVm12vzQHLMpV4ETDFBPpSP7TTf0hRrdsY5i/La6uzZE05b5QxadfInNtbKV/Tf2OpjV3dygALOo2932xChw==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.14.2.tgz", + "integrity": "sha512-7sRzJc392CQYNNrtdPEfOHJdRqsqf6nASCtbS5A9hL2UrdWQ4uN7r/D+Y1HpuizwY9eOkZvarcFfsYt5wE0Pug==", "dev": true, "engines": { "node": ">=18" } }, "node_modules/@cspell/url": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-8.14.1.tgz", - "integrity": "sha512-K8TSiDti+mhuITezwr0fpmD756Y52cbJdxAgoXioL3Ri6ZoyQyhyhsJFeE6kNZLq24KwddZa8WJaY7hHKylygg==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-8.14.2.tgz", + "integrity": "sha512-YmWW+B/2XQcCynLpiAQF77Bitm5Cynw3/BICZkbdveKjJkUzEmXB+U2qWuwXOyU8xUYuwkP63YM8McnI567rUA==", "dev": true, "engines": { "node": ">=18.0" @@ -4742,9 +4742,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.1.tgz", - "integrity": "sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==", + "version": "22.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.2.tgz", + "integrity": "sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==", "dev": true, "dependencies": { "undici-types": "~6.19.2" @@ -7719,24 +7719,24 @@ } }, "node_modules/cspell": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.14.1.tgz", - "integrity": "sha512-UwjSLwt3RR8sP1dtjVbLimc8CpziOlVXH0yXb7/nWyMi3wEPWaV3o0VSTtRHRoaYHAjVzNlXDT6kiSr6RqyPog==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.14.2.tgz", + "integrity": "sha512-ii/W7fwO4chNQVYl1C/8k7RW8EXzLb69rvg08p8mSJx8B2UasVJ9tuJpTH2Spo1jX6N3H0dKPWUbd1fAmdAhPg==", "dev": true, "dependencies": { - "@cspell/cspell-json-reporter": "8.14.1", - "@cspell/cspell-pipe": "8.14.1", - "@cspell/cspell-types": "8.14.1", - "@cspell/dynamic-import": "8.14.1", - "@cspell/url": "8.14.1", + "@cspell/cspell-json-reporter": "8.14.2", + "@cspell/cspell-pipe": "8.14.2", + "@cspell/cspell-types": "8.14.2", + "@cspell/dynamic-import": "8.14.2", + "@cspell/url": "8.14.2", "chalk": "^5.3.0", "chalk-template": "^1.1.0", "commander": "^12.1.0", - "cspell-dictionary": "8.14.1", - "cspell-gitignore": "8.14.1", - "cspell-glob": "8.14.1", - "cspell-io": "8.14.1", - "cspell-lib": "8.14.1", + "cspell-dictionary": "8.14.2", + "cspell-gitignore": "8.14.2", + "cspell-glob": "8.14.2", + "cspell-io": "8.14.2", + "cspell-lib": "8.14.2", "fast-glob": "^3.3.2", "fast-json-stable-stringify": "^2.1.0", "file-entry-cache": "^9.0.0", @@ -7756,12 +7756,12 @@ } }, "node_modules/cspell-config-lib": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.14.1.tgz", - "integrity": "sha512-660v4G+3AikdGfSri09YGx7enR4RWPIPLiFKA+3F+CY2lj16l4bh7B/aNfU9oYRDvCcWBCik53AyOne/bSuPVg==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.14.2.tgz", + "integrity": "sha512-yHP1BdcH5dbjb8qiZr6+bxEnJ+rxTULQ00wBz3eBPWCghJywEAYYvMWoYuxVtPpndlkKYC1wJAHsyNkweQyepA==", "dev": true, "dependencies": { - "@cspell/cspell-types": "8.14.1", + "@cspell/cspell-types": "8.14.2", "comment-json": "^4.2.5", "yaml": "^2.5.0" }, @@ -7770,14 +7770,14 @@ } }, "node_modules/cspell-dictionary": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.14.1.tgz", - "integrity": "sha512-+QI3RLzfA4bkKEa5H9OQx2cPN+f5mXx8zbmccoJXxgjUi3fWmNGz4LPHnNQQ7pWXxQ2V81UXDwd7qRN9qkzISQ==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.14.2.tgz", + "integrity": "sha512-gWuAvf6queGGUvGbfAxxUq55cZ0OevWPbjnCrSB0PpJ4tqdFd8dLcvVrIKzoE2sBXKPw2NDkmoEngs6iGavC0w==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "8.14.1", - "@cspell/cspell-types": "8.14.1", - "cspell-trie-lib": "8.14.1", + "@cspell/cspell-pipe": "8.14.2", + "@cspell/cspell-types": "8.14.2", + "cspell-trie-lib": "8.14.2", "fast-equals": "^5.0.1" }, "engines": { @@ -7785,14 +7785,14 @@ } }, "node_modules/cspell-gitignore": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.14.1.tgz", - "integrity": "sha512-f/3rZqHKTFOB37Ey8b7eIQwom4w+wKKzr1sEsoEdLsWyRAd7HdSXkDG6O0S3RYvUYoiXZM9HQsQY695CVhq2wQ==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.14.2.tgz", + "integrity": "sha512-lrO/49NaKBpkR7vFxv4OOY+oHmsG5+gNQejrBBWD9Nv9vvjJtz/G36X/rcN6M6tFcQQMWwa01kf04nxz8Ejuhg==", "dev": true, "dependencies": { - "@cspell/url": "8.14.1", - "cspell-glob": "8.14.1", - "cspell-io": "8.14.1", + "@cspell/url": "8.14.2", + "cspell-glob": "8.14.2", + "cspell-io": "8.14.2", "find-up-simple": "^1.0.0" }, "bin": { @@ -7803,12 +7803,12 @@ } }, "node_modules/cspell-glob": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.14.1.tgz", - "integrity": "sha512-562ZbkBikXlB3JEGlGsi+3Xa4aghc2nqW1DLhcyXId/eunuJuUIqDGeexHkRwb0yBkq1we8O67hJtC3W0ih5GQ==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.14.2.tgz", + "integrity": "sha512-9Q1Kgoo1ev3fKTpp9y5n8M4RLxd8B0f5o4y5FQe4dBU0j/bt+/YDrLZNWDm77JViV606XQ6fimG1FTTq6pT9/g==", "dev": true, "dependencies": { - "@cspell/url": "8.14.1", + "@cspell/url": "8.14.2", "micromatch": "^4.0.7" }, "engines": { @@ -7816,13 +7816,13 @@ } }, "node_modules/cspell-grammar": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.14.1.tgz", - "integrity": "sha512-q4soR+FDU7Z3Z2gxl9dYP8qtrunH32aozhIGx6kkLWKWSy/jk2HaWdDp2MkpsQUURXLKMJ6PBZfpzR9Mxz3KqA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.14.2.tgz", + "integrity": "sha512-eYwceVP80FGYVJenE42ALnvEKOXaXjq4yVbb1Ni1umO/9qamLWNCQ1RP6rRACy5e/cXviAbhrQ5Mtw6n+pyPEQ==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "8.14.1", - "@cspell/cspell-types": "8.14.1" + "@cspell/cspell-pipe": "8.14.2", + "@cspell/cspell-types": "8.14.2" }, "bin": { "cspell-grammar": "bin.mjs" @@ -7832,40 +7832,40 @@ } }, "node_modules/cspell-io": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.14.1.tgz", - "integrity": "sha512-BQvFFzlPXu0RrBecjryZI6EwegpCeph7CnNoWlBUlO/T6kJiB6uG674n/LyenOImnLRrLUbRt1yZcPxziFHNlA==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.14.2.tgz", + "integrity": "sha512-uaKpHiY3DAgfdzgKMQml6U8F8o9udMuYxGqYa5FVfN7D5Ap7B2edQzSLTUYwxrFEn4skSfp6XY73+nzJvxzH4Q==", "dev": true, "dependencies": { - "@cspell/cspell-service-bus": "8.14.1", - "@cspell/url": "8.14.1" + "@cspell/cspell-service-bus": "8.14.2", + "@cspell/url": "8.14.2" }, "engines": { "node": ">=18" } }, "node_modules/cspell-lib": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.14.1.tgz", - "integrity": "sha512-wVZVVezge8ubq+zOED1V6EEtrJQZPNJcpNPDnc7ZXwnWiQxREWvQkuBa60EvAezPImxYdVDr8Y0dIS8yLd9WJg==", - "dev": true, - "dependencies": { - "@cspell/cspell-bundled-dicts": "8.14.1", - "@cspell/cspell-pipe": "8.14.1", - "@cspell/cspell-resolver": "8.14.1", - "@cspell/cspell-types": "8.14.1", - "@cspell/dynamic-import": "8.14.1", - "@cspell/filetypes": "8.14.1", - "@cspell/strong-weak-map": "8.14.1", - "@cspell/url": "8.14.1", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.14.2.tgz", + "integrity": "sha512-d2oiIXHXnADmnhIuFLOdNE63L7OUfzgpLbYaqAWbkImCUDkevfGrOgnX8TJ03fUgZID4nvQ+3kgu/n2j4eLZjQ==", + "dev": true, + "dependencies": { + "@cspell/cspell-bundled-dicts": "8.14.2", + "@cspell/cspell-pipe": "8.14.2", + "@cspell/cspell-resolver": "8.14.2", + "@cspell/cspell-types": "8.14.2", + "@cspell/dynamic-import": "8.14.2", + "@cspell/filetypes": "8.14.2", + "@cspell/strong-weak-map": "8.14.2", + "@cspell/url": "8.14.2", "clear-module": "^4.1.2", "comment-json": "^4.2.5", - "cspell-config-lib": "8.14.1", - "cspell-dictionary": "8.14.1", - "cspell-glob": "8.14.1", - "cspell-grammar": "8.14.1", - "cspell-io": "8.14.1", - "cspell-trie-lib": "8.14.1", + "cspell-config-lib": "8.14.2", + "cspell-dictionary": "8.14.2", + "cspell-glob": "8.14.2", + "cspell-grammar": "8.14.2", + "cspell-io": "8.14.2", + "cspell-trie-lib": "8.14.2", "env-paths": "^3.0.0", "fast-equals": "^5.0.1", "gensequence": "^7.0.0", @@ -7892,13 +7892,13 @@ } }, "node_modules/cspell-trie-lib": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.14.1.tgz", - "integrity": "sha512-2B5pnRHgYHFtdE4N1a0oWCYI1pZVeD1kvcst77ySe520Rg/U18aIET3UzsvN97EDwQ6Y23tHoVsXha0PxD1xfw==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.14.2.tgz", + "integrity": "sha512-rZMbaEBGoyy4/zxKECaMyVyGLbuUxYmZ5jlEgiA3xPtEdWwJ4iWRTo5G6dWbQsXoxPYdAXXZ0/q0GQ2y6Jt0kw==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "8.14.1", - "@cspell/cspell-types": "8.14.1", + "@cspell/cspell-pipe": "8.14.2", + "@cspell/cspell-types": "8.14.2", "gensequence": "^7.0.0" }, "engines": { @@ -11063,9 +11063,9 @@ } }, "node_modules/hono": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.5.6.tgz", - "integrity": "sha512-9SuUC/zLQv8YAcnIxJko0KCeLI0Q6menPsDWuJ9jaH+r8ZkVXeLqeLs1QJXCPKKbURAWj9x0SJBSFh803EnAUw==", + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.5.7.tgz", + "integrity": "sha512-7GeBa+zuZ6rXQEcsYvoAafLNgDr3IMxoMlU1JUc23Buy99FaUpjB0viKIFOsfnzMdEp7RhPL6uLYsVuddjdMvw==", "dev": true, "engines": { "node": ">=16.0.0" @@ -11156,9 +11156,9 @@ } }, "node_modules/husky": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.4.tgz", - "integrity": "sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", + "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", "dev": true, "bin": { "husky": "bin.js" diff --git a/package.json b/package.json index e37e11cb5..687e371a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webpack-dev-middleware", - "version": "7.4.1", + "version": "7.4.2", "description": "A development middleware for webpack", "license": "MIT", "repository": "webpack/webpack-dev-middleware", diff --git a/src/index.js b/src/index.js index 6f9e8ad10..fb9007ac0 100644 --- a/src/index.js +++ b/src/index.js @@ -305,7 +305,7 @@ function wdm(compiler, options = {}) { /** * @template S * @template O - * @typedef {HapiPluginBase & { pkg: { name: string } }} HapiPlugin + * @typedef {HapiPluginBase & { pkg: { name: string }, multiple: boolean }} HapiPlugin */ /** @@ -322,6 +322,8 @@ function hapiWrapper() { pkg: { name: "webpack-dev-middleware", }, + // Allow to have multiple middleware + multiple: true, register(server, options) { const { compiler, ...rest } = options; @@ -332,7 +334,11 @@ function hapiWrapper() { const devMiddleware = wdm(compiler, rest); // @ts-ignore - server.decorate("server", "webpackDevMiddleware", devMiddleware); + if (!server.decorations.server.includes("webpackDevMiddleware")) { + // @ts-ignore + server.decorate("server", "webpackDevMiddleware", devMiddleware); + } + // @ts-ignore server.ext("onRequest", (request, h) => new Promise((resolve, reject) => { @@ -568,6 +574,8 @@ function honoWrapper(compiler, options) { res.getReadyReadableStreamState = () => "readable"; + res.getHeadersSent = () => c.env.outgoing.headersSent; + let body; try { diff --git a/src/middleware.js b/src/middleware.js index 2b4bdcb60..ae6ea0e9c 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -15,6 +15,7 @@ const { setResponseHeader, removeResponseHeader, getResponseHeaders, + getHeadersSent, send, finish, pipe, @@ -494,56 +495,44 @@ function wrapper(context) { return; } + if (getHeadersSent(res)) { + await goNext(); + return; + } + const { size } = /** @type {import("fs").Stats} */ (extra.stats); let len = size; let offset = 0; // Send logic - let { headers } = context.options; + if (context.options.headers) { + let { headers } = context.options; - if (typeof headers === "function") { - headers = /** @type {NormalizedHeaders} */ (headers(req, res, context)); - } - - /** - * @type {{key: string, value: string | number}[]} - */ - const allHeaders = []; - - if (typeof headers !== "undefined") { - if (!Array.isArray(headers)) { - // eslint-disable-next-line guard-for-in - for (const name in headers) { - allHeaders.push({ key: name, value: headers[name] }); - } - - headers = allHeaders; + if (typeof headers === "function") { + headers = /** @type {NormalizedHeaders} */ ( + headers(req, res, context) + ); } - for (const { key, value } of headers) { - setResponseHeader(res, key, value); - } - } + /** + * @type {{key: string, value: string | number}[]} + */ + const allHeaders = []; - if ( - !getResponseHeader(res, "Content-Type") || - getStatusCode(res) === 404 - ) { - removeResponseHeader(res, "Content-Type"); - // content-type name(like application/javascript; charset=utf-8) or false - const contentType = mime.contentType(path.extname(filename)); + if (typeof headers !== "undefined") { + if (!Array.isArray(headers)) { + // eslint-disable-next-line guard-for-in + for (const name in headers) { + allHeaders.push({ key: name, value: headers[name] }); + } - // Only set content-type header if media type is known - // https://tools.ietf.org/html/rfc7231#section-3.1.1.5 - if (contentType) { - setResponseHeader(res, "Content-Type", contentType); - } else if (context.options.mimeTypeDefault) { - setResponseHeader( - res, - "Content-Type", - context.options.mimeTypeDefault, - ); + headers = allHeaders; + } + + for (const { key, value } of headers) { + setResponseHeader(res, key, value); + } } } @@ -667,6 +656,27 @@ function wrapper(context) { } } + if ( + !getResponseHeader(res, "Content-Type") || + getStatusCode(res) === 404 + ) { + removeResponseHeader(res, "Content-Type"); + // content-type name(like application/javascript; charset=utf-8) or false + const contentType = mime.contentType(path.extname(filename)); + + // Only set content-type header if media type is known + // https://tools.ietf.org/html/rfc7231#section-3.1.1.5 + if (contentType) { + setResponseHeader(res, "Content-Type", contentType); + } else if (context.options.mimeTypeDefault) { + setResponseHeader( + res, + "Content-Type", + context.options.mimeTypeDefault, + ); + } + } + // Conditional GET support if (isConditionalGET()) { if (isPreconditionFailure()) { diff --git a/src/utils/compatibleAPI.js b/src/utils/compatibleAPI.js index b86e32481..7e5518b15 100644 --- a/src/utils/compatibleAPI.js +++ b/src/utils/compatibleAPI.js @@ -19,6 +19,7 @@ * @property {(data: string | Buffer) => void} [send] * @property {(data?: string | Buffer) => void} [finish] * @property {() => string[]} [getResponseHeaders] + * @property {() => boolean} [getHeadersSent] * @property {(data: any) => void} [stream] * @property {() => any} [getOutgoing] * @property {(name: string, value: any) => void} [setState] @@ -147,6 +148,20 @@ function getResponseHeaders(res) { return res.getHeaderNames(); } +/** + * @template {ServerResponse & ExpectedServerResponse} Response + * @param {Response} res + * @returns {boolean} + */ +function getHeadersSent(res) { + // Pseudo API + if (typeof res.getHeadersSent === "function") { + return res.getHeadersSent(); + } + + return res.headersSent; +} + /** * @template {ServerResponse & ExpectedServerResponse} Response * @param {Response} res @@ -305,6 +320,7 @@ module.exports = { setResponseHeader, removeResponseHeader, getResponseHeaders, + getHeadersSent, pipe, send, finish, diff --git a/src/utils/getFilenameFromUrl.js b/src/utils/getFilenameFromUrl.js index 0fe40c232..d3fd80e7e 100644 --- a/src/utils/getFilenameFromUrl.js +++ b/src/utils/getFilenameFromUrl.js @@ -129,6 +129,7 @@ function getFilenameFromUrl(context, url, extra = {}) { pathname.slice(publicPathObject.pathname.length), ); + // eslint-disable-next-line no-param-reassign extra.immutable = assetInfo ? assetInfo.immutable : false; } diff --git a/test/middleware.test.js b/test/middleware.test.js index d17db2715..a9f6ac17a 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -3287,6 +3287,97 @@ describe.each([ ); }); }); + + describe("should work when headers are already sent", () => { + let compiler; + + const outputPath = path.resolve( + __dirname, + "./outputs/basic-test-errors-headers-sent", + ); + + beforeAll(async () => { + compiler = getCompiler({ + ...webpackConfig, + output: { + filename: "bundle.js", + path: outputPath, + }, + }); + + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + {}, + { + setupMiddlewares: (middlewares) => { + if (name === "hapi") { + // There's no such thing as "the next route handler" in hapi. One request is matched to one or no route handlers. + } else if (name === "koa") { + middlewares.push(async (ctx, next) => { + // eslint-disable-next-line no-param-reassign + ctx.url = "/index.html"; + + await next(); + }); + middlewares.push(middleware.koaWrapper(compiler, {})); + } else if (name === "hono") { + middlewares.unshift(async (c, next) => { + await next(); + + return new Response("Hello Node.js!"); + }); + middlewares.push(middleware.honoWrapper(compiler, {})); + } else { + middlewares.push({ + route: "/", + fn: (req, res, next) => { + // eslint-disable-next-line no-param-reassign + req.url = "/index.html"; + next(); + }, + }); + middlewares.push(middleware(compiler, {})); + } + + return middlewares; + }, + }, + ); + + instance.context.outputFileSystem.mkdirSync(outputPath, { + recursive: true, + }); + instance.context.outputFileSystem.writeFileSync( + path.resolve(outputPath, "index.html"), + "HTML", + ); + }); + + afterAll(async () => { + await close(server, instance); + }); + + it('should return the "200" code for the "GET" request to the bundle file', async () => { + const response = await req.get("/"); + + expect(response.statusCode).toEqual(200); + expect(response.headers["content-type"]).toEqual( + "text/html; charset=utf-8", + ); + }); + + it('should return the "200" code for the "HEAD" request to the bundle file', async () => { + const response = await req.head("/"); + + expect(response.statusCode).toEqual(200); + expect(response.headers["content-type"]).toEqual( + "text/html; charset=utf-8", + ); + expect(response.text).toBeUndefined(); + }); + }); }); describe("mimeTypes option", () => { @@ -4467,6 +4558,7 @@ describe.each([ middlewares.push(async (ctx, next) => { locals = ctx.state; + // eslint-disable-next-line no-param-reassign ctx.status = 200; await next(); diff --git a/types/index.d.ts b/types/index.d.ts index f6886e1bd..18c9971dd 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -213,7 +213,7 @@ declare namespace wdm { /** * @template S * @template O - * @typedef {HapiPluginBase & { pkg: { name: string } }} HapiPlugin + * @typedef {HapiPluginBase & { pkg: { name: string }, multiple: boolean }} HapiPlugin */ /** * @typedef {Options & { compiler: Compiler | MultiCompiler }} HapiOptions @@ -409,6 +409,7 @@ type HapiPlugin = HapiPluginBase & { pkg: { name: string; }; + multiple: boolean; }; type HapiOptions = Options & { compiler: Compiler | MultiCompiler; diff --git a/types/utils/compatibleAPI.d.ts b/types/utils/compatibleAPI.d.ts index 5a3ab777a..7500fcd0c 100644 --- a/types/utils/compatibleAPI.d.ts +++ b/types/utils/compatibleAPI.d.ts @@ -22,6 +22,7 @@ export type ExpectedServerResponse = { send?: ((data: string | Buffer) => void) | undefined; finish?: ((data?: string | Buffer) => void) | undefined; getResponseHeaders?: (() => string[]) | undefined; + getHeadersSent?: (() => boolean) | undefined; stream?: ((data: any) => void) | undefined; getOutgoing?: (() => any) | undefined; setState?: ((name: string, value: any) => void) | undefined; @@ -64,6 +65,7 @@ export function getStatusCode< * @property {(data: string | Buffer) => void} [send] * @property {(data?: string | Buffer) => void} [finish] * @property {() => string[]} [getResponseHeaders] + * @property {() => boolean} [getHeadersSent] * @property {(data: any) => void} [stream] * @property {() => any} [getOutgoing] * @property {(name: string, value: any) => void} [setState] @@ -133,6 +135,14 @@ export function removeResponseHeader< export function getResponseHeaders< Response extends ServerResponse & ExpectedServerResponse, >(res: Response): string[]; +/** + * @template {ServerResponse & ExpectedServerResponse} Response + * @param {Response} res + * @returns {boolean} + */ +export function getHeadersSent< + Response extends ServerResponse & ExpectedServerResponse, +>(res: Response): boolean; /** * @template {ServerResponse & ExpectedServerResponse} Response * @param {Response} res