index.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.toMatcher = exports.toRegex = void 0;
  4. const escapeRe = (ch) => (/[.^$+{}()|\\]/.test(ch) ? `\\${ch}` : ch);
  5. /**
  6. * Convert a glob pattern to a regular expression
  7. *
  8. * Supports:
  9. * - `/` to separate path segments
  10. * - `*` to match zero or more characters in a path segment
  11. * - `?` to match one character in a path segment
  12. * - `**` to match any number of path segments, including none
  13. * - `{}` to group conditions (e.g. `{html,txt}`)
  14. * - `[abc]`, `[a-z]`, `[!a-z]`, `[!abc]` character classes
  15. */
  16. const toRegex = (pattern) => {
  17. let regexStr = '';
  18. let i = 0;
  19. // Helper to parse a brace group like {a,b,c}. No nesting support.
  20. const parseBraceGroup = () => {
  21. // Assume current char is '{'
  22. i++; // skip '{'
  23. const parts = [];
  24. let cur = '';
  25. let closed = false;
  26. while (i < pattern.length) {
  27. const ch = pattern[i];
  28. if (ch === '}') {
  29. parts.push(cur);
  30. i++; // consume '}'
  31. closed = true;
  32. break;
  33. }
  34. if (ch === ',') {
  35. parts.push(cur);
  36. cur = '';
  37. i++;
  38. continue;
  39. }
  40. cur += ch;
  41. i++;
  42. }
  43. if (!closed) {
  44. // treat as literal '{...'
  45. return '\\{' + escapeRe(cur);
  46. }
  47. // Convert each part recursively to support globs inside braces
  48. const alt = parts.map((p) => (0, exports.toRegex)(p).source.replace(/^\^/, '').replace(/\$$/, '')).join('|');
  49. return `(?:${alt})`;
  50. };
  51. while (i < pattern.length) {
  52. const char = pattern[i];
  53. switch (char) {
  54. case '*': {
  55. // Check for double star **
  56. if (pattern[i + 1] === '*') {
  57. // Collapse consecutive * beyond two (e.g., *** -> **)
  58. let j = i + 2;
  59. while (pattern[j] === '*')
  60. j++;
  61. // If followed by a slash, make it optional to allow zero segments
  62. if (pattern[j] === '/') {
  63. regexStr += '(?:.*/)?';
  64. i = j + 1; // consume **/
  65. }
  66. else {
  67. regexStr += '.*';
  68. i = j; // consume **
  69. }
  70. }
  71. else {
  72. regexStr += '[^/]*';
  73. i++;
  74. }
  75. break;
  76. }
  77. case '?':
  78. regexStr += '[^/]';
  79. i++;
  80. break;
  81. case '[': {
  82. // Copy character class as-is with support for leading '!'
  83. let cls = '[';
  84. i++;
  85. if (i < pattern.length && pattern[i] === '!') {
  86. cls += '^';
  87. i++;
  88. }
  89. // if first after [ or [^ is ']' include it literally
  90. if (i < pattern.length && pattern[i] === ']') {
  91. cls += ']';
  92. i++;
  93. }
  94. while (i < pattern.length && pattern[i] !== ']') {
  95. const ch = pattern[i];
  96. // Escape backslash inside class
  97. cls += ch === '\\' ? '\\\\' : ch;
  98. i++;
  99. }
  100. if (i < pattern.length && pattern[i] === ']') {
  101. cls += ']';
  102. i++;
  103. }
  104. else {
  105. // Unclosed class -> treat '[' literally
  106. regexStr += '\\[';
  107. continue;
  108. }
  109. regexStr += cls;
  110. break;
  111. }
  112. case '{': {
  113. regexStr += parseBraceGroup();
  114. break;
  115. }
  116. case '/':
  117. regexStr += '/';
  118. i++;
  119. break;
  120. case '.':
  121. case '^':
  122. case '$':
  123. case '+':
  124. case '(':
  125. case ')':
  126. case '|':
  127. case '\\':
  128. regexStr += `\\${char}`;
  129. i++;
  130. break;
  131. default:
  132. regexStr += char;
  133. i++;
  134. break;
  135. }
  136. }
  137. return new RegExp('^' + regexStr + '$');
  138. };
  139. exports.toRegex = toRegex;
  140. const isRegExp = /^\/(.{1,4096})\/([gimsuy]{0,6})$/;
  141. const toMatcher = (pattern) => {
  142. const regexes = [];
  143. const patterns = Array.isArray(pattern) ? pattern : [pattern];
  144. for (const pat of patterns) {
  145. if (typeof pat === 'string') {
  146. const match = isRegExp.exec(pat);
  147. if (match) {
  148. const [, expr, flags] = match;
  149. regexes.push(new RegExp(expr, flags));
  150. }
  151. else {
  152. regexes.push((0, exports.toRegex)(pat));
  153. }
  154. }
  155. else {
  156. regexes.push(pat);
  157. }
  158. }
  159. const length = regexes.length;
  160. return (path) => {
  161. for (let i = 0; i < length; i++)
  162. if (regexes[i].test(path))
  163. return true;
  164. return false;
  165. };
  166. };
  167. exports.toMatcher = toMatcher;