From f588adaf179535b9c2ab45a56e7dc17dcd821ab6 Mon Sep 17 00:00:00 2001 From: Joe Lencioni Date: Mon, 8 Jun 2020 12:49:20 -0500 Subject: [PATCH] Add code comments to globsToMatcher The logic here might be a little confusing, so I am adding some comments that I hope will help make it easier for future explorers to understand. While I was doing this, I noticed a small way to simplify this function even more. --- packages/jest-util/src/globsToMatcher.ts | 40 +++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/packages/jest-util/src/globsToMatcher.ts b/packages/jest-util/src/globsToMatcher.ts index 52f72c11b395..6c8bf4baa804 100644 --- a/packages/jest-util/src/globsToMatcher.ts +++ b/packages/jest-util/src/globsToMatcher.ts @@ -17,29 +17,44 @@ const globsMatchers = new Map< } >(); +// Every time micromatch is called, it will parse the glob strings and turn them +// into regexp instances. Instead of calling micromatch repeatedly with the same +// globs, we can use this function which will build the micromatch matchers +// ahead of time and then have an optimized path for determining whether an +// individual path matches. +// +// This function is intended to match the behavior of `micromatch()`. + export default function globsToMatcher( globs: Array, ): (path: Config.Path) => boolean { if (globs.length === 0) { + // Since there were no globs given, we can simply have a fast path here and + // return with a very simple function. return (_: Config.Path): boolean => false; } + const micromatchOptions = {dot: true}; const matchers = globs.map(glob => { if (!globsMatchers.has(glob)) { - const state = micromatch.scan(glob, {dot: true}); + // Matchers that are negated have different behavior than matchers that + // are not negated, so we need to store this information ahead of time. + const {negated} = micromatch.scan(glob, micromatchOptions); + const matcher = { - isMatch: micromatch.matcher(glob, {dot: true}), - negated: state.negated, + isMatch: micromatch.matcher(glob, micromatchOptions), + negated, }; + globsMatchers.set(glob, matcher); } + return globsMatchers.get(glob)!; }); return (path: Config.Path): boolean => { const replacedPath = replacePathSepForGlob(path); - let kept = false; - let omitted = false; + let kept = undefined; let negatives = 0; for (let i = 0; i < matchers.length; i++) { @@ -50,14 +65,23 @@ export default function globsToMatcher( const matched = isMatch(replacedPath); if (!matched && negated) { + // The path was not matched, and the matcher is a negated matcher, so we + // want to omit the path. This means that the negative matcher is + // filtering the path out. kept = false; - omitted = true; } else if (matched && !negated) { + // The path was matched, and the matcher is not a negated matcher, so we + // want to keep the path. kept = true; - omitted = false; } } - return negatives === matchers.length ? !omitted : kept && !omitted; + // If all of the globs were negative globs, then we want to include the path + // as long as it was not explicitly not kept. Otherwise only include + // the path if it was kept. This allows sets of globs that are all negated + // to allow some paths to be matched, while sets of globs that are mixed + // negated and non-negated to cause the negated matchers to only omit paths + // and not keep them. + return negatives === matchers.length ? kept !== false : !!kept; }; }