index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. module.exports = function (glob, opts) {
  2. if (typeof glob !== 'string') {
  3. throw new TypeError('Expected a string');
  4. }
  5. var str = String(glob);
  6. // The regexp we are building, as a string.
  7. var reStr = "";
  8. // Whether we are matching so called "extended" globs (like bash) and should
  9. // support single character matching, matching ranges of characters, group
  10. // matching, etc.
  11. var extended = opts ? !!opts.extended : false;
  12. // When globstar is _false_ (default), '/foo/*' is translated a regexp like
  13. // '^\/foo\/.*$' which will match any string beginning with '/foo/'
  14. // When globstar is _true_, '/foo/*' is translated to regexp like
  15. // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT
  16. // which does not have a '/' to the right of it.
  17. // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but
  18. // these will not '/foo/bar/baz', '/foo/bar/baz.txt'
  19. // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when
  20. // globstar is _false_
  21. var globstar = opts ? !!opts.globstar : false;
  22. // If we are doing extended matching, this boolean is true when we are inside
  23. // a group (eg {*.html,*.js}), and false otherwise.
  24. var inGroup = false;
  25. // RegExp flags (eg "i" ) to pass in to RegExp constructor.
  26. var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : "";
  27. var c;
  28. for (var i = 0, len = str.length; i < len; i++) {
  29. c = str[i];
  30. switch (c) {
  31. case "/":
  32. case "$":
  33. case "^":
  34. case "+":
  35. case ".":
  36. case "(":
  37. case ")":
  38. case "=":
  39. case "!":
  40. case "|":
  41. reStr += "\\" + c;
  42. break;
  43. case "?":
  44. if (extended) {
  45. reStr += ".";
  46. break;
  47. }
  48. case "[":
  49. case "]":
  50. if (extended) {
  51. reStr += c;
  52. break;
  53. }
  54. case "{":
  55. if (extended) {
  56. inGroup = true;
  57. reStr += "(";
  58. break;
  59. }
  60. case "}":
  61. if (extended) {
  62. inGroup = false;
  63. reStr += ")";
  64. break;
  65. }
  66. case ",":
  67. if (inGroup) {
  68. reStr += "|";
  69. break;
  70. }
  71. reStr += "\\" + c;
  72. break;
  73. case "*":
  74. // Move over all consecutive "*"'s.
  75. // Also store the previous and next characters
  76. var prevChar = str[i - 1];
  77. var starCount = 1;
  78. while(str[i + 1] === "*") {
  79. starCount++;
  80. i++;
  81. }
  82. var nextChar = str[i + 1];
  83. if (!globstar) {
  84. // globstar is disabled, so treat any number of "*" as one
  85. reStr += ".*";
  86. } else {
  87. // globstar is enabled, so determine if this is a globstar segment
  88. var isGlobstar = starCount > 1 // multiple "*"'s
  89. && (prevChar === "/" || prevChar === undefined) // from the start of the segment
  90. && (nextChar === "/" || nextChar === undefined) // to the end of the segment
  91. if (isGlobstar) {
  92. // it's a globstar, so match zero or more path segments
  93. reStr += "((?:[^/]*(?:\/|$))*)";
  94. i++; // move over the "/"
  95. } else {
  96. // it's not a globstar, so only match one path segment
  97. reStr += "([^/]*)";
  98. }
  99. }
  100. break;
  101. default:
  102. reStr += c;
  103. }
  104. }
  105. // When regexp 'g' flag is specified don't
  106. // constrain the regular expression with ^ & $
  107. if (!flags || !~flags.indexOf('g')) {
  108. reStr = "^" + reStr + "$";
  109. }
  110. return new RegExp(reStr, flags);
  111. };