From 066ccf88947cd882a8e59843c61df74615eedd2f Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 23 Sep 2024 16:52:05 +0800 Subject: [PATCH] Improve the performance when checking broad glob patterns. (#14481) 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. --- Co-authored-by: Robin Malfait --- CHANGELOG.md | 4 +++- src/lib/content.js | 26 ++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) 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 diff --git a/src/lib/content.js b/src/lib/content.js index 1a9d3c70e741..3e23f63c2e12 100644 --- a/src/lib/content.js +++ b/src/lib/content.js @@ -210,9 +210,22 @@ export function createBroadPatternCheck(paths) { return () => {} } - // 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)) + // 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 @@ -225,12 +238,13 @@ 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 (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 matchingGlob = paths.find((path) => micromatch.isMatch(file, path)) - if (!matchingGlob) return // This should never happen + let matchingGlobIndex = matchers.findIndex((matcher) => matcher(file)) + if (matchingGlobIndex === -1) return // This should never happen + let matchingGlob = paths[matchingGlobIndex] // Create relative paths to make the output a bit more readable. let relativeMatchingGlob = path.relative(process.cwd(), matchingGlob)