"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.unescape = exports.escape = exports.Minimatch = exports.match = exports.braceExpand = exports.defaults = exports.filter = exports.GLOBSTAR = exports.sep = exports.minimatch = void 0; const brace_expansion_1 = require("brace-expansion"); const nonrecursive_glob_match_1 = require("@isaacs/nonrecursive-glob-match"); const assert_valid_pattern_js_1 = require("./assert-valid-pattern.js"); const escape_js_1 = require("./escape.js"); const unescape_js_1 = require("./unescape.js"); const minimatch = (p, pattern, options = {}) => { (0, assert_valid_pattern_js_1.assertValidPattern)(pattern); // shortcut: comments match nothing. if (!options.nocomment && pattern.charAt(0) === '#') { return false; } return cachedMinimatch(pattern, options).match(p); }; exports.minimatch = minimatch; // The Minimatch objects that minimatch() used most recently. A Map // keeps its keys in the order they were set, so the first key is the // one used least recently. const cache = new Map(); const cacheMax = 256; const cachedMinimatch = (pattern, options) => { const key = pattern + '\0' + JSON.stringify(options); let mm = cache.get(key); if (mm) { cache.delete(key); } else { // a copy, so a later change to the options can not change it mm = new Minimatch(pattern, { ...options }); if (cache.size >= cacheMax) { cache.delete(cache.keys().next().value); } } cache.set(key, mm); return mm; }; /* c8 ignore start */ const defaultPlatform = (typeof process === 'object' && process ? process.env?.__MINIMATCH_TESTING_PLATFORM__ || process.platform : 'posix'); const path = { win32: { sep: '\\' }, posix: { sep: '/' }, }; /* c8 ignore stop */ exports.sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep; exports.minimatch.sep = exports.sep; exports.GLOBSTAR = Symbol('globstar **'); exports.minimatch.GLOBSTAR = exports.GLOBSTAR; const filter = (pattern, options = {}) => (p) => (0, exports.minimatch)(p, pattern, options); exports.filter = filter; exports.minimatch.filter = exports.filter; const ext = (a, b = {}) => Object.assign({}, a, b); const defaults = (def) => { if (!def || typeof def !== 'object' || !Object.keys(def).length) { return exports.minimatch; } const orig = exports.minimatch; const m = (p, pattern, options = {}) => orig(p, pattern, ext(def, options)); return Object.assign(m, { Minimatch: class Minimatch extends orig.Minimatch { constructor(pattern, options = {}) { super(pattern, ext(def, options)); } static defaults(options) { return orig.defaults(ext(def, options)).Minimatch; } }, unescape: (s, options = {}) => orig.unescape(s, ext(def, options)), escape: (s, options = {}) => orig.escape(s, ext(def, options)), filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)), defaults: (options) => orig.defaults(ext(def, options)), braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)), match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)), sep: orig.sep, GLOBSTAR: exports.GLOBSTAR, }); }; exports.defaults = defaults; exports.minimatch.defaults = exports.defaults; // Brace expansion: // a{b,c}d -> abd acd // a{b,}c -> abc ac // a{0..3}d -> a0d a1d a2d a3d // a{b,c{d,e}f}g -> abg acdfg acefg // a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg // // Invalid sets are not expanded. // a{2..}b -> a{2..}b // a{b}c -> a{b}c const braceExpand = (pattern, options = {}) => { (0, assert_valid_pattern_js_1.assertValidPattern)(pattern); // Thanks to Yeting Li for // improving this regexp to avoid a ReDOS vulnerability. if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) { // shortcut. no need to expand. return [pattern]; } return (0, brace_expansion_1.expand)(pattern, { max: options.braceExpandMax }); }; exports.braceExpand = braceExpand; exports.minimatch.braceExpand = exports.braceExpand; const match = (list, pattern, options = {}) => { const mm = new Minimatch(pattern, options); list = list.filter(f => mm.match(f)); if (mm.options.nonull && !list.length) { list.push(pattern); } return list; }; exports.match = match; exports.minimatch.match = exports.match; const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/; // Index of the `]` that closes the class that starts at `start`, // or -1 if it is not closed. Posix classes such as `[:alpha:]` are // not skipped, so this can find a `]` before the real end. That // only makes a class look less like a literal. const classEnd = (pattern, start) => { let i = start + 1; if (pattern.charAt(i) === '!' || pattern.charAt(i) === '^') i++; let sawStart = false; for (; i < pattern.length; i++) { const c = pattern.charAt(i); if (c === ']' && sawStart) return i; sawStart = true; if (c === '\\') i++; } return -1; }; // If a path portion pattern has no magic, return the string it // matches. Otherwise, return undefined. This can report magic for // a pattern that is in fact literal (eg, `[]`), but never the // reverse, so a string result is always safe to compare directly. const literal = (pattern, noext) => { if (!noext && (pattern === '+()' || pattern === '@()')) return pattern; let lit = ''; for (let i = 0; i < pattern.length; i++) { const c = pattern.charAt(i); if (c === '\\') { // a trailing \ is a literal \ lit += pattern.charAt(++i) || c; } else if (c === '*' || c === '?') { return undefined; } else if (c === '[') { const close = classEnd(pattern, i); if (close === -1) { // not closed, so the [ is a literal lit += c; continue; } // a class of one char, such as `[*]` or `[\b]`, is a literal let body = pattern.slice(i + 1, close); if (body.startsWith('\\')) body = body.slice(1); if ([...body].length !== 1 || '!^'.includes(pattern.charAt(i + 1))) { return undefined; } lit += body; i = close; } else if (!noext && '+@!'.includes(c) && pattern.charAt(i + 1) === '(' && pattern.includes(')', i + 2)) { return undefined; } else { lit += c; } } return lit; }; // True if the portion pattern has the same meaning as an alternative // inside `@(...)`. It must not have a `|` or `)` that is not in a // closed extglob (those are literal at the top level), an extglob // that is not closed (the `)` of the `@(...)` would close it), or a // trailing `\` (it would escape the next `|`). A pattern that is // only an empty extglob, such as `?()`, is literal on its own, but // not in `@(...)`. Posix classes such as `[:alpha:]` can hide a `]` // from classEnd, so patterns with them are not merged. const extSafe = (pattern) => { if (/^[?*+@]\(\)$/.test(pattern) || pattern.includes('[:')) return false; let depth = 0; for (let i = 0; i < pattern.length; i++) { const c = pattern.charAt(i); if (c === '\\') { if (++i === pattern.length) return false; } else if (c === '[') { const close = classEnd(pattern, i); if (close !== -1) i = close; } else if ('+@!?*'.includes(c) && pattern.charAt(i + 1) === '(') { depth++; i++; } else if (c === ')') { if (!depth--) return false; } else if (c === '|' && !depth) { return false; } } return !depth; }; // The last path that Minimatch#match split, and its parts. A path is // often tested against many patterns in a row (such as a list of // ignore patterns), so this saves splitting it again. The parts are // never changed, so they can be shared. let lastSplit; // The last path that Minimatch#match checked before splitting it. If // the same path is checked again, it is being tested against many // patterns, so it is split and the parts are kept for the others. let lastChecked; // true if a pattern portion that is not ** matches a path portion. // An empty path portion only matches an empty pattern portion. A path // portion never has a `/`, so the Matcher does not need to check it. const portionMatch = (p, f) => typeof p === 'string' ? f === p : f !== '' && p.testUnchecked(f); // Where the last portion of a row matches in a path: the start of the // last portion of the path, or of the one before a trailing `/`. -1 if // it matches neither. const endIndex = (p, last, lastStart, prev, prevStart) => portionMatch(p, last) ? lastStart : prev !== undefined && portionMatch(p, prev) ? prevStart : -1; const isDots = (p) => p === '.' || p === '..'; // a Windows drive letter portion, like `c:` const isDrive = (p) => typeof p === 'string' && /^[a-z]:$/i.test(p); // true for the `UNC` portion of a long UNC root, //?/UNC/host/share const isLongUNC = (p) => typeof p === 'string' && /^unc$/i.test(p); // The index of the host in a row or split path that starts with a // Windows UNC root: 2 for `//host/share`, and 4 for // `//?/UNC/host/share`. Otherwise -1. `//?/c:` is not one. const uncHost = (p) => { if (p[0] !== '' || p[1] !== '') return -1; const h = p[2] !== '?' ? 2 : isLongUNC(p[3]) ? 4 : -1; const host = p[h]; const share = p[h + 1]; return (h !== -1 && host !== undefined && host !== '' && host !== exports.GLOBSTAR && share !== undefined && share !== '' && share !== exports.GLOBSTAR) ? h : -1; }; // true if ** can walk the path portion: not `.` or `..`, and not a // dotfile unless `dot` is set const walkable = (p, dot) => !isDots(p) && (dot || !p.startsWith('.')); // True if a portion of the path from `start` to `end` can not be // walked by **: one that starts with `.`, or with `dot`, one that is // `.` or `..`. `start` is the start of a portion, and `end` is the end // of the path, or just after a separator. const dotPortion = (f, start, end, dot, sep) => { for (let k = start; k < end;) { if (f.charCodeAt(k) === 46) { if (!dot) return true; const n = f.charCodeAt(k + 1) === 46 ? k + 2 : k + 1; if (n >= end || isSep(f.charCodeAt(n), sep)) return true; } // the start of the next portion, after any run of separators const s = nextSep(f, k, sep); if (s === -1) return false; k = s + 1; while (k < end && isSep(f.charCodeAt(k), sep)) k++; } return false; }; const isSep = (c, sep) => sep === '/' ? c === 47 : sep === '\\' ? c === 92 : c === 47 || c === 92; // the index of the first separator at or after `from`, or -1 const nextSep = (f, from, sep) => { if (sep !== 'both') return f.indexOf(sep, from); const s = f.indexOf('/', from); const b = f.indexOf('\\', from); return (s === -1 ? b : b === -1 ? s : Math.min(s, b)); }; // the index of the last separator at or before `from`, or -1 const lastSep = (f, from, sep) => sep !== 'both' ? f.lastIndexOf(sep, from) : Math.max(f.lastIndexOf('/', from), f.lastIndexOf('\\', from)); // What parse() returns in place of a Matcher while make() builds and // merges the rows. Most of the Matchers in rows that are merged would // never be used, so they are only made for the rows that are left. const PENDING = {}; class Minimatch { options; set; pattern; windowsPathsNoEscape; nonegate; negate; comment; empty; preserveMultipleSlashes; partial; globSet; globParts; nocase; isWindows; platform; windowsNoMagicRoot; maxGlobstarRecursion; // the path can be checked against the last portion, and the string // portions, of each row before it is split. See #canMatch. #endCheck; #literalCheck; // level 2 resolves `.` and `..` portions in the path #level2; // the unicode normalization form, if any #form; // the string portions of each row in the set #literals = []; // the rows that #canMatch can match without splitting the path #shapes = []; // the rows whose last portion can be checked against the path's #endRows = []; // brace expansion can make the same portion in many rows, so they // share one Matcher, and the states that it caches #matchers = new Map(); // parse() returns PENDING in place of a Matcher #pending = false; // the GlobstarParts of each pattern row, by start index and partial #globstarParts = new WeakMap(); constructor(pattern, options = {}) { (0, assert_valid_pattern_js_1.assertValidPattern)(pattern); options = options || {}; this.options = options; this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200; this.pattern = pattern; this.platform = options.platform || defaultPlatform; this.isWindows = this.platform === 'win32'; // avoid the annoying deprecation flag lol const awe = ('allowWindow' + 'sEscape'); this.windowsPathsNoEscape = !!options.windowsPathsNoEscape || options[awe] === false; if (this.windowsPathsNoEscape) { this.pattern = this.pattern.replace(/\\/g, '/'); } this.preserveMultipleSlashes = !!options.preserveMultipleSlashes; this.negate = false; this.nonegate = !!options.nonegate; this.comment = false; this.empty = false; this.partial = !!options.partial; this.nocase = !!this.options.nocase; this.windowsNoMagicRoot = options.windowsNoMagicRoot !== undefined ? options.windowsNoMagicRoot : !!(this.isWindows && this.nocase); this.globSet = []; this.globParts = []; this.set = []; this.#endCheck = !this.preserveMultipleSlashes; this.#level2 = (options.optimizationLevel ?? 1) >= 2; const n = options.normalize; this.#form = n === true ? 'NFC' : n || undefined; // make the set of Matchers etc. this.make(); // On Windows, the portions of a row up to its drive letter or UNC // share are skipped or compared without case, if the path has one // too. So the checks only use the portions after it. const drives = this.set.map(row => this.#rootIndex(row)); this.#endRows = this.set.map((row, r) => this.#endCheck && row.length - 1 > drives[r]); // with the end check, the last portion is already checked this.#literals = this.set.map((row, r) => { const end = this.#endRows[r] ? row.length - 1 : row.length; const lits = row .slice(drives[r] + 1, end) .filter((p) => typeof p === 'string' && !!p); return [...new Set(lits)]; }); this.#literalCheck = this.#literals.some(l => l.length); this.#shapes = this.set.map((row, r) => { const [p0, p1, p2] = row; if (this.#endRows[r] && row.length === 2) { if (p0 === exports.GLOBSTAR && p1 !== exports.GLOBSTAR) return 'tail'; } // the basename of a path that is split on / is its last portion, // or the one before a trailing /, the same as the end check if (this.#endRows[r] && options.matchBase && row.length === 1 && p0 !== exports.GLOBSTAR && p0 !== '') { return 'base'; } // the portion must not be a root, or '', which can be a // different portion on Windows (where //x paths get an extra '') if (!this.#endCheck || drives[r] !== -1) return undefined; if (row.length === 3 && p0 === exports.GLOBSTAR && p2 === exports.GLOBSTAR && p1 !== exports.GLOBSTAR && p1 !== '' && // the portion between is only matched below this limit this.maxGlobstarRecursion > 0) { return 'mid'; } if (row.length === 2 && p1 === exports.GLOBSTAR && p0 !== exports.GLOBSTAR && p0 !== '') { return 'head'; } return undefined; }); } // a string portion of a pattern, normalized if the option is set #norm(p) { return this.#form ? p.normalize(this.#form) : p; } // On Windows, the index of the last root portion of a row, as // #matchRow finds it: 0 for `c:/...`, 3 for `//?/c:/...` and // `//host/share/...`, and 5 for `//?/UNC/host/share/...`. Otherwise // -1. #rootIndex(row) { if (!this.isWindows) return -1; if (isDrive(row[0])) return 0; const uncDrive = row[0] === '' && row[1] === '' && row[2] === '?' && isDrive(row[3]); if (uncDrive) return 3; const h = uncHost(row); return h === -1 ? -1 : h + 1; } hasMagic() { if (this.options.magicalBraces && this.set.length > 1) { return true; } for (const pattern of this.set) { for (const part of pattern) { if (typeof part !== 'string') return true; } } return false; } make() { const pattern = this.pattern; const options = this.options; // empty patterns and comments match nothing. if (!options.nocomment && pattern.charAt(0) === '#') { this.comment = true; return; } if (!pattern) { this.empty = true; return; } // step 1: figure out negation, etc. this.parseNegate(); // step 2: expand braces this.globSet = [...new Set(this.braceExpand())]; // step 3: now we have a set, so turn each one into a series of // path-portion matching patterns. // These will be Matchers, except in the case of "**", which is // set to the GLOBSTAR object for globstar behavior, // and will not contain any / characters // // First, we preprocess to make the glob pattern sets a bit simpler // and deduped. There are some perf-killing patterns that can cause // problems with a glob walk, but we can simplify them down a bit. const rawGlobParts = this.globSet.map(s => this.#slashSplit(s)); this.globParts = this.preprocess(rawGlobParts); // glob --> Matchers this.#pending = true; let set = this.globParts.map((s, _, __) => { if (this.isWindows && this.windowsNoMagicRoot) { // check if it's a drive or unc path. The root of a long UNC // path, //?/UNC/host/share, is 6 portions. const n = s[2] === '?' && isLongUNC(s[3]) ? 6 : 4; const isUNC = s[0] === '' && s[1] === '' && (s[2] === '?' || !globMagic.test(s[2])) && s.slice(3, n).every(p => !globMagic.test(p)); const isDrive = /^[a-z]:/i.test(s[0]); if (isUNC) { return [ ...s.slice(0, n).map(p => this.#norm(p)), ...s.slice(n).map(ss => this.parse(ss)), ]; } else if (isDrive) { return [ this.#norm(s[0]), ...s.slice(1).map(ss => this.parse(ss)), ]; } } return s.map(ss => this.parse(ss)); }); // filter out everything that didn't compile properly. this.set = set.filter(s => s.indexOf(false) === -1); // do not treat the ? in UNC paths as magic if (this.isWindows) { for (let i = 0; i < this.set.length; i++) { const p = this.set[i]; if (p[0] === '' && p[1] === '' && this.globParts[i][2] === '?' && (isDrive(p[3]) || isLongUNC(this.globParts[i][3]))) { if (isLongUNC(this.globParts[i][3])) p[3] = 'UNC'; p[2] = '?'; } } } // with noext, `@(...)` would be literal if (!options.noext) this.mergeRows(); this.#pending = false; this.set.forEach((row, i) => { for (let k = 0; k < row.length; k++) { if (row[k] === PENDING) { row[k] = this.parse(this.globParts[i][k]); } } }); } // Merge rows that only differ in one portion, where each of them // is a Matcher, into one row that matches any of them with // `@(a|b|...)`. Then a path is only walked once for all of them, // for example with `**/*.{js,ts}`. mergeRows() { let merged = true; while (merged) { merged = false; const len = Math.max(0, ...this.set.map(r => r.length)); for (let k = 0; k < len; k++) { // the rows that are the same other than at portion k const groups = new Map(); this.set.forEach((row, i) => { if (!(row[k] instanceof nonrecursive_glob_match_1.Matcher || row[k] === PENDING) || !extSafe(this.globParts[i][k])) { return; } const parts = [...this.globParts[i]]; parts[k] = ''; const key = `${row.length}/${parts.join('/')}`; const g = groups.get(key); if (g) g.push(i); else groups.set(key, [i]); }); const drop = new Set(); for (const g of groups.values()) { if (g.length < 2) continue; const [first, ...rest] = g; const alts = g.map(i => this.globParts[i][k]); const glob = `@(${alts.join('|')})`; this.globParts[first][k] = glob; this.set[first][k] = this.parse(glob); for (const i of rest) drop.add(i); } if (drop.size) { merged = true; this.set = this.set.filter((_, i) => !drop.has(i)); this.globParts = this.globParts.filter((_, i) => !drop.has(i)); } } } } // various transforms to equivalent pattern sets that are // faster to process in a filesystem walk. The goal is to // eliminate what we can, and push all ** patterns as far // to the right as possible, even if it increases the number // of patterns that we have to process. preprocess(globParts) { // if we're not in globstar mode, then turn ** into * if (this.options.noglobstar) { for (const partset of globParts) { for (let j = 0; j < partset.length; j++) { if (partset[j] === '**') { partset[j] = '*'; } } } } const { optimizationLevel = 1 } = this.options; if (optimizationLevel >= 2) { // aggressive optimization for the purpose of fs walking globParts = this.firstPhasePreProcess(globParts); globParts = this.secondPhasePreProcess(globParts); } else if (optimizationLevel >= 1) { // just basic optimizations to remove some .. parts globParts = this.levelOneOptimize(globParts); } else { // just collapse multiple ** portions into one globParts = this.adjascentGlobstarOptimize(globParts); } return globParts; } // just get rid of adjascent ** portions adjascentGlobstarOptimize(globParts) { return globParts.map(parts => { let gs = -1; while (-1 !== (gs = parts.indexOf('**', gs + 1))) { let i = gs; while (parts[i + 1] === '**') { i++; } if (i !== gs) { parts.splice(gs, i - gs); } } return parts; }); } // get rid of adjascent ** and resolve .. portions levelOneOptimize(globParts) { return globParts.map(parts => { // .. can not remove the root of a Windows path const root = this.#rootIndex(parts) + 1; parts = parts.reduce((set, part) => { const prev = set[set.length - 1]; if (part === '**' && prev === '**') { return set; } if (part === '..') { if (set.length > root && prev && prev !== '..' && prev !== '.' && prev !== '**') { set.pop(); return set; } } set.push(part); return set; }, []); return parts.length === 0 ? [''] : parts; }); } // Check the path against each row before it is split. 'no' if no // row can match, so it does not need to be split. 'yes' if a row of // `**` and one more portion matches. 'maybe' if it must be split to // know. None of this applies with partial, where the path can end // before the row does. // // On Windows, `\` is a separator in the path too, and the portions of // a row up to its drive letter or UNC share are not checked (see // #rootIndex). // // Each string portion of a row must be a portion of the path, so the // path must contain it. Level 2 can only take portions out of the // path, so this is still true then. // // The last portion of a row must match the last portion of the path, // or the one before a trailing `/` (as a/* and a/**/* match a/b/). // This does not apply to rows that end in **, rows that end in a // root, or preserveMultipleSlashes (which can leave more // than one '' at the end). At level 2, it does not apply if that // portion is `.` or `..`, which can be // resolved away. Level 2 only takes other portions out of the // middle of the path. // // A row of `**/x` then matches if the ** can walk the portions // before the one that x matches, the same as #matchGlobstar. At // level 2, this is only known if none of them are `.` or `..`, so // that the path does not change when it is resolved. #canMatch(f) { const sep = !this.isWindows || !f.includes('\\') ? '/' : f.includes('/') ? 'both' : '\\'; // the portions that f.split(/\/+/) would end with, and where they // start in f, found when a row needs them. `ends` is 0 until they // are found, then 1, or -1 if they can not be used. let ends = this.#endCheck ? 0 : -1; let last = ''; let lastStart = 0; let prev; let prevStart = 0; const dot = !!this.options.dot; // A string compare is cheaper than includes(), and a Matcher test // costs more, so the cheaper check goes first. ROWS: for (let r = 0; r < this.set.length; r++) { const row = this.set[r]; const p = row[row.length - 1]; let end = ends !== -1 && this.#endRows[r] && p !== exports.GLOBSTAR; if (end && ends === 0) { const i = lastSep(f, f.length, sep); lastStart = i + 1; last = f.slice(lastStart); if (last === '' && i >= 0) { let j = i; while (j > 0 && isSep(f.charCodeAt(j - 1), sep)) j--; prevStart = j === 0 ? 0 : lastSep(f, j - 1, sep) + 1; prev = f.slice(prevStart, j); } const resolves = this.#level2 && (isDots(last) || (last === '' && prev !== undefined && isDots(prev))); ends = resolves ? -1 : 1; end = !resolves; } let tailStart = -1; if (end && typeof p === 'string') { tailStart = endIndex(p, last, lastStart, prev, prevStart); if (tailStart === -1) continue; } if (this.#literalCheck) { for (const l of this.#literals[r]) { if (!f.includes(l)) continue ROWS; } } if (end && typeof p === 'object') { tailStart = endIndex(p, last, lastStart, prev, prevStart); if (tailStart === -1) continue; } const shape = this.#shapes[r]; if (shape === 'base' && end) return 'yes'; if (shape === 'tail' && end) { if (this.#level2 && dotPortion(f, 0, tailStart, true, sep)) { return 'maybe'; } if (!dotPortion(f, 0, tailStart, dot, sep)) return 'yes'; continue; } if (shape === 'mid' || shape === 'head') { const m = this.#walk(f, row, shape === 'head', dot, sep); if (m !== 'no') return m; continue; } return 'maybe'; } return 'no'; } // Match a row of a known shape against the split path, the same as // #matchGlobstar and #matchGlobStarBodySections do. None of these // rows have a drive letter, so they start at the first portion. #matchShape(file, row, shape) { const dot = !!this.options.dot; const n = file.length; if (shape === 'head') { // x, then at least one portion that ** can walk if (n < 2 || !portionMatch(row[0], file[0])) { return false; } for (let i = 1; i < n; i++) if (!walkable(file[i], dot)) return false; return true; } const x = row[1]; if (shape === 'tail') { // x is the last portion, or the one before a trailing '' let t = n - 1; if (!portionMatch(x, file[t])) { if (file[t] !== '' || n === 1 || !portionMatch(x, file[--t])) { return false; } } for (let i = 0; i < t; i++) if (!walkable(file[i], dot)) return false; return true; } // mid: the first x that ** can walk to, with a portion after it, // and ** can walk all of those for (let k = 0; k < n; k++) { if (portionMatch(x, file[k]) && k + 1 < n) { let i = k + 1; while (i < n && walkable(file[i], dot)) i++; if (i === n) return true; } if (!walkable(file[k], dot)) return false; } return false; } // Match a row of `**/x/**` or `x/**` against the path, the same as // #matchGlobstar and #matchGlobStarBodySections do once it is split. // x must match a portion (the first one, for `x/**`), the ** before // it must be able to walk the portions before that one, and there // must be a portion after it, which the ** after it can walk. At // level 2, this is only known if no portion is `.` or `..`. #walk(f, row, head, dot, sep) { if (this.#level2 && dotPortion(f, 0, f.length, true, sep)) return 'maybe'; const x = row[head ? 0 : 1]; // the last portion has none after it, so it can not be x let s = 0; for (let e = nextSep(f, s, sep); e !== -1; e = nextSep(f, s, sep)) { // the start of the next portion let next = e + 1; while (next < f.length && isSep(f.charCodeAt(next), sep)) next++; const hit = typeof x === 'string' ? e - s === x.length && f.startsWith(x, s) : e > s && x.testUnchecked(f.slice(s, e)); if (hit && !dotPortion(f, next, f.length, dot, sep)) return 'yes'; if (head || dotPortion(f, s, next, dot, sep)) return 'no'; s = next; } return 'no'; } // the last split, if it was the same path with the same split // settings #cachedSplit(f) { const last = lastSplit; return (last?.f === f && last.isWindows === this.isWindows && last.preserveMultipleSlashes === this.preserveMultipleSlashes && last.normalize === this.#form) ? last : undefined; } // split a path for match(), and keep the parts for the next call #split(f) { const { isWindows, preserveMultipleSlashes } = this; // windows: need to use /, not \ let parts = this.#slashSplit(isWindows ? f.split('\\').join('/') : f); // normalize each portion, so that a char that normalizes to `/` // does not make a new portion const normalize = this.#form; if (normalize) parts = parts.map(p => p.normalize(normalize)); lastSplit = { f, isWindows, preserveMultipleSlashes, normalize, parts, }; return lastSplit; } // resolve `.` and `..` portions of a path, for level 2 #levelTwoFileOptimize(file) { // work on a copy, so that the cached parts do not change const parts = [...file]; let didSomething = false; do { didSomething = false; //
// -> 
/
            if (!this.preserveMultipleSlashes) {
                for (let i = 1; i < parts.length - 1; i++) {
                    const p = parts[i];
                    // don't squeeze out UNC patterns
                    if (i === 1 && p === '' && parts[0] === '')
                        continue;
                    if (p === '.' || p === '') {
                        didSomething = true;
                        parts.splice(i, 1);
                        i--;
                    }
                }
                if (parts[0] === '.' &&
                    parts.length === 2 &&
                    (parts[1] === '.' || parts[1] === '')) {
                    didSomething = true;
                    parts.pop();
                }
            }
            // 
