From 9492a00dcc8199015cdf293982319419b1184e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulrich-Matthias=20Sch=C3=A4fer?= Date: Fri, 13 Dec 2024 14:50:21 +0100 Subject: [PATCH] chore(deps): update chokidar to v4 In order to replace chokidars removed glob functionality, we have to use some trickery. - `glob-parent` is used to get the common ancenstor dir that needs to be watched. - `picomatch` is used as ignore-filter to make sure that only files/dirs are watched that match the given glob - `is-glob` is used to only use glob logic when needed In total this adds 4 direct or transitive dependencies. However, the update to chokidar v4 removes 12. So its a net positive I also added a test to ensure that creation of nested directories is detected properly. --- .eslintignore | 1 + .gitignore | 1 + lib/Server.js | 33 ++- package-lock.json | 208 +++++++++++++----- package.json | 8 +- .../watch-files.test.js.snap.webpack5 | 10 + test/e2e/watch-files.test.js | 83 +++++++ types/lib/Server.d.ts | 50 ++++- 8 files changed, 332 insertions(+), 62 deletions(-) diff --git a/.eslintignore b/.eslintignore index 9bc9ffb002..18329f0a43 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,4 @@ node_modules examples/**/main.js examples/client/trusted-types-overlay/app.js test/fixtures/reload-config/foo.js +test/fixtures/worker-config-dev-server-false/public/worker-bundle.js diff --git a/.gitignore b/.gitignore index de3b694aec..3877986208 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ yarn-error.log test/fixtures/static-config/public/assets/non-exist.txt test/fixtures/watch-files-config/public/assets/non-exist.txt +test/fixtures/watch-files-config/public/non-existant/non-exist.txt test/fixtures/reload-config/main.css test/fixtures/reload-config-2/main.css test/fixtures/worker-config-dev-server-false/public diff --git a/lib/Server.js b/lib/Server.js index 80051e735e..640dc87d06 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -18,7 +18,7 @@ const schema = require("./options.json"); /** @typedef {import("webpack").Stats} Stats */ /** @typedef {import("webpack").MultiStats} MultiStats */ /** @typedef {import("os").NetworkInterfaceInfo} NetworkInterfaceInfo */ -/** @typedef {import("chokidar").WatchOptions} WatchOptions */ +/** @typedef {import("chokidar").ChokidarOptions} WatchOptions */ /** @typedef {import("chokidar").FSWatcher} FSWatcher */ /** @typedef {import("connect-history-api-fallback").Options} ConnectHistoryApiFallbackOptions */ /** @typedef {import("bonjour-service").Bonjour} Bonjour */ @@ -3255,9 +3255,36 @@ class Server { * @param {string | string[]} watchPath * @param {WatchOptions} [watchOptions] */ - watchFiles(watchPath, watchOptions) { + watchFiles(watchPath, watchOptions = {}) { const chokidar = require("chokidar"); - const watcher = chokidar.watch(watchPath, watchOptions); + const isGlob = require("is-glob"); + + const watchPathArr = Array.isArray(watchPath) ? watchPath : [watchPath]; + const watchPathGlobs = watchPathArr.filter((p) => isGlob(p)); + + // No need to do all this work when no globs are used + if (watchPathGlobs.length > 0) { + const globParent = require("glob-parent"); + const picomatch = require("picomatch"); + + watchPathGlobs.forEach((p) => { + watchPathArr[watchPathArr.indexOf(p)] = globParent(p); + }); + + const matcher = picomatch(watchPathGlobs); + const ignoreFunc = (/** @type {string} */ p) => + !watchPathArr.includes(p) && !matcher(p); + + if (Array.isArray(watchOptions.ignored)) { + watchOptions.ignored.push(ignoreFunc); + } else { + watchOptions.ignored = watchOptions.ignored + ? [watchOptions.ignored, ignoreFunc] + : ignoreFunc; + } + } + + const watcher = chokidar.watch(watchPathArr, watchOptions); // disabling refreshing on changing the content if (this.options.liveReload) { diff --git a/package-lock.json b/package-lock.json index 48a4337741..76c8cb9149 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,17 +18,20 @@ "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", + "chokidar": "^4.0.1", "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", "express": "^4.21.2", + "glob-parent": "^6.0.2", "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.7", "ipaddr.js": "^2.1.0", + "is-glob": "^4.0.3", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", + "picomatch": "^4.0.2", "schema-utils": "^4.2.0", "selfsigned": "^2.4.1", "serve-index": "^1.9.1", @@ -52,8 +55,11 @@ "@commitlint/config-conventional": "^19.5.0", "@hono/node-server": "^1.13.3", "@types/compression": "^1.7.2", + "@types/glob-parent": "^5.1.3", + "@types/is-glob": "^4.0.4", "@types/node": "^22.8.4", "@types/node-forge": "^1.3.1", + "@types/picomatch": "^3.0.1", "@types/sockjs-client": "^1.5.1", "@types/trusted-types": "^2.0.2", "acorn": "^8.14.0", @@ -161,6 +167,70 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/cli/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@babel/cli/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@babel/cli/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@babel/cli/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", @@ -4146,6 +4216,12 @@ "@types/send": "*" } }, + "node_modules/@types/glob-parent": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@types/glob-parent/-/glob-parent-5.1.3.tgz", + "integrity": "sha512-p+NciRH8TRvrgISOCQ55CP+lktMmDpOXsp4spULIIz0L4aJ6G9zFX+N0UZ2xulmJRgaQLRxXIp4xHdL6YOQjDg==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -4174,6 +4250,12 @@ "@types/node": "*" } }, + "node_modules/@types/is-glob": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/is-glob/-/is-glob-4.0.4.tgz", + "integrity": "sha512-3mFBtIPQ0TQetKRDe94g8YrxJZxdMillMGegyv6zRBXvq4peRRhf2wLZ/Dl53emtTsC29dQQBwYvovS20yXpiQ==", + "dev": true + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -4254,6 +4336,12 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, + "node_modules/@types/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-1MRgzpzY0hOp9pW/kLRxeQhUWwil6gnrUYd3oEpeYBqp/FexhaCPv3F8LsYr47gtUU45fO2cm1dbwkSrHEo8Uw==", + "dev": true + }, "node_modules/@types/qs": { "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", @@ -4757,6 +4845,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -4765,6 +4854,18 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5278,6 +5379,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "optional": true, "engines": { "node": ">=8" }, @@ -5593,26 +5696,17 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" } }, "node_modules/chrome-trace-event": { @@ -9113,18 +9207,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/eslint/node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -9957,6 +10039,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -10682,14 +10765,14 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/glob-to-regexp": { @@ -11525,6 +11608,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "optional": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -13461,6 +13546,18 @@ "node": ">=8" } }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/jest-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -14588,6 +14685,17 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -14886,6 +14994,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -15521,11 +15630,11 @@ "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -16192,14 +16301,15 @@ } }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "engines": { - "node": ">=8.10.0" + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/rechoir": { @@ -18236,18 +18346,6 @@ } } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", diff --git a/package.json b/package.json index 8a1d0b7725..287d55022b 100644 --- a/package.json +++ b/package.json @@ -55,17 +55,20 @@ "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", + "chokidar": "^4.0.1", "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", "express": "^4.21.2", + "glob-parent": "^6.0.2", "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.7", "ipaddr.js": "^2.1.0", + "is-glob": "^4.0.3", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", + "picomatch": "^4.0.2", "schema-utils": "^4.2.0", "selfsigned": "^2.4.1", "serve-index": "^1.9.1", @@ -86,8 +89,11 @@ "@commitlint/config-conventional": "^19.5.0", "@hono/node-server": "^1.13.3", "@types/compression": "^1.7.2", + "@types/glob-parent": "^5.1.3", + "@types/is-glob": "^4.0.4", "@types/node": "^22.8.4", "@types/node-forge": "^1.3.1", + "@types/picomatch": "^3.0.1", "@types/sockjs-client": "^1.5.1", "@types/trusted-types": "^2.0.2", "acorn": "^8.14.0", diff --git a/test/e2e/__snapshots__/watch-files.test.js.snap.webpack5 b/test/e2e/__snapshots__/watch-files.test.js.snap.webpack5 index 5c63e7b714..fcff80b9ad 100644 --- a/test/e2e/__snapshots__/watch-files.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/watch-files.test.js.snap.webpack5 @@ -20,6 +20,16 @@ exports[`watchFiles option should work with array config should reload when file exports[`watchFiles option should work with array config should reload when file content is changed: response status 1`] = `200`; +exports[`watchFiles option should work with glob when creating nested directory should reload when file content is changed: console messages 1`] = ` +[ + "Hey.", +] +`; + +exports[`watchFiles option should work with glob when creating nested directory should reload when file content is changed: page errors 1`] = `[]`; + +exports[`watchFiles option should work with glob when creating nested directory should reload when file content is changed: response status 1`] = `200`; + exports[`watchFiles option should work with object with multiple paths should reload when file content is changed: console messages 1`] = ` [ "Hey.", diff --git a/test/e2e/watch-files.test.js b/test/e2e/watch-files.test.js index 6c70b2d8fa..997076b04e 100644 --- a/test/e2e/watch-files.test.js +++ b/test/e2e/watch-files.test.js @@ -309,6 +309,89 @@ describe("watchFiles option", () => { }); }); + describe("should work with glob when creating nested directory", () => { + const nonExistFile = path.join(watchDir, "non-existant/non-exist.txt"); + let compiler; + let server; + let page; + let browser; + let pageErrors; + let consoleMessages; + + beforeEach(async () => { + try { + fs.unlinkSync(nonExistFile); + fs.rmdirSync(path.join(watchDir, "non-existant")); + } catch (error) { + // ignore + } + + compiler = webpack(config); + + server = new Server( + { + watchFiles: `${watchDir}/**/*`, + port, + }, + compiler, + ); + + await server.start(); + + ({ page, browser } = await runBrowser()); + + pageErrors = []; + consoleMessages = []; + }); + + afterEach(async () => { + await browser.close(); + await server.stop(); + }); + + it("should reload when file content is changed", async () => { + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + const response = await page.goto(`http://127.0.0.1:${port}/`, { + waitUntil: "networkidle0", + }); + + expect(response.status()).toMatchSnapshot("response status"); + + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages", + ); + + expect(pageErrors).toMatchSnapshot("page errors"); + + await new Promise((resolve) => { + server.staticWatchers[0].on("change", async (changedPath) => { + // page reload + await page.waitForNavigation({ waitUntil: "networkidle0" }); + + expect(changedPath).toBe(nonExistFile); + resolve(); + }); + + // create file content + setTimeout(() => { + fs.mkdirSync(path.join(watchDir, "non-existant")); + fs.writeFileSync(nonExistFile, "Kurosaki Ichigo", "utf8"); + // change file content + setTimeout(() => { + fs.writeFileSync(nonExistFile, "Kurosaki Ichigo", "utf8"); + }, 1000); + }, 1000); + }); + }); + }); + describe("should work with object with single path", () => { const file = path.join(watchDir, "assets/example.txt"); let compiler; diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts index 57ee326f17..92e821a3ab 100644 --- a/types/lib/Server.d.ts +++ b/types/lib/Server.d.ts @@ -1484,7 +1484,7 @@ type StatsCompilation = import("webpack").StatsCompilation; type Stats = import("webpack").Stats; type MultiStats = import("webpack").MultiStats; type NetworkInterfaceInfo = import("os").NetworkInterfaceInfo; -type WatchOptions = import("chokidar").WatchOptions; +type WatchOptions = import("chokidar").ChokidarOptions; type FSWatcher = import("chokidar").FSWatcher; type ConnectHistoryApiFallbackOptions = import("connect-history-api-fallback").Options; @@ -1550,7 +1550,29 @@ type Port = number | string | "auto"; type WatchFiles = { paths: string | string[]; options?: - | (import("chokidar").WatchOptions & { + | (Partial< + { + persistent: boolean; + ignoreInitial: boolean; + followSymlinks: boolean; + cwd?: string; + usePolling: boolean; + interval: number; + binaryInterval: number; + alwaysStat?: boolean; + depth?: number; + ignorePermissionErrors: boolean; + atomic: boolean | number; + } & { + ignored: import("chokidar").Matcher | import("chokidar").Matcher[]; + awaitWriteFinish: + | boolean + | Partial<{ + stabilityThreshold: number; + pollInterval: number; + }>; + } + > & { aggregateTimeout?: number; ignored?: WatchOptions["ignored"]; poll?: number | boolean; @@ -1568,7 +1590,29 @@ type Static = { | undefined; watch?: | boolean - | (import("chokidar").WatchOptions & { + | (Partial< + { + persistent: boolean; + ignoreInitial: boolean; + followSymlinks: boolean; + cwd?: string; + usePolling: boolean; + interval: number; + binaryInterval: number; + alwaysStat?: boolean; + depth?: number; + ignorePermissionErrors: boolean; + atomic: boolean | number; + } & { + ignored: import("chokidar").Matcher | import("chokidar").Matcher[]; + awaitWriteFinish: + | boolean + | Partial<{ + stabilityThreshold: number; + pollInterval: number; + }>; + } + > & { aggregateTimeout?: number; ignored?: WatchOptions["ignored"]; poll?: number | boolean;