From 456b837c2e0ec54cf4dee5804e773eded5b2c378 Mon Sep 17 00:00:00 2001 From: ivanwonder Date: Fri, 20 Sep 2024 21:04:29 +0800 Subject: [PATCH 1/3] Improve the performance when checking broad glob patterns. In a large project, it's costly to repeatedly call the function `micromatch.isMatch` that parses a glob pattern, creates a regular expression, and tests the path name against the regular expression. To optimize performance, it's important to cache the parsing and creating process before entering the loop. For example, the content configuration in a project looks like this `['./pages/**/*.{ts,js}', './node_modules/pages/**/*.{ts,js}']`. If the project has 10000 matched files and 10 glob patterns, the function `micromatch.isMatch` will be called 100000 times. Fixes #14353 --- src/lib/content.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/content.js b/src/lib/content.js index 1a9d3c70e741..662afdac3400 100644 --- a/src/lib/content.js +++ b/src/lib/content.js @@ -210,9 +210,12 @@ export function createBroadPatternCheck(paths) { return () => {} } + const pathsMatcher = paths.map((path) => micromatch.matcher(path)) // All globs that explicitly contain any of the known large directories (e.g.: // node_modules). - let explicitGlobs = paths.filter((path) => LARGE_DIRECTORIES_REGEX.test(path)) + let explicitGlobsMatcher = paths + .filter((path) => LARGE_DIRECTORIES_REGEX.test(path)) + .map((path) => micromatch.matcher(path)) // Keep track of whether we already warned about the broad pattern issue or // not. The `log.warn` function already does something similar where we only @@ -225,11 +228,12 @@ export function createBroadPatternCheck(paths) { */ return (file) => { if (warned) return // Already warned about the broad pattern - if (micromatch.isMatch(file, explicitGlobs)) return // Explicitly included, so we can skip further checks + if (explicitGlobsMatcher.some((matcher) => matcher(file))) return // Explicitly included, so we can skip further checks // When a broad pattern is used, we have to double check that the file was // not explicitly included in the globs. - let matchingGlob = paths.find((path) => micromatch.isMatch(file, path)) + let matchingGlobIndex = pathsMatcher.findIndex((matcher) => matcher(file)) + let matchingGlob = paths[matchingGlobIndex] if (!matchingGlob) return // This should never happen // Create relative paths to make the output a bit more readable. From 2bdc836a349182563bf119347ab6de98db3e6498 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 20 Sep 2024 17:15:38 +0200 Subject: [PATCH 2/3] refactor matchers setup --- src/lib/content.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/lib/content.js b/src/lib/content.js index 662afdac3400..3e23f63c2e12 100644 --- a/src/lib/content.js +++ b/src/lib/content.js @@ -210,12 +210,22 @@ export function createBroadPatternCheck(paths) { return () => {} } - const pathsMatcher = paths.map((path) => micromatch.matcher(path)) - // All globs that explicitly contain any of the known large directories (e.g.: - // node_modules). - let explicitGlobsMatcher = paths - .filter((path) => LARGE_DIRECTORIES_REGEX.test(path)) - .map((path) => micromatch.matcher(path)) + // All glob matchers + let matchers = [] + + // All glob matchers that explicitly contain any of the known large + // directories (e.g.: node_modules). + let explicitMatchers = [] + + // Create matchers for all paths + for (let path of paths) { + let matcher = micromatch.matcher(path) + if (LARGE_DIRECTORIES_REGEX.test(path)) { + explicitMatchers.push(matcher) + } + + matchers.push(matcher) + } // Keep track of whether we already warned about the broad pattern issue or // not. The `log.warn` function already does something similar where we only @@ -228,13 +238,13 @@ export function createBroadPatternCheck(paths) { */ return (file) => { if (warned) return // Already warned about the broad pattern - if (explicitGlobsMatcher.some((matcher) => matcher(file))) return // Explicitly included, so we can skip further checks + if (explicitMatchers.some((matcher) => matcher(file))) return // Explicitly included, so we can skip further checks // When a broad pattern is used, we have to double check that the file was // not explicitly included in the globs. - let matchingGlobIndex = pathsMatcher.findIndex((matcher) => matcher(file)) + let matchingGlobIndex = matchers.findIndex((matcher) => matcher(file)) + if (matchingGlobIndex === -1) return // This should never happen let matchingGlob = paths[matchingGlobIndex] - if (!matchingGlob) return // This should never happen // Create relative paths to make the output a bit more readable. let relativeMatchingGlob = path.relative(process.cwd(), matchingGlob) From fac4f4dd362fd7df473e25d8b2fbe794723e3b8e Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Mon, 23 Sep 2024 10:47:43 +0200 Subject: [PATCH 3/3] update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c2257cc373e..c1064ac0d6e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Improve source glob verification performance ([#14481](https://github.com/tailwindlabs/tailwindcss/pull/14481)) ## [3.4.12] - 2024-09-17