/

/../ ->

/
            // .. can not remove the root of a Windows path
            const root = this.#rootIndex(parts) + 1;
            let dd = root;
            while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
                const p = parts[dd - 1];
                if (p && p !== '.' && p !== '..' && p !== '**') {
                    didSomething = true;
                    parts.splice(dd - 1, 2);
                    dd = Math.max(dd - 2, root);
                }
            }
        } while (didSomething);
        return parts.length === 0 ? [''] : parts;
    }
    // First phase: single-pattern processing
    // 
 is 1 or more portions
    //  is 1 or more portions
    // 

is any portion other than ., .., '', or ** // is . or '' // // **/.. is *brutal* for filesystem walking performance, because // it effectively resets the recursive walk each time it occurs, // and ** cannot be reduced out by a .. pattern part like a regexp // or most strings (other than .., ., and '') can be. // //

/**/../

/

/ -> {

/../

/

/,

/**/

/

/} //

// -> 
/
    // 
/

/../ ->

/
    // **/**/ -> **/
    //
    // **/*/ -> */**/ <== not valid because ** doesn't follow
    // this WOULD be allowed if ** did follow symlinks, or * didn't
    firstPhasePreProcess(globParts) {
        // each **/.. split doubles the patterns, so stop at the brace max
        const { braceExpandMax = 100_000 } = this.options;
        let didSomething = false;
        do {
            didSomething = false;
            // 
/**/../

/

/ -> {

/../

/

/,

/**/

/

/} for (let parts of globParts) { let gs = -1; while (-1 !== (gs = parts.indexOf('**', gs + 1))) { let gss = gs; while (parts[gss + 1] === '**') { //

/**/**/ -> 
/**/
                        gss++;
                    }
                    // eg, if gs is 2 and gss is 4, that means we have 3 **
                    // parts, and can remove 2 of them.
                    if (gss > gs) {
                        parts.splice(gs + 1, gss - gs);
                    }
                    let next = parts[gs + 1];
                    const p = parts[gs + 2];
                    const p2 = parts[gs + 3];
                    if (next !== '..')
                        continue;
                    if (!p ||
                        p === '.' ||
                        p === '..' ||
                        !p2 ||
                        p2 === '.' ||
                        p2 === '..' ||
                        globParts.length >= braceExpandMax) {
                        continue;
                    }
                    didSomething = true;
                    // edit parts in place, and push the new one
                    parts.splice(gs, 1);
                    const other = parts.slice(0);
                    other[gs] = '**';
                    globParts.push(other);
                    gs--;
                }
                // 
// -> 
/
                if (!this.preserveMultipleSlashes) {
                    for (let i = 1; i < parts.length - 1; i++) {
                        const p = parts[i];
                        // don't squeeze out UNC patterns
                        if (i === 1 && p === '' && parts[0] === '')
                            continue;
                        if (p === '.' || p === '') {
                            didSomething = true;
                            parts.splice(i, 1);
                            i--;
                        }
                    }
                    if (parts[0] === '.' &&
                        parts.length === 2 &&
                        (parts[1] === '.' || parts[1] === '')) {
                        didSomething = true;
                        parts.pop();
                    }
                }
                // 
/

/../ ->

/
                // .. can not remove the root of a Windows path
                const root = this.#rootIndex(parts) + 1;
                let dd = root;
                while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
                    const p = parts[dd - 1];
                    if (p && p !== '.' && p !== '..' && p !== '**') {
                        didSomething = true;
                        const needDot = dd === 1 && parts[dd + 1] === '**';
                        const splin = needDot ? ['.'] : [];
                        parts.splice(dd - 1, 2, ...splin);
                        if (parts.length === 0)
                            parts.push('');
                        dd = Math.max(dd - 2, root);
                    }
                }
            }
        } while (didSomething);
        return globParts;
    }
    // second phase: multi-pattern dedupes
    // {
