globsToMatcher.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = globsToMatcher;
  6. function _picomatch() {
  7. const data = _interopRequireDefault(require('picomatch'));
  8. _picomatch = function () {
  9. return data;
  10. };
  11. return data;
  12. }
  13. var _replacePathSepForGlob = _interopRequireDefault(
  14. require('./replacePathSepForGlob')
  15. );
  16. function _interopRequireDefault(obj) {
  17. return obj && obj.__esModule ? obj : {default: obj};
  18. }
  19. /**
  20. * Copyright (c) Meta Platforms, Inc. and affiliates.
  21. *
  22. * This source code is licensed under the MIT license found in the
  23. * LICENSE file in the root directory of this source tree.
  24. */
  25. const globsToMatchersMap = new Map();
  26. const picomatchOptions = {
  27. dot: true
  28. };
  29. /**
  30. * Converts a list of globs into a function that matches a path against the
  31. * globs.
  32. *
  33. * Every time picomatch is called, it will parse the glob strings and turn
  34. * them into regexp instances. Instead of calling picomatch repeatedly with
  35. * the same globs, we can use this function which will build the picomatch
  36. * matchers ahead of time and then have an optimized path for determining
  37. * whether an individual path matches.
  38. *
  39. * This function is intended to match the behavior of `micromatch()`.
  40. *
  41. * @example
  42. * const isMatch = globsToMatcher(['*.js', '!*.test.js']);
  43. * isMatch('pizza.js'); // true
  44. * isMatch('pizza.test.js'); // false
  45. */
  46. function globsToMatcher(globs) {
  47. if (globs.length === 0) {
  48. // Since there were no globs given, we can simply have a fast path here and
  49. // return with a very simple function.
  50. return () => false;
  51. }
  52. const matchers = globs.map(glob => {
  53. if (!globsToMatchersMap.has(glob)) {
  54. const isMatch = (0, _picomatch().default)(glob, picomatchOptions, true);
  55. const matcher = {
  56. isMatch,
  57. // Matchers that are negated have different behavior than matchers that
  58. // are not negated, so we need to store this information ahead of time.
  59. negated: isMatch.state.negated || !!isMatch.state.negatedExtglob
  60. };
  61. globsToMatchersMap.set(glob, matcher);
  62. }
  63. return globsToMatchersMap.get(glob);
  64. });
  65. return path => {
  66. const replacedPath = (0, _replacePathSepForGlob.default)(path);
  67. let kept = undefined;
  68. let negatives = 0;
  69. for (let i = 0; i < matchers.length; i++) {
  70. const {isMatch, negated} = matchers[i];
  71. if (negated) {
  72. negatives++;
  73. }
  74. const matched = isMatch(replacedPath);
  75. if (!matched && negated) {
  76. // The path was not matched, and the matcher is a negated matcher, so we
  77. // want to omit the path. This means that the negative matcher is
  78. // filtering the path out.
  79. kept = false;
  80. } else if (matched && !negated) {
  81. // The path was matched, and the matcher is not a negated matcher, so we
  82. // want to keep the path.
  83. kept = true;
  84. }
  85. }
  86. // If all of the globs were negative globs, then we want to include the path
  87. // as long as it was not explicitly not kept. Otherwise only include
  88. // the path if it was kept. This allows sets of globs that are all negated
  89. // to allow some paths to be matched, while sets of globs that are mixed
  90. // negated and non-negated to cause the negated matchers to only omit paths
  91. // and not keep them.
  92. return negatives === matchers.length ? kept !== false : !!kept;
  93. };
  94. }