globToRegExp.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. function globToRegExp(glob) {
  6. // * [^\\\/]*
  7. // /**/ /.+/
  8. // ^* \./.+ (concord special)
  9. // ? [^\\\/]
  10. // [!...] [^...]
  11. // [^...] [^...]
  12. // / [\\\/]
  13. // {...,...} (...|...)
  14. // ?(...|...) (...|...)?
  15. // +(...|...) (...|...)+
  16. // *(...|...) (...|...)*
  17. // @(...|...) (...|...)
  18. if(/^\(.+\)$/.test(glob)) {
  19. // allow to pass an RegExp in brackets
  20. return new RegExp(glob.substr(1, glob.length - 2));
  21. }
  22. var tokens = tokenize(glob);
  23. var process = createRoot();
  24. var regExpStr = tokens.map(process).join("");
  25. return new RegExp("^" + regExpStr + "$");
  26. }
  27. var SIMPLE_TOKENS = {
  28. "@(": "one",
  29. "?(": "zero-one",
  30. "+(": "one-many",
  31. "*(": "zero-many",
  32. "|": "segment-sep",
  33. "/**/": "any-path-segments",
  34. "**": "any-path",
  35. "*": "any-path-segment",
  36. "?": "any-char",
  37. "{": "or",
  38. "/": "path-sep",
  39. ",": "comma",
  40. ")": "closing-segment",
  41. "}": "closing-or"
  42. };
  43. function tokenize(glob) {
  44. return glob.split(/([@?+*]\(|\/\*\*\/|\*\*|[?*]|\[[\!\^]?(?:[^\]\\]|\\.)+\]|\{|,|\/|[|)}])/g).map(function(item) {
  45. if(!item)
  46. return null;
  47. var t = SIMPLE_TOKENS[item];
  48. if(t) {
  49. return {
  50. type: t
  51. };
  52. }
  53. if(item[0] === "[") {
  54. if(item[1] === "^" || item[1] === "!") {
  55. return {
  56. type: "inverted-char-set",
  57. value: item.substr(2, item.length - 3)
  58. };
  59. } else {
  60. return {
  61. type: "char-set",
  62. value: item.substr(1, item.length - 2)
  63. };
  64. }
  65. }
  66. return {
  67. type: "string",
  68. value: item
  69. };
  70. }).filter(Boolean).concat({
  71. type: "end"
  72. });
  73. }
  74. function createRoot() {
  75. var inOr = [];
  76. var process = createSeqment();
  77. var initial = true;
  78. return function(token) {
  79. switch(token.type) {
  80. case "or":
  81. inOr.push(initial);
  82. return "(";
  83. case "comma":
  84. if(inOr.length) {
  85. initial = inOr[inOr.length - 1];
  86. return "|";
  87. } else {
  88. return process({
  89. type: "string",
  90. value: ","
  91. }, initial);
  92. }
  93. case "closing-or":
  94. if(inOr.length === 0)
  95. throw new Error("Unmatched '}'");
  96. inOr.pop();
  97. return ")";
  98. case "end":
  99. if(inOr.length)
  100. throw new Error("Unmatched '{'");
  101. return process(token, initial);
  102. default:
  103. var result = process(token, initial);
  104. initial = false;
  105. return result;
  106. }
  107. };
  108. }
  109. function createSeqment() {
  110. var inSeqment = [];
  111. var process = createSimple();
  112. return function(token, initial) {
  113. switch(token.type) {
  114. case "one":
  115. case "one-many":
  116. case "zero-many":
  117. case "zero-one":
  118. inSeqment.push(token.type);
  119. return "(";
  120. case "segment-sep":
  121. if(inSeqment.length) {
  122. return "|";
  123. } else {
  124. return process({
  125. type: "string",
  126. value: "|"
  127. }, initial);
  128. }
  129. case "closing-segment":
  130. var segment = inSeqment.pop();
  131. switch(segment) {
  132. case "one":
  133. return ")";
  134. case "one-many":
  135. return ")+";
  136. case "zero-many":
  137. return ")*";
  138. case "zero-one":
  139. return ")?";
  140. }
  141. throw new Error("Unexcepted segment " + segment);
  142. case "end":
  143. if(inSeqment.length > 0) {
  144. throw new Error("Unmatched segment, missing ')'");
  145. }
  146. return process(token, initial);
  147. default:
  148. return process(token, initial);
  149. }
  150. };
  151. }
  152. function createSimple() {
  153. return function(token, initial) {
  154. switch(token.type) {
  155. case "path-sep":
  156. return "[\\\\/]+";
  157. case "any-path-segments":
  158. return "[\\\\/]+(?:(.+)[\\\\/]+)?";
  159. case "any-path":
  160. return "(.*)";
  161. case "any-path-segment":
  162. if(initial) {
  163. return "\\.[\\\\/]+(?:.*[\\\\/]+)?([^\\\\/]+)";
  164. } else {
  165. return "([^\\\\/]*)";
  166. }
  167. case "any-char":
  168. return "[^\\\\/]";
  169. case "inverted-char-set":
  170. return "[^" + token.value + "]";
  171. case "char-set":
  172. return "[" + token.value + "]";
  173. case "string":
  174. return token.value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
  175. case "end":
  176. return "";
  177. default:
  178. throw new Error("Unsupported token '" + token.type + "'");
  179. }
  180. };
  181. }
  182. exports.globToRegExp = globToRegExp;