Skip to content

Commit

Permalink
Improve the performance when checking broad glob patterns. (#14481)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ivanwonder and RobinMalfait authored Sep 23, 2024
1 parent e8614a2 commit 066ccf8
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 7 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
26 changes: 20 additions & 6 deletions src/lib/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down

0 comments on commit 066ccf8

Please sign in to comment.