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 <malfait.robin@gmail.com>
This commit is contained in:
ivan 2024-09-23 16:52:05 +08:00 committed by GitHub
parent e8614a268d
commit 066ccf8894
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 23 additions and 7 deletions

View File

@ -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

View File

@ -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)