/*/,
/

/} ->

/*/
    // {
/,
/} -> 
/
    // {
/**/,
/} -> 
/**/
    //
    // {
/**/,
/**/

/} ->

/**/
    // ^-- not valid because ** doens't follow symlinks
    secondPhasePreProcess(globParts) {
        // each pattern is compared to each other one, so skip this for
        // large sets. The patterns match the same paths either way.
        if (globParts.length > 1000)
            return globParts;
        for (let i = 0; i < globParts.length - 1; i++) {
            for (let j = i + 1; j < globParts.length; j++) {
                const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
                if (matched) {
                    globParts[i] = [];
                    globParts[j] = matched;
                    break;
                }
            }
        }
        return globParts.filter(gs => gs.length);
    }
    partsMatch(a, b, emptyGSMatch = false) {
        let ai = 0;
        let bi = 0;
        let result = [];
        let which = '';
        while (ai < a.length && bi < b.length) {
            if (a[ai] === b[bi]) {
                result.push(which === 'b' ? b[bi] : a[ai]);
                ai++;
                bi++;
            }
            else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
                result.push(a[ai]);
                ai++;
            }
            else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
                result.push(b[bi]);
                bi++;
            }
            else if (a[ai] === '*' &&
                b[bi] &&
                (this.options.dot || !b[bi].startsWith('.')) &&
                b[bi] !== '**') {
                if (which === 'b')
                    return false;
                which = 'a';
                result.push(a[ai]);
                ai++;
                bi++;
            }
            else if (b[bi] === '*' &&
                a[ai] &&
                (this.options.dot || !a[ai].startsWith('.')) &&
                a[ai] !== '**') {
                if (which === 'a')
                    return false;
                which = 'b';
                result.push(b[bi]);
                ai++;
                bi++;
            }
            else {
                return false;
            }
        }
        // if we fall out of the loop, it means they two are identical
        // as long as their lengths match
        return a.length === b.length && result;
    }
    parseNegate() {
        if (this.nonegate)
            return;
        const pattern = this.pattern;
        let negate = false;
        let negateOffset = 0;
        for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
            negate = !negate;
            negateOffset++;
        }
        if (negateOffset)
            this.pattern = pattern.slice(negateOffset);
        this.negate = negate;
    }
    // Match the parts of a path against one row of the set. `resolved`
    // is the parts after level 2 resolves `.` and `..` in them, or the
    // same parts below level 2. Set partial to true to test if, for
    // example, "/a/b" matches the start of "/*/b/*/d". Partial means, if
    // you run out of file before you run out of pattern, then that's
    // fine, as long as all the parts match.
    #matchRow(file, resolved, pattern, partial) {
        let fileStartIndex = 0;
        let patternStartIndex = 0;
        // UNC paths like //?/X:/... can match X:/... and vice versa, and
        // //?/UNC/host/share/... can match //host/share/... and vice versa.
        // Drive letters in absolute drive or unc paths, and the host and
        // share of UNC paths, are always compared case-insensitively.
        const fh = this.isWindows ? uncHost(resolved) : -1;
        const ph = fh === -1 ? -1 : uncHost(pattern);
        if (ph !== -1) {
            const host = pattern[ph];
            const share = pattern[ph + 1];
            if (typeof host !== 'string' || typeof share !== 'string') {
                // a host or share with magic is matched as usual
                fileStartIndex = fh;
                patternStartIndex = ph;
            }
            else if (resolved[fh].toLowerCase() === host.toLowerCase() &&
                resolved[fh + 1].toLowerCase() === share.toLowerCase()) {
                fileStartIndex = fh + 2;
                patternStartIndex = ph + 2;
            }
        }
        else if (this.isWindows) {
            const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
            const fileUNC = !fileDrive &&
                file[0] === '' &&
                file[1] === '' &&
                file[2] === '?' &&
                /^[a-z]:$/i.test(file[3]);
            const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
            const patternUNC = !patternDrive &&
                pattern[0] === '' &&
                pattern[1] === '' &&
                pattern[2] === '?' &&
                typeof pattern[3] === 'string' &&
                /^[a-z]:$/i.test(pattern[3]);
            const fdi = fileUNC ? 3
                : fileDrive ? 0
                    : undefined;
            const pdi = patternUNC ? 3
                : patternDrive ? 0
                    : undefined;
            if (typeof fdi === 'number' && typeof pdi === 'number') {
                const [fd, pd] = [
                    file[fdi],
                    pattern[pdi],
                ];
                // the drives match, so start matching after them
                if (fd.toLowerCase() === pd.toLowerCase()) {
                    patternStartIndex = pdi + 1;
                    fileStartIndex = fdi + 1;
                }
            }
        }
        // the drive letters are found in the parts before level 2 resolves
        // them, and the match uses the resolved parts
        file = resolved;
        if (pattern.includes(exports.GLOBSTAR)) {
            return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
        }
        return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
    }
    #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
        const { head, body, tail, segments } = this.#getGlobstarParts(pattern, partial, patternIndex);
        // check the head, from the current file/pattern index.
        if (head.length) {
            const headEnd = fileIndex + head.length;
            if (!this.#matchOne(file, head, partial, fileIndex, 0, headEnd)) {
                return false;
            }
            fileIndex += head.length;
            patternIndex += head.length;
        }
        // now we know the head matches!
        // if the last portion is not empty, it MUST match the end
        // check the tail
        let fileTailMatch = 0;
        if (tail.length) {
            // if head + tail > file, then we cannot possibly match
            if (tail.length + fileIndex > file.length)
                return false;
            // try to match the tail
            let tailStart = file.length - tail.length;
            if (this.#matchOne(file, tail, partial, tailStart, 0)) {
                fileTailMatch = tail.length;
            }
            else {
                // affordance for stuff like a/**/* matching a/b/
                // if the last file portion is '', and there's more to the pattern
                // then try without the '' bit.
                if (file[file.length - 1] !== '' ||
                    fileIndex + tail.length === file.length) {
                    return false;
                }
                tailStart--;
                if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
                    return false;
                }
                fileTailMatch = tail.length + 1;
            }
        }
        // now we know the tail matches!
        // the middle is zero or more portions wrapped in **, possibly
        // containing more ** sections.
        // so a/**/b/**/c/**/d has become **/b/**/c/**
        // if it's empty, it means a/**/b, just verify we have no bad dots
        // if there's no tail, so it ends on /**, then we must have *something*
        // after the head, or it's not a matc
        if (!body.length) {
            let sawSome = !!fileTailMatch;
            for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
                const f = String(file[i]);
                sawSome = true;
                if (f === '.' ||
                    f === '..' ||
                    (!this.options.dot && f.startsWith('.'))) {
                    return false;
                }
            }
            // in partial mode, we just need to get past all file parts
            return partial || sawSome;
        }
        // now we know that there's one or more body sections, which can
        // be matched anywhere from the 0 index (because the head was pruned)
        // through to the length-fileTailMatch index.
        return !!this.#matchGlobStarBodySections(file, segments, file.length - fileTailMatch, fileIndex, 0, partial, 0, !!fileTailMatch, new Set());
    }
    // Split the pattern into head, tail, and middle of ** delimited
    // parts. The tail has to be at the end, and the others just have to
    // be found in order from the head. These only depend on the pattern
    // row, so they are kept for the next path.
    #getGlobstarParts(pattern, partial, patternIndex) {
        let byStart = this.#globstarParts.get(pattern);
        if (!byStart) {
            byStart = [];
            this.#globstarParts.set(pattern, byStart);
        }
        const key = patternIndex * 2 + (partial ? 1 : 0);
        const found = byStart[key];
        if (found)
            return found;
        const firstgs = pattern.indexOf(exports.GLOBSTAR, patternIndex);
        const lastgs = pattern.lastIndexOf(exports.GLOBSTAR);
        const head = pattern.slice(patternIndex, firstgs);
        const body = pattern.slice(firstgs + 1, partial ? undefined : lastgs);
        const tail = partial ? [] : pattern.slice(lastgs + 1);
        // split the body up into sections, and note the number of
        // portions that must come after the start of each one
        const segments = [[[], 0]];
        let current = segments[0];
        let nonGsParts = 0;
        const nonGsPartsSums = [0];
        for (const b of body) {
            if (b === exports.GLOBSTAR) {
                nonGsPartsSums.push(nonGsParts);
                current = [[], 0];
                segments.push(current);
            }
            else {
                current[0].push(b);
                nonGsParts++;
            }
        }
        // the portions in this section and the ones after it
        segments.forEach((b, i) => {
            b[1] = nonGsParts - nonGsPartsSums[i];
        });
        const parts = { head, body, tail, segments };
        byStart[key] = parts;
        return parts;
    }
    // return false for "nope, not matching"
    // return null for "not matching, cannot keep trying"
    #matchGlobStarBodySections(file, 
    // pattern section, and the portions that it and the rest need
    bodySegments, 
    // the end of the file portions that the body can match
    fileLength, fileIndex, bodyIndex, partial, globStarDepth, sawTail, 
    // the places that a walk has already tried, as
    // bodyIndex * (file.length + 1) + fileIndex. A walk from a place
    // does the same no matter how it got there, and it only comes back
    // if it found no match, so a walk that gets to one stops.
    tried) {
        // take the first body segment, and walk from fileIndex to its "after"
        // value at the end
        // If it doesn't match at that position, we increment, until we hit
        // that final possible position, and give up.
        // If it does match, then advance and try to rest.
        // If any of them fail we keep walking forward.
        // this is still a bit recursively painful, but it's more constrained
        // than previous implementations, because we never test something that
        // can't possibly be a valid matching condition.
        const bs = bodySegments[bodyIndex];
        if (!bs) {
            // with partial, the body is the rest of the pattern, so it
            // matches if no portions are left, or only the '' of a trailing
            // / (as a/* matches a/b/). A ** at the end walks them as its own
            // empty section.
            if (partial) {
                return (fileIndex >= fileLength ||
                    (fileIndex === fileLength - 1 && file[fileIndex] === ''));
            }
            // just make sure that there's no bad dots before the tail
            for (let i = fileIndex; i < fileLength; i++) {
                sawTail = true;
                const f = file[i];
                if (f === '.' ||
                    f === '..' ||
                    (!this.options.dot && f.startsWith('.'))) {
                    return false;
                }
            }
            return sawTail;
        }
        // have a non-globstar body section to test, up to the last index
        // where it can start
        const [body, need] = bs;
        // with partial, the path can end before the body does
        const after = partial ? fileLength : fileLength - need;
        while (fileIndex <= after) {
            const key = bodyIndex * (file.length + 1) + fileIndex;
            if (tried.has(key))
                return false;
            tried.add(key);
            const m = this.#matchOne(file, body, partial, fileIndex, 0, fileIndex + body.length);
            // if limit exceeded, no match. intentional false negative,
            // acceptable break in correctness for security.
            if (m && globStarDepth < this.maxGlobstarRecursion) {
                // match! see if the rest match. if so, we're done!
                const sub = this.#matchGlobStarBodySections(file, bodySegments, fileLength, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail, tried);
                if (sub !== false) {
                    return sub;
                }
            }
            // the ** does not walk past the last place the body can start
            if (fileIndex === after)
                break;
            const f = file[fileIndex];
            if (f === '.' ||
                f === '..' ||
                (!this.options.dot && f.startsWith('.'))) {
                return false;
            }
            fileIndex++;
        }
        // walked off. no point continuing
        return partial || null;
    }
    #matchOne(file, pattern, partial, fileIndex, patternIndex, 
    // only match the file portions before this index
    fileEnd = file.length) {
        let fi;
        let pi;
        let pl;
        let fl;
        for (fi = fileIndex,
            pi = patternIndex,
            fl = Math.min(fileEnd, file.length),
            pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
            let p = pattern[pi];
            let f = file[fi];
            // should be impossible.
            /* c8 ignore start */
            if (p === false || p === exports.GLOBSTAR) {
                return false;
            }
            /* c8 ignore stop */
            // something other than **
            // non-magic patterns just have to match exactly
            // patterns with magic have been turned into Matchers.
            if (!portionMatch(p, f))
                return false;
        }
        // Note: ending in / means that we'll get a final ""
        // at the end of the pattern.  This can only match a
        // corresponding "" at the end of the file.
        // If the file ends in /, then it can only match a
        // a pattern that ends in /, unless the pattern just
        // doesn't have any more for it. But, a/b/ should *not*
        // match "a/b/*", even though * can match "", except in
        // partial mode, where it might simply not be reached yet.
        // However, a/b/ should still satisfy a/*
        // now either we fell off the end of the pattern, or we're done.
        if (fi === fl && pi === pl) {
            // ran out of pattern and filename at the same time.
            // an exact hit!
            return true;
        }
        else if (fi === fl) {
            // ran out of file, but still had pattern left.
            // this is ok if we're doing the match as part of
            // a glob fs traversal.
            return partial;
        }
        else if (pi === pl) {
            // ran out of pattern, still have file left.
            // this is only acceptable if we're on the very last
            // empty segment of a file with a trailing slash.
            // a/* should match a/b/
            return fi === fl - 1 && file[fi] === '';
            /* c8 ignore start */
        }
        else {
            // should be unreachable.
            throw new Error('wtf?');
        }
        /* c8 ignore stop */
    }
    braceExpand() {
        return (0, exports.braceExpand)(this.pattern, this.options);
    }
    parse(pattern) {
        (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
        const options = this.options;
        // shortcuts
        if (pattern === '**')
            return exports.GLOBSTAR;
        if (pattern === '')
            return '';
        const { nocase, nocaseMagicOnly, noext = false, dot, normalize, } = options;
        const lit = literal(pattern, noext);
        // in nocase mode, a literal with cased chars still needs a
        // Matcher, unless nocaseMagicOnly is set.
        if (lit !== undefined &&
            !(nocase &&
                !nocaseMagicOnly &&
                pattern.toUpperCase() !== pattern.toLowerCase())) {
            return this.#norm(lit);
        }
        if (this.#pending)
            return PENDING;
        let m = this.#matchers.get(pattern);
        if (!m) {
            m = new nonrecursive_glob_match_1.Matcher(pattern, { nocase, noext, dot, normalize });
            this.#matchers.set(pattern, m);
        }
        return m;
    }
    #slashSplit(p) {
        // if p starts with // on windows, we preserve that
        // so that UNC paths aren't broken.  Otherwise, any number of
        // / characters are coalesced into one, unless
        // preserveMultipleSlashes is set to true.
        if (this.preserveMultipleSlashes) {
            return p.split('/');
        }
        else if (this.isWindows && /^\/\/[^/]+/.test(p)) {
            // add an extra '' for the one we lose
            return ['', ...p.split(/\/+/)];
        }
        else {
            return p.split(/\/+/);
        }
    }
    match(f, partial = this.partial) {
        // short-circuit in the case of busted things.
        // comments, etc.
        if (this.comment) {
            return false;
        }
        if (this.empty) {
            return f === '';
        }
        if (f === '/' && partial) {
            return true;
        }
        const options = this.options;
        // treat the test path as a set of pathparts. If it was not just
        // split or checked, check that it can match before splitting it.
        let split = this.#cachedSplit(f);
        if (!split) {
            // the checks look at the path as given, so not if it is
            // normalized
            if (!partial &&
                !this.#form &&
                (this.#endCheck || this.#literalCheck) &&
                lastChecked !== f) {
                lastChecked = f;
                const can = this.#canMatch(f);
                if (can === 'no')
                    return options.flipNegate ? false : this.negate;
                if (can === 'yes')
                    return options.flipNegate ? true : !this.negate;
            }
            split = this.#split(f);
        }
        const ff = split.parts;
        // the parts after level 2 resolves them, kept for the next pattern
        // that tests the same path
        const resolved = this.#level2 ?
            (split.resolved ??= this.#levelTwoFileOptimize(ff))
            : ff;
        // just ONE of the pattern sets in this.set needs to match
        // in order for it to be valid.  If negating, then just one
        // match means that we have failed.
        // Either way, return on the first hit.
        const set = this.set;
        // Find the basename of the path by looking for the last non-empty segment
        let filename = ff[ff.length - 1];
        if (!filename) {
            for (let i = ff.length - 2; !filename && i >= 0; i--) {
                filename = ff[i];
            }
        }
        for (let r = 0; r < set.length; r++) {
            const pattern = set[r];
            const shape = partial ? undefined : this.#shapes[r];
            // level 2 does not change a path of one portion
            const hit = shape === 'base' ?
                portionMatch(pattern[0], filename)
                : shape ? this.#matchShape(resolved, pattern, shape)
                    : options.matchBase && pattern.length === 1 ?
                        this.#matchRow([filename], [filename], pattern, partial)
                        : this.#matchRow(ff, resolved, pattern, partial);
            if (hit) {
                if (options.flipNegate) {
                    return true;
                }
                return !this.negate;
            }
        }
        // didn't get any hits.  this is success if it's a negative
        // pattern, failure otherwise.
        if (options.flipNegate) {
            return false;
        }
        return this.negate;
    }
    static defaults(def) {
        return exports.minimatch.defaults(def).Minimatch;
    }
}
exports.Minimatch = Minimatch;
/* c8 ignore start */
var escape_js_2 = require("./escape.js");
Object.defineProperty(exports, "escape", { enumerable: true, get: function () { return escape_js_2.escape; } });
var unescape_js_2 = require("./unescape.js");
Object.defineProperty(exports, "unescape", { enumerable: true, get: function () { return unescape_js_2.unescape; } });
/* c8 ignore stop */
exports.minimatch.Minimatch = Minimatch;
exports.minimatch.escape = escape_js_1.escape;
exports.minimatch.unescape = unescape_js_1.unescape;
//# sourceMappingURL=index.js.map