globToRegExp.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Haijie Xie @hai-x
  4. */
  5. "use strict";
  6. // Based on https://github.com/fitzgen/glob-to-regexp (MIT)
  7. // Specialized for watchpack: `extended` and `globstar` always enabled.
  8. // Returns the regexp source without `^`/`$` anchors.
  9. const CC_EXCLAMATION = 33; // "!"
  10. const CC_DOLLAR = 36; // "$"
  11. const CC_LEFT_PARENTHESIS = 40; // "("
  12. const CC_RIGHT_PARENTHESIS = 41; // ")"
  13. const CC_ASTERISK = 42; // "*"
  14. const CC_PLUS = 43; // "+"
  15. const CC_COMMA = 44; // ","
  16. const CC_DOT = 46; // "."
  17. const CC_SLASH = 47; // "/"
  18. const CC_EQUAL = 61; // "="
  19. const CC_QUESTION_MARK = 63; // "?"
  20. const CC_LEFT_BRACKET = 91; // "["
  21. const CC_RIGHT_BRACKET = 93; // "]"
  22. const CC_CARET = 94; // "^"
  23. const CC_LEFT_BRACE = 123; // "{"
  24. const CC_PIPE = 124; // "|"
  25. const CC_RIGHT_BRACE = 125; // "}"
  26. /**
  27. * @param {string} glob glob pattern
  28. * @returns {string} regexp source without anchors
  29. */
  30. module.exports = (glob) => {
  31. if (typeof glob !== "string") {
  32. throw new TypeError("Expected a string");
  33. }
  34. const len = glob.length;
  35. let reStr = "";
  36. let inGroup = false;
  37. // Start of the current run of literal characters, copied with one slice
  38. let literalStart = 0;
  39. for (let i = 0; i < len; i++) {
  40. const cc = glob.charCodeAt(i);
  41. const tokenStart = i;
  42. let mapped;
  43. switch (cc) {
  44. case CC_SLASH:
  45. mapped = "\\/";
  46. break;
  47. case CC_DOLLAR:
  48. mapped = "\\$";
  49. break;
  50. case CC_CARET:
  51. mapped = "\\^";
  52. break;
  53. case CC_PLUS:
  54. mapped = "\\+";
  55. break;
  56. case CC_DOT:
  57. mapped = "\\.";
  58. break;
  59. case CC_LEFT_PARENTHESIS:
  60. mapped = "\\(";
  61. break;
  62. case CC_RIGHT_PARENTHESIS:
  63. mapped = "\\)";
  64. break;
  65. case CC_EQUAL:
  66. mapped = "\\=";
  67. break;
  68. case CC_EXCLAMATION:
  69. mapped = "\\!";
  70. break;
  71. case CC_PIPE:
  72. mapped = "\\|";
  73. break;
  74. case CC_QUESTION_MARK:
  75. mapped = ".";
  76. break;
  77. case CC_LEFT_BRACKET:
  78. mapped = "[";
  79. break;
  80. case CC_RIGHT_BRACKET:
  81. mapped = "]";
  82. break;
  83. case CC_LEFT_BRACE:
  84. inGroup = true;
  85. mapped = "(";
  86. break;
  87. case CC_RIGHT_BRACE:
  88. inGroup = false;
  89. mapped = ")";
  90. break;
  91. case CC_COMMA:
  92. mapped = inGroup ? "|" : "\\,";
  93. break;
  94. case CC_ASTERISK: {
  95. const atStart = i === 0;
  96. const afterSlash = !atStart && glob.charCodeAt(i - 1) === CC_SLASH;
  97. let starCount = 1;
  98. while (i + 1 < len && glob.charCodeAt(i + 1) === CC_ASTERISK) {
  99. starCount++;
  100. i++;
  101. }
  102. const atEnd = i + 1 === len;
  103. const beforeSlash = !atEnd && glob.charCodeAt(i + 1) === CC_SLASH;
  104. if (
  105. starCount > 1 &&
  106. (atStart || afterSlash) &&
  107. (atEnd || beforeSlash)
  108. ) {
  109. // Globstar segment, matches zero or more path segments
  110. mapped = "((?:[^/]*(?:\\/|$))*)";
  111. i++; // Move over the "/"
  112. } else {
  113. // Not a globstar, matches one path segment
  114. mapped = "([^/]*)";
  115. }
  116. break;
  117. }
  118. default:
  119. // Literal character, extend the current run
  120. continue;
  121. }
  122. if (literalStart < tokenStart) {
  123. reStr += glob.slice(literalStart, tokenStart);
  124. }
  125. reStr += mapped;
  126. literalStart = i + 1;
  127. }
  128. if (literalStart < len) {
  129. reStr += literalStart === 0 ? glob : glob.slice(literalStart);
  130. }
  131. return reStr;
  132. };