index.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  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. regexp;
  197. constructor(pattern, options = {}) {
  198. assertValidPattern(pattern);
  199. options = options || {};
  200. this.options = options;
  201. this.pattern = pattern;
  202. this.platform = options.platform || defaultPlatform;
  203. this.isWindows = this.platform === 'win32';
  204. this.windowsPathsNoEscape =
  205. !!options.windowsPathsNoEscape || options.allowWindowsEscape === false;
  206. if (this.windowsPathsNoEscape) {
  207. this.pattern = this.pattern.replace(/\\/g, '/');
  208. }
  209. this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
  210. this.regexp = null;
  211. this.negate = false;
  212. this.nonegate = !!options.nonegate;
  213. this.comment = false;
  214. this.empty = false;
  215. this.partial = !!options.partial;
  216. this.nocase = !!this.options.nocase;
  217. this.windowsNoMagicRoot =
  218. options.windowsNoMagicRoot !== undefined
  219. ? options.windowsNoMagicRoot
  220. : !!(this.isWindows && this.nocase);
  221. this.globSet = [];
  222. this.globParts = [];
  223. this.set = [];
  224. // make the set of regexps etc.
  225. this.make();
  226. }
  227. hasMagic() {
  228. if (this.options.magicalBraces && this.set.length > 1) {
  229. return true;
  230. }
  231. for (const pattern of this.set) {
  232. for (const part of pattern) {
  233. if (typeof part !== 'string')
  234. return true;
  235. }
  236. }
  237. return false;
  238. }
  239. debug(..._) { }
  240. make() {
  241. const pattern = this.pattern;
  242. const options = this.options;
  243. // empty patterns and comments match nothing.
  244. if (!options.nocomment && pattern.charAt(0) === '#') {
  245. this.comment = true;
  246. return;
  247. }
  248. if (!pattern) {
  249. this.empty = true;
  250. return;
  251. }
  252. // step 1: figure out negation, etc.
  253. this.parseNegate();
  254. // step 2: expand braces
  255. this.globSet = [...new Set(this.braceExpand())];
  256. if (options.debug) {
  257. this.debug = (...args) => console.error(...args);
  258. }
  259. this.debug(this.pattern, this.globSet);
  260. // step 3: now we have a set, so turn each one into a series of
  261. // path-portion matching patterns.
  262. // These will be regexps, except in the case of "**", which is
  263. // set to the GLOBSTAR object for globstar behavior,
  264. // and will not contain any / characters
  265. //
  266. // First, we preprocess to make the glob pattern sets a bit simpler
  267. // and deduped. There are some perf-killing patterns that can cause
  268. // problems with a glob walk, but we can simplify them down a bit.
  269. const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
  270. this.globParts = this.preprocess(rawGlobParts);
  271. this.debug(this.pattern, this.globParts);
  272. // glob --> regexps
  273. let set = this.globParts.map((s, _, __) => {
  274. if (this.isWindows && this.windowsNoMagicRoot) {
  275. // check if it's a drive or unc path.
  276. const isUNC = s[0] === '' &&
  277. s[1] === '' &&
  278. (s[2] === '?' || !globMagic.test(s[2])) &&
  279. !globMagic.test(s[3]);
  280. const isDrive = /^[a-z]:/i.test(s[0]);
  281. if (isUNC) {
  282. return [...s.slice(0, 4), ...s.slice(4).map(ss => this.parse(ss))];
  283. }
  284. else if (isDrive) {
  285. return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
  286. }
  287. }
  288. return s.map(ss => this.parse(ss));
  289. });
  290. this.debug(this.pattern, set);
  291. // filter out everything that didn't compile properly.
  292. this.set = set.filter(s => s.indexOf(false) === -1);
  293. // do not treat the ? in UNC paths as magic
  294. if (this.isWindows) {
  295. for (let i = 0; i < this.set.length; i++) {
  296. const p = this.set[i];
  297. if (p[0] === '' &&
  298. p[1] === '' &&
  299. this.globParts[i][2] === '?' &&
  300. typeof p[3] === 'string' &&
  301. /^[a-z]:$/i.test(p[3])) {
  302. p[2] = '?';
  303. }
  304. }
  305. }
  306. this.debug(this.pattern, this.set);
  307. }
  308. // various transforms to equivalent pattern sets that are
  309. // faster to process in a filesystem walk. The goal is to
  310. // eliminate what we can, and push all ** patterns as far
  311. // to the right as possible, even if it increases the number
  312. // of patterns that we have to process.
  313. preprocess(globParts) {
  314. // if we're not in globstar mode, then turn all ** into *
  315. if (this.options.noglobstar) {
  316. for (let i = 0; i < globParts.length; i++) {
  317. for (let j = 0; j < globParts[i].length; j++) {
  318. if (globParts[i][j] === '**') {
  319. globParts[i][j] = '*';
  320. }
  321. }
  322. }
  323. }
  324. const { optimizationLevel = 1 } = this.options;
  325. if (optimizationLevel >= 2) {
  326. // aggressive optimization for the purpose of fs walking
  327. globParts = this.firstPhasePreProcess(globParts);
  328. globParts = this.secondPhasePreProcess(globParts);
  329. }
  330. else if (optimizationLevel >= 1) {
  331. // just basic optimizations to remove some .. parts
  332. globParts = this.levelOneOptimize(globParts);
  333. }
  334. else {
  335. // just collapse multiple ** portions into one
  336. globParts = this.adjascentGlobstarOptimize(globParts);
  337. }
  338. return globParts;
  339. }
  340. // just get rid of adjascent ** portions
  341. adjascentGlobstarOptimize(globParts) {
  342. return globParts.map(parts => {
  343. let gs = -1;
  344. while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
  345. let i = gs;
  346. while (parts[i + 1] === '**') {
  347. i++;
  348. }
  349. if (i !== gs) {
  350. parts.splice(gs, i - gs);
  351. }
  352. }
  353. return parts;
  354. });
  355. }
  356. // get rid of adjascent ** and resolve .. portions
  357. levelOneOptimize(globParts) {
  358. return globParts.map(parts => {
  359. parts = parts.reduce((set, part) => {
  360. const prev = set[set.length - 1];
  361. if (part === '**' && prev === '**') {
  362. return set;
  363. }
  364. if (part === '..') {
  365. if (prev && prev !== '..' && prev !== '.' && prev !== '**') {
  366. set.pop();
  367. return set;
  368. }
  369. }
  370. set.push(part);
  371. return set;
  372. }, []);
  373. return parts.length === 0 ? [''] : parts;
  374. });
  375. }
  376. levelTwoFileOptimize(parts) {
  377. if (!Array.isArray(parts)) {
  378. parts = this.slashSplit(parts);
  379. }
  380. let didSomething = false;
  381. do {
  382. didSomething = false;
  383. // <pre>/<e>/<rest> -> <pre>/<rest>
  384. if (!this.preserveMultipleSlashes) {
  385. for (let i = 1; i < parts.length - 1; i++) {
  386. const p = parts[i];
  387. // don't squeeze out UNC patterns
  388. if (i === 1 && p === '' && parts[0] === '')
  389. continue;
  390. if (p === '.' || p === '') {
  391. didSomething = true;
  392. parts.splice(i, 1);
  393. i--;
  394. }
  395. }
  396. if (parts[0] === '.' &&
  397. parts.length === 2 &&
  398. (parts[1] === '.' || parts[1] === '')) {
  399. didSomething = true;
  400. parts.pop();
  401. }
  402. }
  403. // <pre>/<p>/../<rest> -> <pre>/<rest>
  404. let dd = 0;
  405. while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
  406. const p = parts[dd - 1];
  407. if (p && p !== '.' && p !== '..' && p !== '**') {
  408. didSomething = true;
  409. parts.splice(dd - 1, 2);
  410. dd -= 2;
  411. }
  412. }
  413. } while (didSomething);
  414. return parts.length === 0 ? [''] : parts;
  415. }
  416. // First phase: single-pattern processing
  417. // <pre> is 1 or more portions
  418. // <rest> is 1 or more portions
  419. // <p> is any portion other than ., .., '', or **
  420. // <e> is . or ''
  421. //
  422. // **/.. is *brutal* for filesystem walking performance, because
  423. // it effectively resets the recursive walk each time it occurs,
  424. // and ** cannot be reduced out by a .. pattern part like a regexp
  425. // or most strings (other than .., ., and '') can be.
  426. //
  427. // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
  428. // <pre>/<e>/<rest> -> <pre>/<rest>
  429. // <pre>/<p>/../<rest> -> <pre>/<rest>
  430. // **/**/<rest> -> **/<rest>
  431. //
  432. // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
  433. // this WOULD be allowed if ** did follow symlinks, or * didn't
  434. firstPhasePreProcess(globParts) {
  435. let didSomething = false;
  436. do {
  437. didSomething = false;
  438. // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
  439. for (let parts of globParts) {
  440. let gs = -1;
  441. while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
  442. let gss = gs;
  443. while (parts[gss + 1] === '**') {
  444. // <pre>/**/**/<rest> -> <pre>/**/<rest>
  445. gss++;
  446. }
  447. // eg, if gs is 2 and gss is 4, that means we have 3 **
  448. // parts, and can remove 2 of them.
  449. if (gss > gs) {
  450. parts.splice(gs + 1, gss - gs);
  451. }
  452. let next = parts[gs + 1];
  453. const p = parts[gs + 2];
  454. const p2 = parts[gs + 3];
  455. if (next !== '..')
  456. continue;
  457. if (!p ||
  458. p === '.' ||
  459. p === '..' ||
  460. !p2 ||
  461. p2 === '.' ||
  462. p2 === '..') {
  463. continue;
  464. }
  465. didSomething = true;
  466. // edit parts in place, and push the new one
  467. parts.splice(gs, 1);
  468. const other = parts.slice(0);
  469. other[gs] = '**';
  470. globParts.push(other);
  471. gs--;
  472. }
  473. // <pre>/<e>/<rest> -> <pre>/<rest>
  474. if (!this.preserveMultipleSlashes) {
  475. for (let i = 1; i < parts.length - 1; i++) {
  476. const p = parts[i];
  477. // don't squeeze out UNC patterns
  478. if (i === 1 && p === '' && parts[0] === '')
  479. continue;
  480. if (p === '.' || p === '') {
  481. didSomething = true;
  482. parts.splice(i, 1);
  483. i--;
  484. }
  485. }
  486. if (parts[0] === '.' &&
  487. parts.length === 2 &&
  488. (parts[1] === '.' || parts[1] === '')) {
  489. didSomething = true;
  490. parts.pop();
  491. }
  492. }
  493. // <pre>/<p>/../<rest> -> <pre>/<rest>
  494. let dd = 0;
  495. while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
  496. const p = parts[dd - 1];
  497. if (p && p !== '.' && p !== '..' && p !== '**') {
  498. didSomething = true;
  499. const needDot = dd === 1 && parts[dd + 1] === '**';
  500. const splin = needDot ? ['.'] : [];
  501. parts.splice(dd - 1, 2, ...splin);
  502. if (parts.length === 0)
  503. parts.push('');
  504. dd -= 2;
  505. }
  506. }
  507. }
  508. } while (didSomething);
  509. return globParts;
  510. }
  511. // second phase: multi-pattern dedupes
  512. // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
  513. // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
  514. // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
  515. //
  516. // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
  517. // ^-- not valid because ** doens't follow symlinks
  518. secondPhasePreProcess(globParts) {
  519. for (let i = 0; i < globParts.length - 1; i++) {
  520. for (let j = i + 1; j < globParts.length; j++) {
  521. const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
  522. if (matched) {
  523. globParts[i] = [];
  524. globParts[j] = matched;
  525. break;
  526. }
  527. }
  528. }
  529. return globParts.filter(gs => gs.length);
  530. }
  531. partsMatch(a, b, emptyGSMatch = false) {
  532. let ai = 0;
  533. let bi = 0;
  534. let result = [];
  535. let which = '';
  536. while (ai < a.length && bi < b.length) {
  537. if (a[ai] === b[bi]) {
  538. result.push(which === 'b' ? b[bi] : a[ai]);
  539. ai++;
  540. bi++;
  541. }
  542. else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
  543. result.push(a[ai]);
  544. ai++;
  545. }
  546. else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
  547. result.push(b[bi]);
  548. bi++;
  549. }
  550. else if (a[ai] === '*' &&
  551. b[bi] &&
  552. (this.options.dot || !b[bi].startsWith('.')) &&
  553. b[bi] !== '**') {
  554. if (which === 'b')
  555. return false;
  556. which = 'a';
  557. result.push(a[ai]);
  558. ai++;
  559. bi++;
  560. }
  561. else if (b[bi] === '*' &&
  562. a[ai] &&
  563. (this.options.dot || !a[ai].startsWith('.')) &&
  564. a[ai] !== '**') {
  565. if (which === 'a')
  566. return false;
  567. which = 'b';
  568. result.push(b[bi]);
  569. ai++;
  570. bi++;
  571. }
  572. else {
  573. return false;
  574. }
  575. }
  576. // if we fall out of the loop, it means they two are identical
  577. // as long as their lengths match
  578. return a.length === b.length && result;
  579. }
  580. parseNegate() {
  581. if (this.nonegate)
  582. return;
  583. const pattern = this.pattern;
  584. let negate = false;
  585. let negateOffset = 0;
  586. for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
  587. negate = !negate;
  588. negateOffset++;
  589. }
  590. if (negateOffset)
  591. this.pattern = pattern.slice(negateOffset);
  592. this.negate = negate;
  593. }
  594. // set partial to true to test if, for example,
  595. // "/a/b" matches the start of "/*/b/*/d"
  596. // Partial means, if you run out of file before you run
  597. // out of pattern, then that's fine, as long as all
  598. // the parts match.
  599. matchOne(file, pattern, partial = false) {
  600. const options = this.options;
  601. // UNC paths like //?/X:/... can match X:/... and vice versa
  602. // Drive letters in absolute drive or unc paths are always compared
  603. // case-insensitively.
  604. if (this.isWindows) {
  605. const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
  606. const fileUNC = !fileDrive &&
  607. file[0] === '' &&
  608. file[1] === '' &&
  609. file[2] === '?' &&
  610. /^[a-z]:$/i.test(file[3]);
  611. const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
  612. const patternUNC = !patternDrive &&
  613. pattern[0] === '' &&
  614. pattern[1] === '' &&
  615. pattern[2] === '?' &&
  616. typeof pattern[3] === 'string' &&
  617. /^[a-z]:$/i.test(pattern[3]);
  618. const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined;
  619. const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined;
  620. if (typeof fdi === 'number' && typeof pdi === 'number') {
  621. const [fd, pd] = [file[fdi], pattern[pdi]];
  622. if (fd.toLowerCase() === pd.toLowerCase()) {
  623. pattern[pdi] = fd;
  624. if (pdi > fdi) {
  625. pattern = pattern.slice(pdi);
  626. }
  627. else if (fdi > pdi) {
  628. file = file.slice(fdi);
  629. }
  630. }
  631. }
  632. }
  633. // resolve and reduce . and .. portions in the file as well.
  634. // dont' need to do the second phase, because it's only one string[]
  635. const { optimizationLevel = 1 } = this.options;
  636. if (optimizationLevel >= 2) {
  637. file = this.levelTwoFileOptimize(file);
  638. }
  639. this.debug('matchOne', this, { file, pattern });
  640. this.debug('matchOne', file.length, pattern.length);
  641. for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
  642. this.debug('matchOne loop');
  643. var p = pattern[pi];
  644. var f = file[fi];
  645. this.debug(pattern, p, f);
  646. // should be impossible.
  647. // some invalid regexp stuff in the set.
  648. /* c8 ignore start */
  649. if (p === false) {
  650. return false;
  651. }
  652. /* c8 ignore stop */
  653. if (p === GLOBSTAR) {
  654. this.debug('GLOBSTAR', [pattern, p, f]);
  655. // "**"
  656. // a/**/b/**/c would match the following:
  657. // a/b/x/y/z/c
  658. // a/x/y/z/b/c
  659. // a/b/x/b/x/c
  660. // a/b/c
  661. // To do this, take the rest of the pattern after
  662. // the **, and see if it would match the file remainder.
  663. // If so, return success.
  664. // If not, the ** "swallows" a segment, and try again.
  665. // This is recursively awful.
  666. //
  667. // a/**/b/**/c matching a/b/x/y/z/c
  668. // - a matches a
  669. // - doublestar
  670. // - matchOne(b/x/y/z/c, b/**/c)
  671. // - b matches b
  672. // - doublestar
  673. // - matchOne(x/y/z/c, c) -> no
  674. // - matchOne(y/z/c, c) -> no
  675. // - matchOne(z/c, c) -> no
  676. // - matchOne(c, c) yes, hit
  677. var fr = fi;
  678. var pr = pi + 1;
  679. if (pr === pl) {
  680. this.debug('** at the end');
  681. // a ** at the end will just swallow the rest.
  682. // We have found a match.
  683. // however, it will not swallow /.x, unless
  684. // options.dot is set.
  685. // . and .. are *never* matched by **, for explosively
  686. // exponential reasons.
  687. for (; fi < fl; fi++) {
  688. if (file[fi] === '.' ||
  689. file[fi] === '..' ||
  690. (!options.dot && file[fi].charAt(0) === '.'))
  691. return false;
  692. }
  693. return true;
  694. }
  695. // ok, let's see if we can swallow whatever we can.
  696. while (fr < fl) {
  697. var swallowee = file[fr];
  698. this.debug('\nglobstar while', file, fr, pattern, pr, swallowee);
  699. // XXX remove this slice. Just pass the start index.
  700. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
  701. this.debug('globstar found match!', fr, fl, swallowee);
  702. // found a match.
  703. return true;
  704. }
  705. else {
  706. // can't swallow "." or ".." ever.
  707. // can only swallow ".foo" when explicitly asked.
  708. if (swallowee === '.' ||
  709. swallowee === '..' ||
  710. (!options.dot && swallowee.charAt(0) === '.')) {
  711. this.debug('dot detected!', file, fr, pattern, pr);
  712. break;
  713. }
  714. // ** swallows a segment, and continue.
  715. this.debug('globstar swallow a segment, and continue');
  716. fr++;
  717. }
  718. }
  719. // no match was found.
  720. // However, in partial mode, we can't say this is necessarily over.
  721. /* c8 ignore start */
  722. if (partial) {
  723. // ran out of file
  724. this.debug('\n>>> no match, partial?', file, fr, pattern, pr);
  725. if (fr === fl) {
  726. return true;
  727. }
  728. }
  729. /* c8 ignore stop */
  730. return false;
  731. }
  732. // something other than **
  733. // non-magic patterns just have to match exactly
  734. // patterns with magic have been turned into regexps.
  735. let hit;
  736. if (typeof p === 'string') {
  737. hit = f === p;
  738. this.debug('string match', p, f, hit);
  739. }
  740. else {
  741. hit = p.test(f);
  742. this.debug('pattern match', p, f, hit);
  743. }
  744. if (!hit)
  745. return false;
  746. }
  747. // Note: ending in / means that we'll get a final ""
  748. // at the end of the pattern. This can only match a
  749. // corresponding "" at the end of the file.
  750. // If the file ends in /, then it can only match a
  751. // a pattern that ends in /, unless the pattern just
  752. // doesn't have any more for it. But, a/b/ should *not*
  753. // match "a/b/*", even though "" matches against the
  754. // [^/]*? pattern, except in partial mode, where it might
  755. // simply not be reached yet.
  756. // However, a/b/ should still satisfy a/*
  757. // now either we fell off the end of the pattern, or we're done.
  758. if (fi === fl && pi === pl) {
  759. // ran out of pattern and filename at the same time.
  760. // an exact hit!
  761. return true;
  762. }
  763. else if (fi === fl) {
  764. // ran out of file, but still had pattern left.
  765. // this is ok if we're doing the match as part of
  766. // a glob fs traversal.
  767. return partial;
  768. }
  769. else if (pi === pl) {
  770. // ran out of pattern, still have file left.
  771. // this is only acceptable if we're on the very last
  772. // empty segment of a file with a trailing slash.
  773. // a/* should match a/b/
  774. return fi === fl - 1 && file[fi] === '';
  775. /* c8 ignore start */
  776. }
  777. else {
  778. // should be unreachable.
  779. throw new Error('wtf?');
  780. }
  781. /* c8 ignore stop */
  782. }
  783. braceExpand() {
  784. return braceExpand(this.pattern, this.options);
  785. }
  786. parse(pattern) {
  787. assertValidPattern(pattern);
  788. const options = this.options;
  789. // shortcuts
  790. if (pattern === '**')
  791. return GLOBSTAR;
  792. if (pattern === '')
  793. return '';
  794. // far and away, the most common glob pattern parts are
  795. // *, *.*, and *.<ext> Add a fast check method for those.
  796. let m;
  797. let fastTest = null;
  798. if ((m = pattern.match(starRE))) {
  799. fastTest = options.dot ? starTestDot : starTest;
  800. }
  801. else if ((m = pattern.match(starDotExtRE))) {
  802. fastTest = (options.nocase
  803. ? options.dot
  804. ? starDotExtTestNocaseDot
  805. : starDotExtTestNocase
  806. : options.dot
  807. ? starDotExtTestDot
  808. : starDotExtTest)(m[1]);
  809. }
  810. else if ((m = pattern.match(qmarksRE))) {
  811. fastTest = (options.nocase
  812. ? options.dot
  813. ? qmarksTestNocaseDot
  814. : qmarksTestNocase
  815. : options.dot
  816. ? qmarksTestDot
  817. : qmarksTest)(m);
  818. }
  819. else if ((m = pattern.match(starDotStarRE))) {
  820. fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
  821. }
  822. else if ((m = pattern.match(dotStarRE))) {
  823. fastTest = dotStarTest;
  824. }
  825. const re = AST.fromGlob(pattern, this.options).toMMPattern();
  826. if (fastTest && typeof re === 'object') {
  827. // Avoids overriding in frozen environments
  828. Reflect.defineProperty(re, 'test', { value: fastTest });
  829. }
  830. return re;
  831. }
  832. makeRe() {
  833. if (this.regexp || this.regexp === false)
  834. return this.regexp;
  835. // at this point, this.set is a 2d array of partial
  836. // pattern strings, or "**".
  837. //
  838. // It's better to use .match(). This function shouldn't
  839. // be used, really, but it's pretty convenient sometimes,
  840. // when you just want to work with a regex.
  841. const set = this.set;
  842. if (!set.length) {
  843. this.regexp = false;
  844. return this.regexp;
  845. }
  846. const options = this.options;
  847. const twoStar = options.noglobstar
  848. ? star
  849. : options.dot
  850. ? twoStarDot
  851. : twoStarNoDot;
  852. const flags = new Set(options.nocase ? ['i'] : []);
  853. // regexpify non-globstar patterns
  854. // if ** is only item, then we just do one twoStar
  855. // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
  856. // if ** is last, append (\/twoStar|) to previous
  857. // if ** is in the middle, append (\/|\/twoStar\/) to previous
  858. // then filter out GLOBSTAR symbols
  859. let re = set
  860. .map(pattern => {
  861. const pp = pattern.map(p => {
  862. if (p instanceof RegExp) {
  863. for (const f of p.flags.split(''))
  864. flags.add(f);
  865. }
  866. return typeof p === 'string'
  867. ? regExpEscape(p)
  868. : p === GLOBSTAR
  869. ? GLOBSTAR
  870. : p._src;
  871. });
  872. pp.forEach((p, i) => {
  873. const next = pp[i + 1];
  874. const prev = pp[i - 1];
  875. if (p !== GLOBSTAR || prev === GLOBSTAR) {
  876. return;
  877. }
  878. if (prev === undefined) {
  879. if (next !== undefined && next !== GLOBSTAR) {
  880. pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
  881. }
  882. else {
  883. pp[i] = twoStar;
  884. }
  885. }
  886. else if (next === undefined) {
  887. pp[i - 1] = prev + '(?:\\/|' + twoStar + ')?';
  888. }
  889. else if (next !== GLOBSTAR) {
  890. pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
  891. pp[i + 1] = GLOBSTAR;
  892. }
  893. });
  894. return pp.filter(p => p !== GLOBSTAR).join('/');
  895. })
  896. .join('|');
  897. // need to wrap in parens if we had more than one thing with |,
  898. // otherwise only the first will be anchored to ^ and the last to $
  899. const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
  900. // must match entire pattern
  901. // ending in a * or ** will make it less strict.
  902. re = '^' + open + re + close + '$';
  903. // can match anything, as long as it's not this.
  904. if (this.negate)
  905. re = '^(?!' + re + ').+$';
  906. try {
  907. this.regexp = new RegExp(re, [...flags].join(''));
  908. /* c8 ignore start */
  909. }
  910. catch (ex) {
  911. // should be impossible
  912. this.regexp = false;
  913. }
  914. /* c8 ignore stop */
  915. return this.regexp;
  916. }
  917. slashSplit(p) {
  918. // if p starts with // on windows, we preserve that
  919. // so that UNC paths aren't broken. Otherwise, any number of
  920. // / characters are coalesced into one, unless
  921. // preserveMultipleSlashes is set to true.
  922. if (this.preserveMultipleSlashes) {
  923. return p.split('/');
  924. }
  925. else if (this.isWindows && /^\/\/[^\/]+/.test(p)) {
  926. // add an extra '' for the one we lose
  927. return ['', ...p.split(/\/+/)];
  928. }
  929. else {
  930. return p.split(/\/+/);
  931. }
  932. }
  933. match(f, partial = this.partial) {
  934. this.debug('match', f, this.pattern);
  935. // short-circuit in the case of busted things.
  936. // comments, etc.
  937. if (this.comment) {
  938. return false;
  939. }
  940. if (this.empty) {
  941. return f === '';
  942. }
  943. if (f === '/' && partial) {
  944. return true;
  945. }
  946. const options = this.options;
  947. // windows: need to use /, not \
  948. if (this.isWindows) {
  949. f = f.split('\\').join('/');
  950. }
  951. // treat the test path as a set of pathparts.
  952. const ff = this.slashSplit(f);
  953. this.debug(this.pattern, 'split', ff);
  954. // just ONE of the pattern sets in this.set needs to match
  955. // in order for it to be valid. If negating, then just one
  956. // match means that we have failed.
  957. // Either way, return on the first hit.
  958. const set = this.set;
  959. this.debug(this.pattern, 'set', set);
  960. // Find the basename of the path by looking for the last non-empty segment
  961. let filename = ff[ff.length - 1];
  962. if (!filename) {
  963. for (let i = ff.length - 2; !filename && i >= 0; i--) {
  964. filename = ff[i];
  965. }
  966. }
  967. for (let i = 0; i < set.length; i++) {
  968. const pattern = set[i];
  969. let file = ff;
  970. if (options.matchBase && pattern.length === 1) {
  971. file = [filename];
  972. }
  973. const hit = this.matchOne(file, pattern, partial);
  974. if (hit) {
  975. if (options.flipNegate) {
  976. return true;
  977. }
  978. return !this.negate;
  979. }
  980. }
  981. // didn't get any hits. this is success if it's a negative
  982. // pattern, failure otherwise.
  983. if (options.flipNegate) {
  984. return false;
  985. }
  986. return this.negate;
  987. }
  988. static defaults(def) {
  989. return minimatch.defaults(def).Minimatch;
  990. }
  991. }
  992. /* c8 ignore start */
  993. export { AST } from './ast.js';
  994. export { escape } from './escape.js';
  995. export { unescape } from './unescape.js';
  996. /* c8 ignore stop */
  997. minimatch.AST = AST;
  998. minimatch.Minimatch = Minimatch;
  999. minimatch.escape = escape;
  1000. minimatch.unescape = unescape;
  1001. //# sourceMappingURL=index.js.map