glob.js 4.9 KB

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