-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve Jest startup time and test runtime, particularly when running…
… with coverage, by caching micromatch and avoiding recreating RegExp instances (#10131) Co-authored-by: Christoph Nakazawa <[email protected]>
- Loading branch information
Showing
9 changed files
with
244 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import micromatch = require('micromatch'); | ||
import globsToMatcher from '../globsToMatcher'; | ||
|
||
it('works like micromatch with only positive globs', () => { | ||
const globs = ['**/*.test.js', '**/*.test.jsx']; | ||
const matcher = globsToMatcher(globs); | ||
|
||
expect(matcher('some-module.js')).toBe( | ||
micromatch(['some-module.js'], globs).length > 0, | ||
); | ||
|
||
expect(matcher('some-module.test.js')).toBe( | ||
micromatch(['some-module.test.js'], globs).length > 0, | ||
); | ||
}); | ||
|
||
it('works like micromatch with a mix of overlapping positive and negative globs', () => { | ||
const globs = ['**/*.js', '!**/*.test.js', '**/*.test.js']; | ||
const matcher = globsToMatcher(globs); | ||
|
||
expect(matcher('some-module.js')).toBe( | ||
micromatch(['some-module.js'], globs).length > 0, | ||
); | ||
|
||
expect(matcher('some-module.test.js')).toBe( | ||
micromatch(['some-module.test.js'], globs).length > 0, | ||
); | ||
|
||
const globs2 = ['**/*.js', '!**/*.test.js', '**/*.test.js', '!**/*.test.js']; | ||
const matcher2 = globsToMatcher(globs2); | ||
|
||
expect(matcher2('some-module.js')).toBe( | ||
micromatch(['some-module.js'], globs2).length > 0, | ||
); | ||
|
||
expect(matcher2('some-module.test.js')).toBe( | ||
micromatch(['some-module.test.js'], globs2).length > 0, | ||
); | ||
}); | ||
|
||
it('works like micromatch with only negative globs', () => { | ||
const globs = ['!**/*.test.js', '!**/*.test.jsx']; | ||
const matcher = globsToMatcher(globs); | ||
|
||
expect(matcher('some-module.js')).toBe( | ||
micromatch(['some-module.js'], globs).length > 0, | ||
); | ||
|
||
expect(matcher('some-module.test.js')).toBe( | ||
micromatch(['some-module.test.js'], globs).length > 0, | ||
); | ||
}); | ||
|
||
it('works like micromatch with empty globs', () => { | ||
const globs = []; | ||
const matcher = globsToMatcher(globs); | ||
|
||
expect(matcher('some-module.js')).toBe( | ||
micromatch(['some-module.js'], globs).length > 0, | ||
); | ||
|
||
expect(matcher('some-module.test.js')).toBe( | ||
micromatch(['some-module.test.js'], globs).length > 0, | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import micromatch = require('micromatch'); | ||
import type {Config} from '@jest/types'; | ||
import replacePathSepForGlob from './replacePathSepForGlob'; | ||
|
||
const globsToMatchersMap = new Map< | ||
string, | ||
{ | ||
isMatch: (str: string) => boolean; | ||
negated: boolean; | ||
} | ||
>(); | ||
|
||
const micromatchOptions = {dot: true}; | ||
|
||
/** | ||
* Converts a list of globs into a function that matches a path against the | ||
* globs. | ||
* | ||
* 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()`. | ||
* | ||
* @example | ||
* const isMatch = globsToMatcher(['*.js', '!*.test.js']); | ||
* isMatch('pizza.js'); // true | ||
* isMatch('pizza.test.js'); // false | ||
*/ | ||
export default function globsToMatcher( | ||
globs: Array<Config.Glob>, | ||
): (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 matchers = globs.map(glob => { | ||
if (!globsToMatchersMap.has(glob)) { | ||
// 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, micromatchOptions), | ||
negated, | ||
}; | ||
|
||
globsToMatchersMap.set(glob, matcher); | ||
} | ||
|
||
return globsToMatchersMap.get(glob)!; | ||
}); | ||
|
||
return (path: Config.Path): boolean => { | ||
const replacedPath = replacePathSepForGlob(path); | ||
let kept = undefined; | ||
let negatives = 0; | ||
|
||
for (let i = 0; i < matchers.length; i++) { | ||
const {isMatch, negated} = matchers[i]; | ||
|
||
if (negated) { | ||
negatives++; | ||
} | ||
|
||
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; | ||
} 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; | ||
} | ||
} | ||
|
||
// 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; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters