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..b3138160f2 100644
--- a/lib/Server.js
+++ b/lib/Server.js
@@ -8,6 +8,10 @@ const fs = require("graceful-fs");
 const ipaddr = require("ipaddr.js");
 const { validate } = require("schema-utils");
 const schema = require("./options.json");
+const {
+  getGlobbedWatcherPaths,
+  getIgnoreMatchers,
+} = require("./getGlobMatchers");
 
 /** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
 /** @typedef {import("webpack").Compiler} Compiler */
@@ -18,7 +22,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 & { disableGlobbing?: boolean }} WatchOptions */
 /** @typedef {import("chokidar").FSWatcher} FSWatcher */
 /** @typedef {import("connect-history-api-fallback").Options} ConnectHistoryApiFallbackOptions */
 /** @typedef {import("bonjour-service").Bonjour} Bonjour */
@@ -312,6 +316,19 @@ function useFn(route, fn) {
  * @property {typeof useFn} use
  */
 
+/**
+ * @template {Record<string, any>} T
+ * @param {T} obj
+ * @returns {T}
+ */
+function removeUndefinedValues(obj) {
+  return /** @type {T} **/ (
+    Object.fromEntries(
+      Object.entries(obj).filter(([, value]) => typeof value !== "undefined"),
+    )
+  );
+}
+
 /**
  * @template {BasicApplication} [A=ExpressApplication]
  * @template {BasicServer} [S=HTTPServer]
@@ -3255,9 +3272,21 @@ 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 [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      watchPath,
+      watchOptions,
+    );
+
+    watchOptions.ignored = getIgnoreMatchers(watchOptions, ignoreFunction);
+
+    const watcher = chokidar.watch(
+      watchPaths,
+      // https://github.com/paulmillr/chokidar/issues/1394
+      removeUndefinedValues(watchOptions),
+    );
 
     // disabling refreshing on changing the content
     if (this.options.liveReload) {
diff --git a/lib/getGlobMatchers.js b/lib/getGlobMatchers.js
new file mode 100644
index 0000000000..c75d5e1acf
--- /dev/null
+++ b/lib/getGlobMatchers.js
@@ -0,0 +1,76 @@
+"use strict";
+
+module.exports = {
+  /**
+   * @param {string[] | string} _watchPaths
+   * @param {import("./Server").WatchOptions} watchOptions
+   * @returns {[string[], import("chokidar").MatchFunction | null]}*/
+  getGlobbedWatcherPaths(_watchPaths, { disableGlobbing, cwd }) {
+    const watchPaths = Array.isArray(_watchPaths) ? _watchPaths : [_watchPaths];
+
+    if (disableGlobbing === true) {
+      return [watchPaths, null];
+    }
+
+    const picomatch = require("picomatch");
+    const isGlob = require("is-glob");
+    const watchPathGlobs = watchPaths.filter((p) => isGlob(p));
+
+    if (watchPathGlobs.length === 0) {
+      return [watchPaths, null];
+    }
+
+    const globParent = require("glob-parent");
+
+    watchPathGlobs.forEach((p) => {
+      watchPaths[watchPaths.indexOf(p)] = globParent(p);
+    });
+
+    const matcher = picomatch(watchPathGlobs, { cwd, dot: true });
+
+    /** @type {import("chokidar").MatchFunction} */
+    const ignoreFunction = (p) => !watchPaths.includes(p) && !matcher(p);
+
+    // Ignore all paths that don't match any of the globs
+    return [watchPaths, ignoreFunction];
+  },
+
+  /**
+   *
+   * @param {import("./Server").WatchOptions} watchOptions
+   * @param {import("chokidar").MatchFunction | null } ignoreFunction
+   * @returns {import("chokidar").Matcher[]}
+   */
+  getIgnoreMatchers({ disableGlobbing, ignored, cwd }, ignoreFunction) {
+    const _ignored = /** @type {import("chokidar").Matcher[]}**/ (
+      typeof ignored === "undefined" ? [] : [ignored]
+    );
+    const matchers = Array.isArray(ignored) ? ignored : _ignored;
+
+    if (disableGlobbing === true) {
+      return matchers;
+    }
+
+    if (ignoreFunction) {
+      matchers.push(ignoreFunction);
+    }
+
+    const picomatch = require("picomatch");
+    const isGlob = require("is-glob");
+
+    // Double filter to satisfy typescript. Otherwise nasty casting is required
+    const ignoredGlobs = matchers
+      .filter((s) => typeof s === "string")
+      .filter((s) => isGlob(s));
+
+    if (ignoredGlobs.length === 0) {
+      return matchers;
+    }
+
+    const matcher = picomatch(ignoredGlobs, { cwd, dot: true });
+
+    matchers.push(matcher);
+
+    return matchers.filter((s) => typeof s !== "string" || !isGlob(s));
+  },
+};
diff --git a/package-lock.json b/package-lock.json
index 39ec8a4fe7..126711d2c8 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.3",
         "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",
