index.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. import expand from 'brace-expansion';
  2. import { assertValidPattern } from './assert-valid-pattern.js';
  3. import { AST } from './ast.js';
  4. import { escape } from './escape.js';
  5. import { unescape } from './unescape.js';
  6. export const minimatch = (p, pattern, options = {}) => {
  7. assertValidPattern(pattern);
  8. // shortcut: comments match nothing.
  9. if (!options.nocomment && pattern.charAt(0) === '#') {
  10. return false;
  11. }
  12. return new Minimatch(pattern, options).match(p);
  13. };
  14. // Optimized checking for the most common glob patterns.
  15. const starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
  16. const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
  17. const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
  18. const starDotExtTestNocase = (ext) => {
  19. ext = ext.toLowerCase();
  20. return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
  21. };
  22. const starDotExtTestNocaseDot = (ext) => {
  23. ext = ext.toLowerCase();
  24. return (f) => f.toLowerCase().endsWith(ext);
  25. };
  26. const starDotStarRE = /^\*+\.\*+$/;
  27. const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
  28. const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
  29. const dotStarRE = /^\.\*+$/;
  30. const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
  31. const starRE = /^\*+$/;
  32. const starTest = (f) => f.length !== 0 && !f.startsWith('.');
  33. const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
  34. const qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
  35. const qmarksTestNocase = ([$0, ext = '']) => {
  36. const noext = qmarksTestNoExt([$0]);
  37. if (!ext)
  38. return noext;
  39. ext = ext.toLowerCase();
  40. return (f) => noext(f) && f.toLowerCase().endsWith(ext);
  41. };
  42. const qmarksTestNocaseDot = ([$0, ext = '']) => {
  43. const noext = qmarksTestNoExtDot([$0]);
  44. if (!ext)
  45. return noext;
  46. ext = ext.toLowerCase();
  47. return (f) => noext(f) && f.toLowerCase().endsWith(ext);
  48. };
  49. const qmarksTestDot = ([$0, ext = '']) => {
  50. const noext = qmarksTestNoExtDot([$0]);
  51. return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
  52. };
  53. const qmarksTest = ([$0, ext = '']) => {
  54. const noext = qmarksTestNoExt([$0]);
  55. return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
  56. };
  57. const qmarksTestNoExt = ([$0]) => {
  58. const len = $0.length;
  59. return (f) => f.length === len && !f.startsWith('.');
  60. };
  61. const qmarksTestNoExtDot = ([$0]) => {
  62. const len = $0.length;
  63. return (f) => f.length === len && f !== '.' && f !== '..';
  64. };
  65. /* c8 ignore start */
  66. const defaultPlatform = (typeof process === 'object' && process
  67. ? (typeof process.env === 'object' &&
  68. process.env &&
  69. process.env.__MINIMATCH_TESTING_PLATFORM__) ||
  70. process.platform
  71. : 'posix');
  72. const path = {
  73. win32: { sep: '\\' },
  74. posix: { sep: '/' },
  75. };
  76. /* c8 ignore stop */
  77. export const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep;
  78. minimatch.sep = sep;
  79. export const GLOBSTAR = Symbol('globstar **');
  80. minimatch.GLOBSTAR = GLOBSTAR;
  81. // any single thing other than /
  82. // don't need to escape / when using new RegExp()
  83. const qmark = '[^/]';
  84. // * => any number of characters
  85. const star = qmark + '*?';
  86. // ** when dots are allowed. Anything goes, except .. and .
  87. // not (^ or / followed by one or two dots followed by $ or /),
  88. // followed by anything, any number of times.
  89. const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?';
  90. // not a ^ or / followed by a dot,
  91. // followed by anything, any number of times.
  92. const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?';
  93. export const filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
  94. minimatch.filter = filter;
  95. const ext = (a, b = {}) => Object.assign({}, a, b);
  96. export const defaults = (def) => {
  97. if (!def || typeof def !== 'object' || !Object.keys(def).length) {
  98. return minimatch;
  99. }
  100. const orig = minimatch;
  101. const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options));
  102. return Object.assign(m, {
  103. Minimatch: class Minimatch extends orig.Minimatch {
  104. constructor(pattern, options = {}) {
  105. super(pattern, ext(def, options));
  106. }
  107. static defaults(options) {
  108. return orig.defaults(ext(def, options)).Minimatch;
  109. }
  110. },
  111. AST: class AST extends orig.AST {
  112. /* c8 ignore start */
  113. constructor(type, parent, options = {}) {
  114. super(type, parent, ext(def, options));
  115. }
  116. /* c8 ignore stop */
  117. static fromGlob(pattern, options = {}) {
  118. return orig.AST.fromGlob(pattern, ext(def, options));
  119. }
  120. },
  121. unescape: (s, options = {}) => orig.unescape(s, ext(def, options)),
  122. escape: (s, options = {}) => orig.escape(s, ext(def, options)),
  123. filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)),
  124. defaults: (options) => orig.defaults(ext(def, options)),
  125. makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)),
  126. braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)),
  127. match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)),
  128. sep: orig.sep,
  129. GLOBSTAR: GLOBSTAR,
  130. });
  131. };
  132. minimatch.defaults = defaults;
  133. // Brace expansion:
  134. // a{b,c}d -> abd acd
  135. // a{b,}c -> abc ac
  136. // a{0..3}d -> a0d a1d a2d a3d
  137. // a{b,c{d,e}f}g -> abg acdfg acefg
  138. // a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
  139. //
  140. // Invalid sets are not expanded.
  141. // a{2..}b -> a{2..}b
  142. // a{b}c -> a{b}c
  143. export const braceExpand = (pattern, options = {}) => {
  144. assertValidPattern(pattern);
  145. // Thanks to Yeting Li <https://github.com/yetingli> for
  146. // improving this regexp to avoid a ReDOS vulnerability.
  147. if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
  148. // shortcut. no need to expand.
  149. return [pattern];
  150. }
  151. return expand(pattern);
  152. };
  153. minimatch.braceExpand = braceExpand;
  154. // parse a component of the expanded set.
  155. // At this point, no pattern may contain "/" in it
  156. // so we're going to return a 2d array, where each entry is the full
  157. // pattern, split on '/', and then turned into a regular expression.
  158. // A regexp is made at the end which joins each array with an
  159. // escaped /, and another full one which joins each regexp with |.
  160. //
  161. // Following the lead of Bash 4.1, note that "**" only has special meaning
  162. // when it is the *only* thing in a path portion. Otherwise, any series
  163. // of * is equivalent to a single *. Globstar behavior is enabled by
  164. // default, and can be disabled by setting options.noglobstar.
  165. export const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
  166. minimatch.makeRe = makeRe;
  167. export const match = (list, pattern, options = {}) => {
  168. const mm = new Minimatch(pattern, options);
  169. list = list.filter(f => mm.match(f));
  170. if (mm.options.nonull && !list.length) {
  171. list.push(pattern);
  172. }
  173. return list;
  174. };
  175. minimatch.match = match;
  176. // replace stuff like \* with *
  177. const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
  178. const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
  179. export class Minimatch {
  180. options;
  181. set;
  182. pattern;
  183. windowsPathsNoEscape;
  184. nonegate;
  185. negate;
  186. comment;
  187. empty;
  188. preserveMultipleSlashes;
  189. partial;
  190. globSet;
  191. globParts;
  192. nocase;
  193. isWindows;
  194. platform;
  195. windowsNoMagicRoot;
  196. maxGlobstarRecursion;
  197. regexp;
  198. constructor(pattern, options = {}) {
  199. assertValidPattern(pattern);
  200. options = options || {};
  201. this.options = options;
  202. this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
  203. this.pattern = pattern;
  204. this.platform = options.platform || defaultPlatform;
  205. this.isWindows = this.platform === 'win32';
  206. this.windowsPathsNoEscape =
  207. !!options.windowsPathsNoEscape || options.allowWindowsEscape === false;
  208. if (this.windowsPathsNoEscape) {
  209. this.pattern = this.pattern.replace(/\\/g, '/');
  210. }
  211. this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
  212. this.regexp = null;
  213. this.negate = false;
  214. this.nonegate = !!options.nonegate;
  215. this.comment = false;
  216. this.empty = false;
  217. this.partial = !!options.partial;
  218. this.nocase = !!this.options.nocase;
  219. this.windowsNoMagicRoot =
  220. options.windowsNoMagicRoot !== undefined
  221. ? options.windowsNoMagicRoot
  222. : !!(this.isWindows && this.nocase);
  223. this.globSet = [];
  224. this.globParts = [];
  225. this.set = [];
  226. // make the set of regexps etc.
  227. this.make();
  228. }
  229. hasMagic() {
  230. if (this.options.magicalBraces && this.set.length > 1) {
  231. return true;
  232. }
  233. for (const pattern of this.set) {
  234. for (const part of pattern) {
  235. if (typeof part !== 'string')
  236. return true;
  237. }
  238. }
  239. return false;
  240. }
  241. debug(..._) { }
  242. make() {
  243. const pattern = this.pattern;
  244. const options = this.options;
  245. // empty patterns and comments match nothing.
  246. if (!options.nocomment && pattern.charAt(0) === '#') {
  247. this.comment = true;
  248. return;
  249. }
  250. if (!pattern) {
  251. this.empty = true;
  252. return;
  253. }
  254. // step 1: figure out negation, etc.
  255. this.parseNegate();
  256. // step 2: expand braces
  257. this.globSet = [...new Set(this.braceExpand())];
  258. if (options.debug) {
  259. this.debug = (...args) => console.error(...args);
  260. }
  261. this.debug(this.pattern, this.globSet);
  262. // step 3: now we have a set, so turn each one into a series of
  263. // path-portion matching patterns.
  264. // These will be regexps, except in the case of "**", which is
  265. // set to the GLOBSTAR object for globstar behavior,
  266. // and will not contain any / characters
  267. //
  268. // First, we preprocess to make the glob pattern sets a bit simpler
  269. // and deduped. There are some perf-killing patterns that can cause
  270. // problems with a glob walk, but we can simplify them down a bit.
  271. const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
  272. this.globParts = this.preprocess(rawGlobParts);
  273. this.debug(this.pattern, this.globParts);
  274. // glob --> regexps
  275. let set = this.globParts.map((s, _, __) => {
  276. if (this.isWindows && this.windowsNoMagicRoot) {
  277. // check if it's a drive or unc path.
  278. const isUNC = s[0] === '' &&
  279. s[1] === '' &&
  280. (s[2] === '?' || !globMagic.test(s[2])) &&
  281. !globMagic.test(s[3]);
  282. const isDrive = /^[a-z]:/i.test(s[0]);
  283. if (isUNC) {
  284. return [...s.slice(0, 4), ...s.slice(4).map(ss => this.parse(ss))];
  285. }
  286. else if (isDrive) {
  287. return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
  288. }
  289. }
  290. return s.map(ss => this.parse(ss));
  291. });
  292. this.debug(this.pattern, set);
  293. // filter out everything that didn't compile properly.
  294. this.set = set.filter(s => s.indexOf(false) === -1);
  295. // do not treat the ? in UNC paths as magic
  296. if (this.isWindows) {
  297. for (let i = 0; i < this.set.length; i++) {
  298. const p = this.set[i];
  299. if (p[0] === '' &&
  300. p[1] === '' &&
  301. this.globParts[i][2] === '?' &&
  302. typeof p[3] === 'string' &&
  303. /^[a-z]:$/i.test(p[3])) {
  304. p[2] = '?';
  305. }
  306. }
  307. }
  308. this.debug(this.pattern, this.set);
  309. }
  310. // various transforms to equivalent pattern sets that are
  311. // faster to process in a filesystem walk. The goal is to
  312. // eliminate what we can, and push all ** patterns as far
  313. // to the right as possible, even if it increases the number
  314. // of patterns that we have to process.
  315. preprocess(globParts) {
  316. // if we're not in globstar mode, then turn all ** into *
  317. if (this.options.noglobstar) {
  318. for (let i = 0; i < globParts.length; i++) {
  319. for (let j = 0; j < globParts[i].length; j++) {
  320. if (globParts[i][j] === '**') {
  321. globParts[i][j] = '*';
  322. }
  323. }
  324. }
  325. }
  326. const { optimizationLevel = 1 } = this.options;
  327. if (optimizationLevel >= 2) {
  328. // aggressive optimization for the purpose of fs walking
  329. globParts = this.firstPhasePreProcess(globParts);
  330. globParts = this.secondPhasePreProcess(globParts);
  331. }
  332. else if (optimizationLevel >= 1) {
  333. // just basic optimizations to remove some .. parts
  334. globParts = this.levelOneOptimize(globParts);
  335. }
  336. else {
  337. // just collapse multiple ** portions into one
  338. globParts = this.adjascentGlobstarOptimize(globParts);
  339. }
  340. return globParts;
  341. }
  342. // just get rid of adjascent ** portions
  343. adjascentGlobstarOptimize(globParts) {
  344. return globParts.map(parts => {
  345. let gs = -1;
  346. while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
  347. let i = gs;
  348. while (parts[i + 1] === '**') {
  349. i++;
  350. }
  351. if (i !== gs) {
  352. parts.splice(gs, i - gs);
  353. }
  354. }
  355. return parts;
  356. });
  357. }
  358. // get rid of adjascent ** and resolve .. portions
  359. levelOneOptimize(globParts) {
  360. return globParts.map(parts => {
  361. parts = parts.reduce((set, part) => {
  362. const prev = set[set.length - 1];
  363. if (part === '**' && prev === '**') {
  364. return set;
  365. }
  366. if (part === '..') {
  367. if (prev && prev !== '..' && prev !== '.' && prev !== '**') {
  368. set.pop();
  369. return set;
  370. }
  371. }
  372. set.push(part);
  373. return set;
  374. }, []);
  375. return parts.length === 0 ? [''] : parts;
  376. });
  377. }
  378. levelTwoFileOptimize(parts) {
  379. if (!Array.isArray(parts)) {
  380. parts = this.slashSplit(parts);
  381. }
  382. let didSomething = false;
  383. do {
  384. didSomething = false;
  385. // <pre>/<e>/<rest> -> <pre>/<rest>
  386. if (!this.preserveMultipleSlashes) {
  387. for (let i = 1; i < parts.length - 1; i++) {
  388. const p = parts[i];
  389. // don't squeeze out UNC patterns
  390. if (i === 1 && p === '' && parts[0] === '')
  391. continue;
  392. if (p === '.' || p === '') {
  393. didSomething = true;
  394. parts.splice(i, 1);
  395. i--;
  396. }
  397. }
  398. if (parts[0] === '.' &&
  399. parts.length === 2 &&
  400. (parts[1] === '.' || parts[1] === '')) {
  401. didSomething = true;
  402. parts.pop();
  403. }
  404. }
  405. // <pre>/<p>/../<rest> -> <pre>/<rest>
  406. let dd = 0;
  407. while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
  408. const p = parts[dd - 1];
  409. if (p && p !== '.' && p !== '..' && p !== '**') {
  410. didSomething = true;
  411. parts.splice(dd - 1, 2);
  412. dd -= 2;
  413. }
  414. }
  415. } while (didSomething);
  416. return parts.length === 0 ? [''] : parts;
  417. }
  418. // First phase: single-pattern processing
  419. // <pre> is 1 or more portions
  420. // <rest> is 1 or more portions
  421. // <p> is any portion other than ., .., '', or **
  422. // <e> is . or ''
  423. //
  424. // **/.. is *brutal* for filesystem walking performance, because
  425. // it effectively resets the recursive walk each time it occurs,
  426. // and ** cannot be reduced out by a .. pattern part like a regexp
  427. // or most strings (other than .., ., and '') can be.
  428. //
  429. // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
  430. // <pre>/<e>/<rest> -> <pre>/<rest>
  431. // <pre>/<p>/../<rest> -> <pre>/<rest>
  432. // **/**/<rest> -> **/<rest>
  433. //
  434. // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
  435. // this WOULD be allowed if ** did follow symlinks, or * didn't
  436. firstPhasePreProcess(globParts) {
  437. let didSomething = false;
  438. do {
  439. didSomething = false;
  440. // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
  441. for (let parts of globParts) {
  442. let gs = -1;
  443. while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
  444. let gss = gs;
  445. while (parts[gss + 1] === '**') {
  446. // <pre>/**/**/<rest> -> <pre>/**/<rest>
  447. gss++;
  448. }
  449. // eg, if gs is 2 and gss is 4, that means we have 3 **
  450. // parts, and can remove 2 of them.
  451. if (gss > gs) {
  452. parts.splice(gs + 1, gss - gs);
  453. }
  454. let next = parts[gs + 1];
  455. const p = parts[gs + 2];
  456. const p2 = parts[gs + 3];
  457. if (next !== '..')
  458. continue;
  459. if (!p ||
  460. p === '.' ||
  461. p === '..' ||
  462. !p2 ||
  463. p2 === '.' ||
  464. p2 === '..') {
  465. continue;
  466. }
  467. didSomething = true;
  468. // edit parts in place, and push the new one
  469. parts.splice(gs, 1);
  470. const other = parts.slice(0);
  471. other[gs] = '**';
  472. globParts.push(other);
  473. gs--;
  474. }
  475. // <pre>/<e>/<rest> -> <pre>/<rest>
  476. if (!this.preserveMultipleSlashes) {
  477. for (let i = 1; i < parts.length - 1; i++) {
  478. const p = parts[i];
  479. // don't squeeze out UNC patterns
  480. if (i === 1 && p === '' && parts[0] === '')
  481. continue;
  482. if (p === '.' || p === '') {
  483. didSomething = true;
  484. parts.splice(i, 1);
  485. i--;
  486. }
  487. }
  488. if (parts[0] === '.' &&
  489. parts.length === 2 &&
  490. (parts[1] === '.' || parts[1] === '')) {
  491. didSomething = true;
  492. parts.pop();
  493. }
  494. }
  495. // <pre>/<p>/../<rest> -> <pre>/<rest>
  496. let dd = 0;
  497. while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
  498. const p = parts[dd - 1];
  499. if (p && p !== '.' && p !== '..' && p !== '**') {
  500. didSomething = true;
  501. const needDot = dd === 1 && parts[dd + 1] === '**';
  502. const splin = needDot ? ['.'] : [];
  503. parts.splice(dd - 1, 2, ...splin);
  504. if (parts.length === 0)
  505. parts.push('');
  506. dd -= 2;
  507. }
  508. }
  509. }
  510. } while (didSomething);
  511. return globParts;
  512. }
  513. // second phase: multi-pattern dedupes
  514. // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
  515. // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
  516. // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
  517. //
  518. // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
  519. // ^-- not valid because ** doens't follow symlinks
  520. secondPhasePreProcess(globParts) {
  521. for (let i = 0; i < globParts.length - 1; i++) {
  522. for (let j = i + 1; j < globParts.length; j++) {
  523. const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
  524. if (matched) {
  525. globParts[i] = [];
  526. globParts[j] = matched;
  527. break;
  528. }
  529. }
  530. }
  531. return globParts.filter(gs => gs.length);
  532. }
  533. partsMatch(a, b, emptyGSMatch = false) {
  534. let ai = 0;
  535. let bi = 0;
  536. let result = [];
  537. let which = '';
  538. while (ai < a.length && bi < b.length) {
  539. if (a[ai] === b[bi]) {
  540. result.push(which === 'b' ? b[bi] : a[ai]);
  541. ai++;
  542. bi++;
  543. }
  544. else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
  545. result.push(a[ai]);
  546. ai++;
  547. }
  548. else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
  549. result.push(b[bi]);
  550. bi++;
  551. }
  552. else if (a[ai] === '*' &&
  553. b[bi] &&
  554. (this.options.dot || !b[bi].startsWith('.')) &&
  555. b[bi] !== '**') {
  556. if (which === 'b')
  557. return false;
  558. which = 'a';
  559. result.push(a[ai]);
  560. ai++;
  561. bi++;
  562. }
  563. else if (b[bi] === '*' &&
  564. a[ai] &&
  565. (this.options.dot || !a[ai].startsWith('.')) &&
  566. a[ai] !== '**') {
  567. if (which === 'a')
  568. return false;
  569. which = 'b';
  570. result.push(b[bi]);
  571. ai++;
  572. bi++;
  573. }
  574. else {
  575. return false;
  576. }
  577. }
  578. // if we fall out of the loop, it means they two are identical
  579. // as long as their lengths match
  580. return a.length === b.length && result;
  581. }
  582. parseNegate() {
  583. if (this.nonegate)
  584. return;
  585. const pattern = this.pattern;
  586. let negate = false;
  587. let negateOffset = 0;
  588. for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
  589. negate = !negate;
  590. negateOffset++;
  591. }
  592. if (negateOffset)
  593. this.pattern = pattern.slice(negateOffset);
  594. this.negate = negate;
  595. }
  596. // set partial to true to test if, for example,
  597. // "/a/b" matches the start of "/*/b/*/d"
  598. // Partial means, if you run out of file before you run
  599. // out of pattern, then that's fine, as long as all
  600. // the parts match.
  601. matchOne(file, pattern, partial = false) {
  602. let fileStartIndex = 0;
  603. let patternStartIndex = 0;
  604. // UNC paths like //?/X:/... can match X:/... and vice versa
  605. // Drive letters in absolute drive or unc paths are always compared
  606. // case-insensitively.
  607. if (this.isWindows) {
  608. const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
  609. const fileUNC = !fileDrive &&
  610. file[0] === '' &&
  611. file[1] === '' &&
  612. file[2] === '?' &&
  613. /^[a-z]:$/i.test(file[3]);
  614. const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
  615. const patternUNC = !patternDrive &&
  616. pattern[0] === '' &&
  617. pattern[1] === '' &&
  618. pattern[2] === '?' &&
  619. typeof pattern[3] === 'string' &&
  620. /^[a-z]:$/i.test(pattern[3]);
  621. const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined;
  622. const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined;
  623. if (typeof fdi === 'number' && typeof pdi === 'number') {
  624. const [fd, pd] = [
  625. file[fdi],
  626. pattern[pdi],
  627. ];
  628. if (fd.toLowerCase() === pd.toLowerCase()) {
  629. pattern[pdi] = fd;
  630. patternStartIndex = pdi;
  631. fileStartIndex = fdi;
  632. }
  633. }
  634. }
  635. // resolve and reduce . and .. portions in the file as well.
  636. // dont' need to do the second phase, because it's only one string[]
  637. const { optimizationLevel = 1 } = this.options;
  638. if (optimizationLevel >= 2) {
  639. file = this.levelTwoFileOptimize(file);
  640. }
  641. if (pattern.includes(GLOBSTAR)) {
  642. return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
  643. }
  644. return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
  645. }
  646. #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
  647. const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
  648. const lastgs = pattern.lastIndexOf(GLOBSTAR);
  649. const [head, body, tail] = partial ? [
  650. pattern.slice(patternIndex, firstgs),
  651. pattern.slice(firstgs + 1),
  652. [],
  653. ] : [
  654. pattern.slice(patternIndex, firstgs),
  655. pattern.slice(firstgs + 1, lastgs),
  656. pattern.slice(lastgs + 1),
  657. ];
  658. if (head.length) {
  659. const fileHead = file.slice(fileIndex, fileIndex + head.length);
  660. if (!this.#matchOne(fileHead, head, partial, 0, 0))
  661. return false;
  662. fileIndex += head.length;
  663. }
  664. let fileTailMatch = 0;
  665. if (tail.length) {
  666. if (tail.length + fileIndex > file.length)
  667. return false;
  668. let tailStart = file.length - tail.length;
  669. if (this.#matchOne(file, tail, partial, tailStart, 0)) {
  670. fileTailMatch = tail.length;
  671. }
  672. else {
  673. if (file[file.length - 1] !== '' ||
  674. fileIndex + tail.length === file.length) {
  675. return false;
  676. }
  677. tailStart--;
  678. if (!this.#matchOne(file, tail, partial, tailStart, 0))
  679. return false;
  680. fileTailMatch = tail.length + 1;
  681. }
  682. }
  683. if (!body.length) {
  684. let sawSome = !!fileTailMatch;
  685. for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
  686. const f = String(file[i]);
  687. sawSome = true;
  688. if (f === '.' || f === '..' ||
  689. (!this.options.dot && f.startsWith('.'))) {
  690. return false;
  691. }
  692. }
  693. return partial || sawSome;
  694. }
  695. const bodySegments = [[[], 0]];
  696. let currentBody = bodySegments[0];
  697. let nonGsParts = 0;
  698. const nonGsPartsSums = [0];
  699. for (const b of body) {
  700. if (b === GLOBSTAR) {
  701. nonGsPartsSums.push(nonGsParts);
  702. currentBody = [[], 0];
  703. bodySegments.push(currentBody);
  704. }
  705. else {
  706. currentBody[0].push(b);
  707. nonGsParts++;
  708. }
  709. }
  710. let i = bodySegments.length - 1;
  711. const fileLength = file.length - fileTailMatch;
  712. for (const b of bodySegments) {
  713. b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
  714. }
  715. return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
  716. }
  717. #matchGlobStarBodySections(file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
  718. const bs = bodySegments[bodyIndex];
  719. if (!bs) {
  720. for (let i = fileIndex; i < file.length; i++) {
  721. sawTail = true;
  722. const f = file[i];
  723. if (f === '.' || f === '..' ||
  724. (!this.options.dot && f.startsWith('.'))) {
  725. return false;
  726. }
  727. }
  728. return sawTail;
  729. }
  730. const [body, after] = bs;
  731. while (fileIndex <= after) {
  732. const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
  733. if (m && globStarDepth < this.maxGlobstarRecursion) {
  734. const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
  735. if (sub !== false)
  736. return sub;
  737. }
  738. const f = file[fileIndex];
  739. if (f === '.' || f === '..' ||
  740. (!this.options.dot && f.startsWith('.'))) {
  741. return false;
  742. }
  743. fileIndex++;
  744. }
  745. return partial || null;
  746. }
  747. #matchOne(file, pattern, partial, fileIndex, patternIndex) {
  748. let fi;
  749. let pi;
  750. let pl;
  751. let fl;
  752. for (fi = fileIndex, pi = patternIndex,
  753. fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
  754. this.debug('matchOne loop');
  755. let p = pattern[pi];
  756. let f = file[fi];
  757. this.debug(pattern, p, f);
  758. /* c8 ignore start */
  759. if (p === false || p === GLOBSTAR)
  760. return false;
  761. /* c8 ignore stop */
  762. let hit;
  763. if (typeof p === 'string') {
  764. hit = f === p;
  765. this.debug('string match', p, f, hit);
  766. }
  767. else {
  768. hit = p.test(f);
  769. this.debug('pattern match', p, f, hit);
  770. }
  771. if (!hit)
  772. return false;
  773. }
  774. if (fi === fl && pi === pl) {
  775. return true;
  776. }
  777. else if (fi === fl) {
  778. return partial;
  779. }
  780. else if (pi === pl) {
  781. return fi === fl - 1 && file[fi] === '';
  782. /* c8 ignore start */
  783. }
  784. else {
  785. throw new Error('wtf?');
  786. }
  787. /* c8 ignore stop */
  788. }
  789. braceExpand() {
  790. return braceExpand(this.pattern, this.options);
  791. }
  792. parse(pattern) {
  793. assertValidPattern(pattern);
  794. const options = this.options;
  795. // shortcuts
  796. if (pattern === '**')
  797. return GLOBSTAR;
  798. if (pattern === '')
  799. return '';
  800. // far and away, the most common glob pattern parts are
  801. // *, *.*, and *.<ext> Add a fast check method for those.
  802. let m;
  803. let fastTest = null;
  804. if ((m = pattern.match(starRE))) {
  805. fastTest = options.dot ? starTestDot : starTest;
  806. }
  807. else if ((m = pattern.match(starDotExtRE))) {
  808. fastTest = (options.nocase
  809. ? options.dot
  810. ? starDotExtTestNocaseDot
  811. : starDotExtTestNocase
  812. : options.dot
  813. ? starDotExtTestDot
  814. : starDotExtTest)(m[1]);
  815. }
  816. else if ((m = pattern.match(qmarksRE))) {
  817. fastTest = (options.nocase
  818. ? options.dot
  819. ? qmarksTestNocaseDot
  820. : qmarksTestNocase
  821. : options.dot
  822. ? qmarksTestDot
  823. : qmarksTest)(m);
  824. }
  825. else if ((m = pattern.match(starDotStarRE))) {
  826. fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
  827. }
  828. else if ((m = pattern.match(dotStarRE))) {
  829. fastTest = dotStarTest;
  830. }
  831. const re = AST.fromGlob(pattern, this.options).toMMPattern();
  832. if (fastTest && typeof re === 'object') {
  833. // Avoids overriding in frozen environments
  834. Reflect.defineProperty(re, 'test', { value: fastTest });
  835. }
  836. return re;
  837. }
  838. makeRe() {
  839. if (this.regexp || this.regexp === false)
  840. return this.regexp;
  841. // at this point, this.set is a 2d array of partial
  842. // pattern strings, or "**".
  843. //
  844. // It's better to use .match(). This function shouldn't
  845. // be used, really, but it's pretty convenient sometimes,
  846. // when you just want to work with a regex.
  847. const set = this.set;
  848. if (!set.length) {
  849. this.regexp = false;
  850. return this.regexp;
  851. }
  852. const options = this.options;
  853. const twoStar = options.noglobstar
  854. ? star
  855. : options.dot
  856. ? twoStarDot
  857. : twoStarNoDot;
  858. const flags = new Set(options.nocase ? ['i'] : []);
  859. // regexpify non-globstar patterns
  860. // if ** is only item, then we just do one twoStar
  861. // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
  862. // if ** is last, append (\/twoStar|) to previous
  863. // if ** is in the middle, append (\/|\/twoStar\/) to previous
  864. // then filter out GLOBSTAR symbols
  865. let re = set
  866. .map(pattern => {
  867. const pp = pattern.map(p => {
  868. if (p instanceof RegExp) {
  869. for (const f of p.flags.split(''))
  870. flags.add(f);
  871. }
  872. return typeof p === 'string'
  873. ? regExpEscape(p)
  874. : p === GLOBSTAR
  875. ? GLOBSTAR
  876. : p._src;
  877. });
  878. pp.forEach((p, i) => {
  879. const next = pp[i + 1];
  880. const prev = pp[i - 1];
  881. if (p !== GLOBSTAR || prev === GLOBSTAR) {
  882. return;
  883. }
  884. if (prev === undefined) {
  885. if (next !== undefined && next !== GLOBSTAR) {
  886. pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
  887. }
  888. else {
  889. pp[i] = twoStar;
  890. }
  891. }
  892. else if (next === undefined) {
  893. pp[i - 1] = prev + '(?:\\/|' + twoStar + ')?';
  894. }
  895. else if (next !== GLOBSTAR) {
  896. pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
  897. pp[i + 1] = GLOBSTAR;
  898. }
  899. });
  900. return pp.filter(p => p !== GLOBSTAR).join('/');
  901. })
  902. .join('|');
  903. // need to wrap in parens if we had more than one thing with |,
  904. // otherwise only the first will be anchored to ^ and the last to $
  905. const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
  906. // must match entire pattern
  907. // ending in a * or ** will make it less strict.
  908. re = '^' + open + re + close + '$';
  909. // can match anything, as long as it's not this.
  910. if (this.negate)
  911. re = '^(?!' + re + ').+$';
  912. try {
  913. this.regexp = new RegExp(re, [...flags].join(''));
  914. /* c8 ignore start */
  915. }
  916. catch (ex) {
  917. // should be impossible
  918. this.regexp = false;
  919. }
  920. /* c8 ignore stop */
  921. return this.regexp;
  922. }
  923. slashSplit(p) {
  924. // if p starts with // on windows, we preserve that
  925. // so that UNC paths aren't broken. Otherwise, any number of
  926. // / characters are coalesced into one, unless
  927. // preserveMultipleSlashes is set to true.
  928. if (this.preserveMultipleSlashes) {
  929. return p.split('/');
  930. }
  931. else if (this.isWindows && /^\/\/[^\/]+/.test(p)) {
  932. // add an extra '' for the one we lose
  933. return ['', ...p.split(/\/+/)];
  934. }
  935. else {
  936. return p.split(/\/+/);
  937. }
  938. }
  939. match(f, partial = this.partial) {
  940. this.debug('match', f, this.pattern);
  941. // short-circuit in the case of busted things.
  942. // comments, etc.
  943. if (this.comment) {
  944. return false;
  945. }
  946. if (this.empty) {
  947. return f === '';
  948. }
  949. if (f === '/' && partial) {
  950. return true;
  951. }
  952. const options = this.options;
  953. // windows: need to use /, not \
  954. if (this.isWindows) {
  955. f = f.split('\\').join('/');
  956. }
  957. // treat the test path as a set of pathparts.
  958. const ff = this.slashSplit(f);
  959. this.debug(this.pattern, 'split', ff);
  960. // just ONE of the pattern sets in this.set needs to match
  961. // in order for it to be valid. If negating, then just one
  962. // match means that we have failed.
  963. // Either way, return on the first hit.
  964. const set = this.set;
  965. this.debug(this.pattern, 'set', set);
  966. // Find the basename of the path by looking for the last non-empty segment
  967. let filename = ff[ff.length - 1];
  968. if (!filename) {
  969. for (let i = ff.length - 2; !filename && i >= 0; i--) {
  970. filename = ff[i];
  971. }
  972. }
  973. for (let i = 0; i < set.length; i++) {
  974. const pattern = set[i];
  975. let file = ff;
  976. if (options.matchBase && pattern.length === 1) {
  977. file = [filename];
  978. }
  979. const hit = this.matchOne(file, pattern, partial);
  980. if (hit) {
  981. if (options.flipNegate) {
  982. return true;
  983. }
  984. return !this.negate;
  985. }
  986. }
  987. // didn't get any hits. this is success if it's a negative
  988. // pattern, failure otherwise.
  989. if (options.flipNegate) {
  990. return false;
  991. }
  992. return this.negate;
  993. }
  994. static defaults(def) {
  995. return minimatch.defaults(def).Minimatch;
  996. }
  997. }
  998. /* c8 ignore start */
  999. export { AST } from './ast.js';
  1000. export { escape } from './escape.js';
  1001. export { unescape } from './unescape.js';
  1002. /* c8 ignore stop */
  1003. minimatch.AST = AST;
  1004. minimatch.Minimatch = Minimatch;
  1005. minimatch.escape = escape;
  1006. minimatch.unescape = unescape;
  1007. //# sourceMappingURL=index.js.map