選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

globToRegExp.js 4.2KB

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