@@ -4147,6 +4217,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",
@@ -4175,6 +4251,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",
@@ -4255,6 +4337,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",
@@ -4758,6 +4846,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"
@@ -4766,6 +4855,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",
@@ -5279,6 +5380,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"
       },
@@ -5594,26 +5697,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.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
       "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": {
@@ -11524,6 +11607,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"
       },
@@ -13460,6 +13545,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",
@@ -14587,6 +14684,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",
@@ -14885,6 +14993,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"
       }
@@ -15520,11 +15629,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"
@@ -16191,14 +16300,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": {
@@ -18235,18 +18345,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..587dae0ed2 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.3",
     "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..1eedc2924a 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.",
@@ -47,7 +57,7 @@ exports[`watchFiles option should work with options {"interval":400,"poll":200}
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
+  "ignored": [],
   "interval": 400,
   "persistent": true,
   "usePolling": true,
@@ -71,7 +81,7 @@ exports[`watchFiles option should work with options {"poll":200} should reload w
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
+  "ignored": [],
   "interval": 200,
   "persistent": true,
   "usePolling": true,
@@ -95,8 +105,7 @@ exports[`watchFiles option should work with options {"poll":true} should reload
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
-  "interval": undefined,
+  "ignored": [],
   "persistent": true,
   "usePolling": true,
 }
@@ -119,7 +128,7 @@ exports[`watchFiles option should work with options {"usePolling":false,"interva
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
+  "ignored": [],
   "interval": 200,
   "persistent": true,
   "usePolling": false,
@@ -143,7 +152,7 @@ exports[`watchFiles option should work with options {"usePolling":false,"poll":2
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
+  "ignored": [],
   "interval": 200,
   "persistent": true,
   "usePolling": false,
@@ -167,8 +176,7 @@ exports[`watchFiles option should work with options {"usePolling":false,"poll":t
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
-  "interval": undefined,
+  "ignored": [],
   "persistent": true,
   "usePolling": false,
 }
@@ -191,8 +199,7 @@ exports[`watchFiles option should work with options {"usePolling":false} should
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
-  "interval": undefined,
+  "ignored": [],
   "persistent": true,
   "usePolling": false,
 }
@@ -215,7 +222,7 @@ exports[`watchFiles option should work with options {"usePolling":true,"interval
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
+  "ignored": [],
   "interval": 200,
   "persistent": true,
   "usePolling": true,
@@ -239,7 +246,7 @@ exports[`watchFiles option should work with options {"usePolling":true,"poll":20
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
+  "ignored": [],
   "interval": 200,
   "persistent": true,
   "usePolling": true,
@@ -263,8 +270,7 @@ exports[`watchFiles option should work with options {"usePolling":true} should r
   "followSymlinks": false,
   "ignoreInitial": true,
   "ignorePermissionErrors": true,
-  "ignored": undefined,
-  "interval": undefined,
+  "ignored": [],
   "persistent": true,
   "usePolling": true,
 }
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/test/unit/globIgnoreMatchers.test.js b/test/unit/globIgnoreMatchers.test.js
new file mode 100644
index 0000000000..10dbc171c4
--- /dev/null
+++ b/test/unit/globIgnoreMatchers.test.js
@@ -0,0 +1,159 @@
+"use strict";
+
+const { getIgnoreMatchers } = require("../../lib/getGlobMatchers");
+
+describe("getIgnoreMatchers", () => {
+  it("should return an array of matchers for glob strings", () => {
+    const watchOptions = {
+      cwd: process.cwd(),
+      ignored: ["src/*.js", "tests/*.spec.js"],
+    };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(typeof matchers[0]).toBe("function");
+  });
+
+  it("should return the original value for non-glob strings", () => {
+    const watchOptions = { cwd: process.cwd(), ignored: "src/file.txt" };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(matchers[0]).toBe("src/file.txt");
+  });
+
+  it("should return empty array if ignored is not defined", () => {
+    const watchOptions = { cwd: process.cwd() };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toEqual([]);
+  });
+
+  it("should return an array that includes the passed matcher function", () => {
+    const watchOptions = { cwd: process.cwd() };
+    const ignoreFunction = () => true;
+    const matchers = getIgnoreMatchers(watchOptions, ignoreFunction);
+
+    expect(matchers).toHaveLength(1);
+    expect(matchers[0]).toBe(ignoreFunction);
+  });
+
+  it("should return all original value and only replace all globs with one function", () => {
+    const ignoreFunction = () => true;
+    const regex = /src\/.*\.js/;
+    const watchOptions = {
+      cwd: process.cwd(),
+      ignored: [
+        "src/*.js",
+        "src/file.txt",
+        "src/**/*.js",
+        ignoreFunction,
+        regex,
+      ],
+    };
+
+    const matchers = getIgnoreMatchers(watchOptions, ignoreFunction);
+
+    expect(matchers).toHaveLength(5);
+    expect(matchers[0]).toBe("src/file.txt");
+    expect(matchers[1]).toBe(ignoreFunction);
+    expect(matchers[2]).toBe(regex);
+    expect(matchers[3]).toBe(ignoreFunction);
+    expect(typeof matchers[4]).toBe("function");
+  });
+
+  it("should work with complicated glob", () => {
+    const watchOptions = {
+      cwd: process.cwd(),
+      ignored: ["src/**/components/*.js"],
+    };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(typeof matchers[0]).toBe("function");
+
+    const filePath = "src/components/file.txt";
+    expect(matchers[0](filePath)).toBe(false);
+
+    const jsFilePath = "src/components/file.js";
+    expect(matchers[0](jsFilePath)).toBe(true);
+
+    const jsFilePath2 = "src/other/components/file.js";
+    expect(matchers[0](jsFilePath2)).toBe(true);
+
+    const nestedJsFilePath = "src/components/nested/file.js";
+    expect(matchers[0](nestedJsFilePath)).toBe(false);
+  });
+
+  it("should work with negated glob", () => {
+    const watchOptions = {
+      cwd: process.cwd(),
+      ignored: ["src/**/components/!(*.spec).js"],
+    };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(typeof matchers[0]).toBe("function");
+
+    const filePath = "src/components/file.txt";
+    expect(matchers[0](filePath)).toBe(false);
+
+    const jsFilePath = "src/components/file.js";
+    expect(matchers[0](jsFilePath)).toBe(true);
+
+    const specJsFilePath = "src/components/file.spec.js";
+    expect(matchers[0](specJsFilePath)).toBe(false);
+  });
+
+  it("should work with directory glob", () => {
+    const watchOptions = { cwd: process.cwd(), ignored: ["src/**"] };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(typeof matchers[0]).toBe("function");
+
+    const filePath = "src/file.txt";
+    expect(matchers[0](filePath)).toBe(true);
+
+    const dirPath = "src/subdir";
+    expect(matchers[0](dirPath)).toBe(true);
+
+    const nestedFilePath = "src/subdir/nested/file.txt";
+    expect(matchers[0](nestedFilePath)).toBe(true);
+
+    const wrongPath = "foo/bar";
+    expect(matchers[0](wrongPath)).toBe(false);
+  });
+
+  it("should work with directory glob and file extension", () => {
+    const watchOptions = { cwd: process.cwd(), ignored: ["src/**/*.{js,ts}"] };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(typeof matchers[0]).toBe("function");
+
+    const jsFilePath = "src/file.js";
+    expect(matchers[0](jsFilePath)).toBe(true);
+
+    const tsFilePath = "src/file.ts";
+    expect(matchers[0](tsFilePath)).toBe(true);
+
+    const txtFilePath = "src/file.txt";
+    expect(matchers[0](txtFilePath)).toBe(false);
+
+    const nestedJsFilePath = "src/subdir/nested/file.js";
+    expect(matchers[0](nestedJsFilePath)).toBe(true);
+  });
+
+  it("should return the input as array when globbing is disabled", () => {
+    const watchOptions = {
+      cwd: process.cwd(),
+      disableGlobbing: true,
+      ignored: "src/**/*.{js,ts}",
+    };
+    const matchers = getIgnoreMatchers(watchOptions, null);
+
+    expect(matchers).toHaveLength(1);
+    expect(matchers[0]).toBe("src/**/*.{js,ts}");
+  });
+});
diff --git a/test/unit/globWatchers.test.js b/test/unit/globWatchers.test.js
new file mode 100644
index 0000000000..54fd8d1c48
--- /dev/null
+++ b/test/unit/globWatchers.test.js
@@ -0,0 +1,140 @@
+"use strict";
+
+const { getGlobbedWatcherPaths } = require("../../lib/getGlobMatchers");
+
+describe("getGlobbedWatcherPaths", () => {
+  it("should watch the parent directory of the glob", () => {
+    const glob = "src/*.js";
+    const watchOptions = { cwd: process.cwd() };
+    const [watchPaths] = getGlobbedWatcherPaths(glob, watchOptions);
+
+    expect(watchPaths).toEqual(["src"]);
+  });
+
+  it("should ignore files that are not part of the glob", () => {
+    const glob = "src/*.js";
+    const watchOptions = { cwd: process.cwd() };
+    const [, ignoreFunction] = getGlobbedWatcherPaths(glob, watchOptions);
+
+    const filePath = "src/file.txt";
+    expect(ignoreFunction(filePath)).toBe(true);
+
+    const jsFilePath = "src/file.js";
+    expect(ignoreFunction(jsFilePath)).toBe(false);
+  });
+
+  it("should work with multiple globs", () => {
+    const globs = ["src/*.js", "tests/*.spec.js"];
+    const watchOptions = { cwd: process.cwd() };
+    const [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      globs,
+      watchOptions,
+    );
+
+    expect(watchPaths).toEqual(["src", "tests"]);
+
+    const filePath = "src/file.txt";
+    expect(ignoreFunction(filePath)).toBe(true);
+
+    const jsFilePath = "src/file.js";
+    expect(ignoreFunction(jsFilePath)).toBe(false);
+
+    const specFilePath = "tests/file.spec.js";
+    expect(ignoreFunction(specFilePath)).toBe(false);
+  });
+
+  it("should work with complicated glob", () => {
+    const glob = "src/**/components/*.js";
+    const watchOptions = { cwd: process.cwd() };
+    const [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      glob,
+      watchOptions,
+    );
+
+    expect(watchPaths).toEqual(["src"]);
+
+    const filePath = "src/components/file.txt";
+    expect(ignoreFunction(filePath)).toBe(true);
+
+    const jsFilePath = "src/components/file.js";
+    expect(ignoreFunction(jsFilePath)).toBe(false);
+
+    const nestedJsFilePath = "src/components/nested/file.js";
+    expect(ignoreFunction(nestedJsFilePath)).toBe(true);
+  });
+
+  it("should work with negated glob", () => {
+    const glob = "src/**/components/!(*.spec).js";
+    const watchOptions = { cwd: process.cwd() };
+    const [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      glob,
+      watchOptions,
+    );
+
+    expect(watchPaths).toEqual(["src"]);
+
+    const filePath = "src/components/file.txt";
+    expect(ignoreFunction(filePath)).toBe(true);
+
+    const jsFilePath = "src/components/file.js";
+    expect(ignoreFunction(jsFilePath)).toBe(false);
+
+    const specJsFilePath = "src/components/file.spec.js";
+    expect(ignoreFunction(specJsFilePath)).toBe(true);
+  });
+
+  it("should work with directory glob", () => {
+    const glob = "src/**";
+    const watchOptions = { cwd: process.cwd() };
+    const [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      glob,
+      watchOptions,
+    );
+
+    expect(watchPaths).toEqual(["src"]);
+
+    const filePath = "src/file.txt";
+    expect(ignoreFunction(filePath)).toBe(false);
+
+    const dirPath = "src/subdir";
+    expect(ignoreFunction(dirPath)).toBe(false);
+
+    const nestedFilePath = "src/subdir/nested/file.txt";
+    expect(ignoreFunction(nestedFilePath)).toBe(false);
+  });
+
+  it("should work with directory glob and file extension", () => {
+    const glob = "src/**/*.{js,ts}";
+    const watchOptions = { cwd: process.cwd() };
+    const [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      glob,
+      watchOptions,
+    );
+
+    expect(watchPaths).toEqual(["src"]);
+
+    const jsFilePath = "src/file.js";
+    expect(ignoreFunction(jsFilePath)).toBe(false);
+
+    const tsFilePath = "src/file.ts";
+    expect(ignoreFunction(tsFilePath)).toBe(false);
+
+    const txtFilePath = "src/file.txt";
+    expect(ignoreFunction(txtFilePath)).toBe(true);
+
+    const nestedJsFilePath = "src/subdir/nested/file.js";
+    expect(ignoreFunction(nestedJsFilePath)).toBe(false);
+  });
+
+  it("should return the input as array when globbing is disabled", () => {
+    const glob = "src/**/*.{js,ts}";
+    const watchOptions = { cwd: process.cwd(), disableGlobbing: true };
+    const [watchPaths, ignoreFunction] = getGlobbedWatcherPaths(
+      glob,
+      watchOptions,
+    );
+
+    expect(watchPaths).toEqual(["src/**/*.{js,ts}"]);
+    expect(ignoreFunction).toBe(null);
+  });
+});
diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts
index 57ee326f17..a66c0be75b 100644
--- a/types/lib/Server.d.ts
+++ b/types/lib/Server.d.ts
@@ -1,8 +1,4 @@
 export = Server;
-/**
- * @typedef {Object} BasicApplication
- * @property {typeof useFn} use
- */
 /**
  * @template {BasicApplication} [A=ExpressApplication]
  * @template {BasicServer} [S=HTTPServer]
@@ -1402,6 +1398,7 @@ declare class Server<
 declare namespace Server {
   export {
     DEFAULT_STATS,
+    BasicApplication,
     Schema,
     Compiler,
     MultiCompiler,
@@ -1469,12 +1466,14 @@ declare namespace Server {
     Middleware,
     BasicServer,
     Configuration,
-    BasicApplication,
   };
 }
 declare class DEFAULT_STATS {
   private constructor();
 }
+type BasicApplication = {
+  use: typeof useFn;
+};
 type Schema = import("schema-utils/declarations/validate").Schema;
 type Compiler = import("webpack").Compiler;
 type MultiCompiler = import("webpack").MultiCompiler;
@@ -1484,7 +1483,9 @@ 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 & {
+  disableGlobbing?: boolean;
+};
 type FSWatcher = import("chokidar").FSWatcher;
 type ConnectHistoryApiFallbackOptions =
   import("connect-history-api-fallback").Options;
@@ -1550,7 +1551,31 @@ 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;
+              }>;
+        }
+      > & {
+        disableGlobbing?: boolean;
+      } & {
         aggregateTimeout?: number;
         ignored?: WatchOptions["ignored"];
         poll?: number | boolean;
@@ -1568,7 +1593,31 @@ 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;
+              }>;
+        }
+      > & {
+        disableGlobbing?: boolean;
+      } & {
         aggregateTimeout?: number;
         ignored?: WatchOptions["ignored"];
         poll?: number | boolean;
@@ -1763,9 +1812,6 @@ type Configuration<
     | ((middlewares: Middleware[], devServer: Server<A, S>) => Middleware[])
     | undefined;
 };
-type BasicApplication = {
-  use: typeof useFn;
-};
 /**
  * @overload
  * @param {NextHandleFunction} fn
diff --git a/types/lib/getGlobMatchers.d.ts b/types/lib/getGlobMatchers.d.ts
new file mode 100644
index 0000000000..6229f0d124
--- /dev/null
+++ b/types/lib/getGlobMatchers.d.ts
@@ -0,0 +1,18 @@
+/**
+ * @param {string[] | string} _watchPaths
+ * @param {import("./Server").WatchOptions} watchOptions
+ * @returns {[string[], import("chokidar").MatchFunction | null]}*/
+export function getGlobbedWatcherPaths(
+  _watchPaths: string[] | string,
+  { disableGlobbing, cwd }: import("./Server").WatchOptions,
+): [string[], import("chokidar").MatchFunction | null];
+/**
+ *
+ * @param {import("./Server").WatchOptions} watchOptions
+ * @param {import("chokidar").MatchFunction | null } ignoreFunction
+ * @returns {import("chokidar").Matcher[]}
+ */
+export function getIgnoreMatchers(
+  { disableGlobbing, ignored, cwd }: import("./Server").WatchOptions,
+  ignoreFunction: import("chokidar").MatchFunction | null,
+): import("chokidar").Matcher[];