index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. case "|":
  42. reStr += "\\" + c;
  43. break;
  44. case "?":
  45. if (extended) {
  46. reStr += ".";
  47. break;
  48. }
  49. case "[":
  50. case "]":
  51. if (extended) {
  52. reStr += c;
  53. break;
  54. }
  55. case "{":
  56. if (extended) {
  57. inGroup = true;
  58. reStr += "(";
  59. break;
  60. }
  61. case "}":
  62. if (extended) {
  63. inGroup = false;
  64. reStr += ")";
  65. break;
  66. }
  67. case ",":
  68. if (inGroup) {
  69. reStr += "|";
  70. break;
  71. }
  72. reStr += "\\" + c;
  73. break;
  74. case "*":
  75. // Move over all consecutive "*"'s.
  76. // Also store the previous and next characters
  77. var prevChar = str[i - 1];
  78. var starCount = 1;
  79. while(str[i + 1] === "*") {
  80. starCount++;
  81. i++;
  82. }
  83. var nextChar = str[i + 1];
  84. if (!globstar) {
  85. // globstar is disabled, so treat any number of "*" as one
  86. reStr += ".*";
  87. } else {
  88. // globstar is enabled, so determine if this is a globstar segment
  89. var isGlobstar = starCount > 1 // multiple "*"'s
  90. && (prevChar === "/" || prevChar === undefined) // from the start of the segment
  91. && (nextChar === "/" || nextChar === undefined) // to the end of the segment
  92. if (isGlobstar) {
  93. // it's a globstar, so match zero or more path segments
  94. reStr += "(?:[^/]*(?:\/|$))*";
  95. i++; // move over the "/"
  96. } else {
  97. // it's not a globstar, so only match one path segment
  98. reStr += "[^/]*";
  99. }
  100. }
  101. break;
  102. default:
  103. reStr += c;
  104. }
  105. }
  106. // When regexp 'g' flag is specified don't
  107. // constrain the regular expression with ^ & $
  108. if (!flags || !~flags.indexOf('g')) {
  109. reStr = "^" + reStr + "$";
  110. }
  111. return new RegExp(reStr, flags);
  112. };