pattern.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. "use strict";
  2. // this is just a very light wrapper around 2 arrays with an offset index
  3. Object.defineProperty(exports, "__esModule", { value: true });
  4. exports.Pattern = void 0;
  5. const minimatch_1 = require("minimatch");
  6. const isPatternList = (pl) => pl.length >= 1;
  7. const isGlobList = (gl) => gl.length >= 1;
  8. /**
  9. * An immutable-ish view on an array of glob parts and their parsed
  10. * results
  11. */
  12. class Pattern {
  13. #patternList;
  14. #globList;
  15. #index;
  16. length;
  17. #platform;
  18. #rest;
  19. #globString;
  20. #isDrive;
  21. #isUNC;
  22. #isAbsolute;
  23. #followGlobstar = true;
  24. constructor(patternList, globList, index, platform) {
  25. if (!isPatternList(patternList)) {
  26. throw new TypeError('empty pattern list');
  27. }
  28. if (!isGlobList(globList)) {
  29. throw new TypeError('empty glob list');
  30. }
  31. if (globList.length !== patternList.length) {
  32. throw new TypeError('mismatched pattern list and glob list lengths');
  33. }
  34. this.length = patternList.length;
  35. if (index < 0 || index >= this.length) {
  36. throw new TypeError('index out of range');
  37. }
  38. this.#patternList = patternList;
  39. this.#globList = globList;
  40. this.#index = index;
  41. this.#platform = platform;
  42. // normalize root entries of absolute patterns on initial creation.
  43. if (this.#index === 0) {
  44. // c: => ['c:/']
  45. // C:/ => ['C:/']
  46. // C:/x => ['C:/', 'x']
  47. // //host/share => ['//host/share/']
  48. // //host/share/ => ['//host/share/']
  49. // //host/share/x => ['//host/share/', 'x']
  50. // /etc => ['/', 'etc']
  51. // / => ['/']
  52. if (this.isUNC()) {
  53. // '' / '' / 'host' / 'share'
  54. const [p0, p1, p2, p3, ...prest] = this.#patternList;
  55. const [g0, g1, g2, g3, ...grest] = this.#globList;
  56. if (prest[0] === '') {
  57. // ends in /
  58. prest.shift();
  59. grest.shift();
  60. }
  61. const p = [p0, p1, p2, p3, ''].join('/');
  62. const g = [g0, g1, g2, g3, ''].join('/');
  63. this.#patternList = [p, ...prest];
  64. this.#globList = [g, ...grest];
  65. this.length = this.#patternList.length;
  66. }
  67. else if (this.isDrive() || this.isAbsolute()) {
  68. const [p1, ...prest] = this.#patternList;
  69. const [g1, ...grest] = this.#globList;
  70. if (prest[0] === '') {
  71. // ends in /
  72. prest.shift();
  73. grest.shift();
  74. }
  75. const p = p1 + '/';
  76. const g = g1 + '/';
  77. this.#patternList = [p, ...prest];
  78. this.#globList = [g, ...grest];
  79. this.length = this.#patternList.length;
  80. }
  81. }
  82. }
  83. /**
  84. * The first entry in the parsed list of patterns
  85. */
  86. pattern() {
  87. return this.#patternList[this.#index];
  88. }
  89. /**
  90. * true of if pattern() returns a string
  91. */
  92. isString() {
  93. return typeof this.#patternList[this.#index] === 'string';
  94. }
  95. /**
  96. * true of if pattern() returns GLOBSTAR
  97. */
  98. isGlobstar() {
  99. return this.#patternList[this.#index] === minimatch_1.GLOBSTAR;
  100. }
  101. /**
  102. * true if pattern() returns a regexp
  103. */
  104. isRegExp() {
  105. return this.#patternList[this.#index] instanceof RegExp;
  106. }
  107. /**
  108. * The /-joined set of glob parts that make up this pattern
  109. */
  110. globString() {
  111. return (this.#globString =
  112. this.#globString ||
  113. (this.#index === 0 ?
  114. this.isAbsolute() ?
  115. this.#globList[0] + this.#globList.slice(1).join('/')
  116. : this.#globList.join('/')
  117. : this.#globList.slice(this.#index).join('/')));
  118. }
  119. /**
  120. * true if there are more pattern parts after this one
  121. */
  122. hasMore() {
  123. return this.length > this.#index + 1;
  124. }
  125. /**
  126. * The rest of the pattern after this part, or null if this is the end
  127. */
  128. rest() {
  129. if (this.#rest !== undefined)
  130. return this.#rest;
  131. if (!this.hasMore())
  132. return (this.#rest = null);
  133. this.#rest = new Pattern(this.#patternList, this.#globList, this.#index + 1, this.#platform);
  134. this.#rest.#isAbsolute = this.#isAbsolute;
  135. this.#rest.#isUNC = this.#isUNC;
  136. this.#rest.#isDrive = this.#isDrive;
  137. return this.#rest;
  138. }
  139. /**
  140. * true if the pattern represents a //unc/path/ on windows
  141. */
  142. isUNC() {
  143. const pl = this.#patternList;
  144. return this.#isUNC !== undefined ?
  145. this.#isUNC
  146. : (this.#isUNC =
  147. this.#platform === 'win32' &&
  148. this.#index === 0 &&
  149. pl[0] === '' &&
  150. pl[1] === '' &&
  151. typeof pl[2] === 'string' &&
  152. !!pl[2] &&
  153. typeof pl[3] === 'string' &&
  154. !!pl[3]);
  155. }
  156. // pattern like C:/...
  157. // split = ['C:', ...]
  158. // XXX: would be nice to handle patterns like `c:*` to test the cwd
  159. // in c: for *, but I don't know of a way to even figure out what that
  160. // cwd is without actually chdir'ing into it?
  161. /**
  162. * True if the pattern starts with a drive letter on Windows
  163. */
  164. isDrive() {
  165. const pl = this.#patternList;
  166. return this.#isDrive !== undefined ?
  167. this.#isDrive
  168. : (this.#isDrive =
  169. this.#platform === 'win32' &&
  170. this.#index === 0 &&
  171. this.length > 1 &&
  172. typeof pl[0] === 'string' &&
  173. /^[a-z]:$/i.test(pl[0]));
  174. }
  175. // pattern = '/' or '/...' or '/x/...'
  176. // split = ['', ''] or ['', ...] or ['', 'x', ...]
  177. // Drive and UNC both considered absolute on windows
  178. /**
  179. * True if the pattern is rooted on an absolute path
  180. */
  181. isAbsolute() {
  182. const pl = this.#patternList;
  183. return this.#isAbsolute !== undefined ?
  184. this.#isAbsolute
  185. : (this.#isAbsolute =
  186. (pl[0] === '' && pl.length > 1) ||
  187. this.isDrive() ||
  188. this.isUNC());
  189. }
  190. /**
  191. * consume the root of the pattern, and return it
  192. */
  193. root() {
  194. const p = this.#patternList[0];
  195. return (typeof p === 'string' && this.isAbsolute() && this.#index === 0) ?
  196. p
  197. : '';
  198. }
  199. /**
  200. * Check to see if the current globstar pattern is allowed to follow
  201. * a symbolic link.
  202. */
  203. checkFollowGlobstar() {
  204. return !(this.#index === 0 ||
  205. !this.isGlobstar() ||
  206. !this.#followGlobstar);
  207. }
  208. /**
  209. * Mark that the current globstar pattern is following a symbolic link
  210. */
  211. markFollowGlobstar() {
  212. if (this.#index === 0 || !this.isGlobstar() || !this.#followGlobstar)
  213. return false;
  214. this.#followGlobstar = false;
  215. return true;
  216. }
  217. }
  218. exports.Pattern = Pattern;
  219. //# sourceMappingURL=pattern.js.map