Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
index.js1114 linesDownload Raw Back to esm
1import { expand } from 'brace-expansion';
2import { assertValidPattern } from './assert-valid-pattern.js';
3import { AST } from './ast.js';
4import { escape } from './escape.js';
5import { unescape } from './unescape.js';
6export 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.
15const starDotExtRE = /^\*+([^+@!?*[(]*)$/;
16const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
17const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
18const starDotExtTestNocase = (ext) => {
19    ext = ext.toLowerCase();
20    return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
21};
22const starDotExtTestNocaseDot = (ext) => {
23    ext = ext.toLowerCase();
24    return (f) => f.toLowerCase().endsWith(ext);
25};
26const starDotStarRE = /^\*+\.\*+$/;
27const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
28const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
29const dotStarRE = /^\.\*+$/;
30const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
31const starRE = /^\*+$/;
32const starTest = (f) => f.length !== 0 && !f.startsWith('.');
33const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
34const qmarksRE = /^\?+([^+@!?*[(]*)?$/;
35const 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};
42const 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};
49const qmarksTestDot = ([$0, ext = '']) => {
50    const noext = qmarksTestNoExtDot([$0]);
51    return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
52};
53const qmarksTest = ([$0, ext = '']) => {
54    const noext = qmarksTestNoExt([$0]);
55    return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
56};
57const qmarksTestNoExt = ([$0]) => {
58    const len = $0.length;
59    return (f) => f.length === len && !f.startsWith('.');
60};
61const qmarksTestNoExtDot = ([$0]) => {
62    const len = $0.length;
63    return (f) => f.length === len && f !== '.' && f !== '..';
64};
65/* c8 ignore start */
66const defaultPlatform = (typeof process === 'object' && process ?
67    (typeof process.env === 'object' &&
68        process.env &&
69        process.env.__MINIMATCH_TESTING_PLATFORM__) ||
70        process.platform
71    : 'posix');
72const path = {
73    win32: { sep: '\\' },
74    posix: { sep: '/' },
75};
76/* c8 ignore stop */
77export const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep;
78minimatch.sep = sep;
79export const GLOBSTAR = Symbol('globstar **');
80minimatch.GLOBSTAR = GLOBSTAR;
81// any single thing other than /
82// don't need to escape / when using new RegExp()
83const qmark = '[^/]';
84// * => any number of characters
85const 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.
89const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?';
90// not a ^ or / followed by a dot,
91// followed by anything, any number of times.
92const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?';
93export const filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
94minimatch.filter = filter;
95const ext = (a, b = {}) => Object.assign({}, a, b);
96export 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};
132minimatch.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
143export 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, { max: options.braceExpandMax });
152};
153minimatch.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.
165export const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
166minimatch.makeRe = makeRe;
167export 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};
175minimatch.match = match;
176// replace stuff like \* with *
177const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
178const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
179export class Minimatch {
180    options;
181    set;
182    pattern;
183    windowsPathsNoEscape;
184    nonegate;
185    negate;
186    comment;
187    empty;
188    preserveMultipleSlashes;
189    partial;
190    globSet;
191    globParts;
192    nocase;
193    isWindows;
194    platform;
195    windowsNoMagicRoot;
196    maxGlobstarRecursion;
197    regexp;
198    constructor(pattern, options = {}) {
199        assertValidPattern(pattern);
200        options = options || {};
201        this.options = options;
202        this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
203        this.pattern = pattern;
204        this.platform = options.platform || defaultPlatform;
205        this.isWindows = this.platform === 'win32';
206        // avoid the annoying deprecation flag lol
207        const awe = ('allowWindow' + 'sEscape');
208        this.windowsPathsNoEscape =
209            !!options.windowsPathsNoEscape || options[awe] === false;
210        if (this.windowsPathsNoEscape) {
211            this.pattern = this.pattern.replace(/\\/g, '/');
212        }
213        this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
214        this.regexp = null;
215        this.negate = false;
216        this.nonegate = !!options.nonegate;
217        this.comment = false;
218        this.empty = false;
219        this.partial = !!options.partial;
220        this.nocase = !!this.options.nocase;
221        this.windowsNoMagicRoot =
222            options.windowsNoMagicRoot !== undefined ?
223                options.windowsNoMagicRoot
224                : !!(this.isWindows && this.nocase);
225        this.globSet = [];
226        this.globParts = [];
227        this.set = [];
228        // make the set of regexps etc.
229        this.make();
230    }
231    hasMagic() {
232        if (this.options.magicalBraces && this.set.length > 1) {
233            return true;
234        }
235        for (const pattern of this.set) {
236            for (const part of pattern) {
237                if (typeof part !== 'string')
238                    return true;
239            }
240        }
241        return false;
242    }
243    debug(..._) { }
244    make() {
245        const pattern = this.pattern;
246        const options = this.options;
247        // empty patterns and comments match nothing.
248        if (!options.nocomment && pattern.charAt(0) === '#') {
249            this.comment = true;
250            return;
251        }
252        if (!pattern) {
253            this.empty = true;
254            return;
255        }
256        // step 1: figure out negation, etc.
257        this.parseNegate();
258        // step 2: expand braces
259        this.globSet = [...new Set(this.braceExpand())];
260        if (options.debug) {
261            //oxlint-disable-next-line no-console
262            this.debug = (...args) => console.error(...args);
263        }
264        this.debug(this.pattern, this.globSet);
265        // step 3: now we have a set, so turn each one into a series of
266        // path-portion matching patterns.
267        // These will be regexps, except in the case of "**", which is
268        // set to the GLOBSTAR object for globstar behavior,
269        // and will not contain any / characters
270        //
271        // First, we preprocess to make the glob pattern sets a bit simpler
272        // and deduped.  There are some perf-killing patterns that can cause
273        // problems with a glob walk, but we can simplify them down a bit.
274        const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
275        this.globParts = this.preprocess(rawGlobParts);
276        this.debug(this.pattern, this.globParts);
277        // glob --> regexps
278        let set = this.globParts.map((s, _, __) => {
279            if (this.isWindows && this.windowsNoMagicRoot) {
280                // check if it's a drive or unc path.
281                const isUNC = s[0] === '' &&
282                    s[1] === '' &&
283                    (s[2] === '?' || !globMagic.test(s[2])) &&
284                    !globMagic.test(s[3]);
285                const isDrive = /^[a-z]:/i.test(s[0]);
286                if (isUNC) {
287                    return [
288                        ...s.slice(0, 4),
289                        ...s.slice(4).map(ss => this.parse(ss)),
290                    ];
291                }
292                else if (isDrive) {
293                    return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
294                }
295            }
296            return s.map(ss => this.parse(ss));
297        });
298        this.debug(this.pattern, set);
299        // filter out everything that didn't compile properly.
300        this.set = set.filter(s => s.indexOf(false) === -1);
301        // do not treat the ? in UNC paths as magic
302        if (this.isWindows) {
303            for (let i = 0; i < this.set.length; i++) {
304                const p = this.set[i];
305                if (p[0] === '' &&
306                    p[1] === '' &&
307                    this.globParts[i][2] === '?' &&
308                    typeof p[3] === 'string' &&
309                    /^[a-z]:$/i.test(p[3])) {
310                    p[2] = '?';
311                }
312            }
313        }
314        this.debug(this.pattern, this.set);
315    }
316    // various transforms to equivalent pattern sets that are
317    // faster to process in a filesystem walk.  The goal is to
318    // eliminate what we can, and push all ** patterns as far
319    // to the right as possible, even if it increases the number
320    // of patterns that we have to process.
321    preprocess(globParts) {
322        // if we're not in globstar mode, then turn ** into *
323        if (this.options.noglobstar) {
324            for (const partset of globParts) {
325                for (let j = 0; j < partset.length; j++) {
326                    if (partset[j] === '**') {
327                        partset[j] = '*';
328                    }
329                }
330            }
331        }
332        const { optimizationLevel = 1 } = this.options;
333        if (optimizationLevel >= 2) {
334            // aggressive optimization for the purpose of fs walking
335            globParts = this.firstPhasePreProcess(globParts);
336            globParts = this.secondPhasePreProcess(globParts);
337        }
338        else if (optimizationLevel >= 1) {
339            // just basic optimizations to remove some .. parts
340            globParts = this.levelOneOptimize(globParts);
341        }
342        else {
343            // just collapse multiple ** portions into one
344            globParts = this.adjascentGlobstarOptimize(globParts);
345        }
346        return globParts;
347    }
348    // just get rid of adjascent ** portions
349    adjascentGlobstarOptimize(globParts) {
350        return globParts.map(parts => {
351            let gs = -1;
352            while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
353                let i = gs;
354                while (parts[i + 1] === '**') {
355                    i++;
356                }
357                if (i !== gs) {
358                    parts.splice(gs, i - gs);
359                }
360            }
361            return parts;
362        });
363    }
364    // get rid of adjascent ** and resolve .. portions
365    levelOneOptimize(globParts) {
366        return globParts.map(parts => {
367            parts = parts.reduce((set, part) => {
368                const prev = set[set.length - 1];
369                if (part === '**' && prev === '**') {
370                    return set;
371                }
372                if (part === '..') {
373                    if (prev && prev !== '..' && prev !== '.' && prev !== '**') {
374                        set.pop();
375                        return set;
376                    }
377                }
378                set.push(part);
379                return set;
380            }, []);
381            return parts.length === 0 ? [''] : parts;
382        });
383    }
384    levelTwoFileOptimize(parts) {
385        if (!Array.isArray(parts)) {
386            parts = this.slashSplit(parts);
387        }
388        let didSomething = false;
389        do {
390            didSomething = false;
391            // <pre>/<e>/<rest> -> <pre>/<rest>
392            if (!this.preserveMultipleSlashes) {
393                for (let i = 1; i < parts.length - 1; i++) {
394                    const p = parts[i];
395                    // don't squeeze out UNC patterns
396                    if (i === 1 && p === '' && parts[0] === '')
397                        continue;
398                    if (p === '.' || p === '') {
399                        didSomething = true;
400                        parts.splice(i, 1);
401                        i--;
402                    }
403                }
404                if (parts[0] === '.' &&
405                    parts.length === 2 &&
406                    (parts[1] === '.' || parts[1] === '')) {
407                    didSomething = true;
408                    parts.pop();
409                }
410            }
411            // <pre>/<p>/../<rest> -> <pre>/<rest>
412            let dd = 0;
413            while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
414                const p = parts[dd - 1];
415                if (p &&
416                    p !== '.' &&
417                    p !== '..' &&
418                    p !== '**' &&
419                    !(this.isWindows && /^[a-z]:$/i.test(p))) {
420                    didSomething = true;
421                    parts.splice(dd - 1, 2);
422                    dd -= 2;
423                }
424            }
425        } while (didSomething);
426        return parts.length === 0 ? [''] : parts;
427    }
428    // First phase: single-pattern processing
429    // <pre> is 1 or more portions
430    // <rest> is 1 or more portions
431    // <p> is any portion other than ., .., '', or **
432    // <e> is . or ''
433    //
434    // **/.. is *brutal* for filesystem walking performance, because
435    // it effectively resets the recursive walk each time it occurs,
436    // and ** cannot be reduced out by a .. pattern part like a regexp
437    // or most strings (other than .., ., and '') can be.
438    //
439    // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
440    // <pre>/<e>/<rest> -> <pre>/<rest>
441    // <pre>/<p>/../<rest> -> <pre>/<rest>
442    // **/**/<rest> -> **/<rest>
443    //
444    // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
445    // this WOULD be allowed if ** did follow symlinks, or * didn't
446    firstPhasePreProcess(globParts) {
447        let didSomething = false;
448        do {
449            didSomething = false;
450            // <pre>/**/../<p>/<p>/<rest> -> {<pre>/../<p>/<p>/<rest>,<pre>/**/<p>/<p>/<rest>}
451            for (let parts of globParts) {
452                let gs = -1;
453                while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
454                    let gss = gs;
455                    while (parts[gss + 1] === '**') {
456                        // <pre>/**/**/<rest> -> <pre>/**/<rest>
457                        gss++;
458                    }
459                    // eg, if gs is 2 and gss is 4, that means we have 3 **
460                    // parts, and can remove 2 of them.
461                    if (gss > gs) {
462                        parts.splice(gs + 1, gss - gs);
463                    }
464                    let next = parts[gs + 1];
465                    const p = parts[gs + 2];
466                    const p2 = parts[gs + 3];
467                    if (next !== '..')
468                        continue;
469                    if (!p ||
470                        p === '.' ||
471                        p === '..' ||
472                        !p2 ||
473                        p2 === '.' ||
474                        p2 === '..') {
475                        continue;
476                    }
477                    didSomething = true;
478                    // edit parts in place, and push the new one
479                    parts.splice(gs, 1);
480                    const other = parts.slice(0);
481                    other[gs] = '**';
482                    globParts.push(other);
483                    gs--;
484                }
485                // <pre>/<e>/<rest> -> <pre>/<rest>
486                if (!this.preserveMultipleSlashes) {
487                    for (let i = 1; i < parts.length - 1; i++) {
488                        const p = parts[i];
489                        // don't squeeze out UNC patterns
490                        if (i === 1 && p === '' && parts[0] === '')
491                            continue;
492                        if (p === '.' || p === '') {
493                            didSomething = true;
494                            parts.splice(i, 1);
495                            i--;
496                        }
497                    }
498                    if (parts[0] === '.' &&
499                        parts.length === 2 &&
500                        (parts[1] === '.' || parts[1] === '')) {
501                        didSomething = true;
502                        parts.pop();
503                    }
504                }
505                // <pre>/<p>/../<rest> -> <pre>/<rest>
506                let dd = 0;
507                while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
508                    const p = parts[dd - 1];
509                    if (p && p !== '.' && p !== '..' && p !== '**') {
510                        didSomething = true;
511                        const needDot = dd === 1 && parts[dd + 1] === '**';
512                        const splin = needDot ? ['.'] : [];
513                        parts.splice(dd - 1, 2, ...splin);
514                        if (parts.length === 0)
515                            parts.push('');
516                        dd -= 2;
517                    }
518                }
519            }
520        } while (didSomething);
521        return globParts;
522    }
523    // second phase: multi-pattern dedupes
524    // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
525    // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
526    // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
527    //
528    // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
529    // ^-- not valid because ** doens't follow symlinks
530    secondPhasePreProcess(globParts) {
531        for (let i = 0; i < globParts.length - 1; i++) {
532            for (let j = i + 1; j < globParts.length; j++) {
533                const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
534                if (matched) {
535                    globParts[i] = [];
536                    globParts[j] = matched;
537                    break;
538                }
539            }
540        }
541        return globParts.filter(gs => gs.length);
542    }
543    partsMatch(a, b, emptyGSMatch = false) {
544        let ai = 0;
545        let bi = 0;
546        let result = [];
547        let which = '';
548        while (ai < a.length && bi < b.length) {
549            if (a[ai] === b[bi]) {
550                result.push(which === 'b' ? b[bi] : a[ai]);
551                ai++;
552                bi++;
553            }
554            else if (emptyGSMatch && a[ai] === '**' && b[bi] === a[ai + 1]) {
555                result.push(a[ai]);
556                ai++;
557            }
558            else if (emptyGSMatch && b[bi] === '**' && a[ai] === b[bi + 1]) {
559                result.push(b[bi]);
560                bi++;
561            }
562            else if (a[ai] === '*' &&
563                b[bi] &&
564                (this.options.dot || !b[bi].startsWith('.')) &&
565                b[bi] !== '**') {
566                if (which === 'b')
567                    return false;
568                which = 'a';
569                result.push(a[ai]);
570                ai++;
571                bi++;
572            }
573            else if (b[bi] === '*' &&
574                a[ai] &&
575                (this.options.dot || !a[ai].startsWith('.')) &&
576                a[ai] !== '**') {
577                if (which === 'a')
578                    return false;
579                which = 'b';
580                result.push(b[bi]);
581                ai++;
582                bi++;
583            }
584            else {
585                return false;
586            }
587        }
588        // if we fall out of the loop, it means they two are identical
589        // as long as their lengths match
590        return a.length === b.length && result;
591    }
592    parseNegate() {
593        if (this.nonegate)
594            return;
595        const pattern = this.pattern;
596        let negate = false;
597        let negateOffset = 0;
598        for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
599            negate = !negate;
600            negateOffset++;
601        }
602        if (negateOffset)
603            this.pattern = pattern.slice(negateOffset);
604        this.negate = negate;
605    }
606    // set partial to true to test if, for example,
607    // "/a/b" matches the start of "/*/b/*/d"
608    // Partial means, if you run out of file before you run
609    // out of pattern, then that's fine, as long as all
610    // the parts match.
611    matchOne(file, pattern, partial = false) {
612        let fileStartIndex = 0;
613        let patternStartIndex = 0;
614        // UNC paths like //?/X:/... can match X:/... and vice versa
615        // Drive letters in absolute drive or unc paths are always compared
616        // case-insensitively.
617        if (this.isWindows) {
618            const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
619            const fileUNC = !fileDrive &&
620                file[0] === '' &&
621                file[1] === '' &&
622                file[2] === '?' &&
623                /^[a-z]:$/i.test(file[3]);
624            const patternDrive = typeof pattern[0] === 'string' && /^[a-z]:$/i.test(pattern[0]);
625            const patternUNC = !patternDrive &&
626                pattern[0] === '' &&
627                pattern[1] === '' &&
628                pattern[2] === '?' &&
629                typeof pattern[3] === 'string' &&
630                /^[a-z]:$/i.test(pattern[3]);
631            const fdi = fileUNC ? 3
632                : fileDrive ? 0
633                    : undefined;
634            const pdi = patternUNC ? 3
635                : patternDrive ? 0
636                    : undefined;
637            if (typeof fdi === 'number' && typeof pdi === 'number') {
638                const [fd, pd] = [
639                    file[fdi],
640                    pattern[pdi],
641                ];
642                // start matching at the drive letter index of each
643                if (fd.toLowerCase() === pd.toLowerCase()) {
644                    pattern[pdi] = fd;
645                    patternStartIndex = pdi;
646                    fileStartIndex = fdi;
647                }
648            }
649        }
650        // resolve and reduce . and .. portions in the file as well.
651        // don't need to do the second phase, because it's only one string[]
652        const { optimizationLevel = 1 } = this.options;
653        if (optimizationLevel >= 2) {
654            file = this.levelTwoFileOptimize(file);
655        }
656        if (pattern.includes(GLOBSTAR)) {
657            return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
658        }
659        return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
660    }
661    #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
662        // split the pattern into head, tail, and middle of ** delimited parts
663        const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
664        const lastgs = pattern.lastIndexOf(GLOBSTAR);
665        // split the pattern up into globstar-delimited sections
666        // the tail has to be at the end, and the others just have
667        // to be found in order from the head.
668        const [head, body, tail] = partial ?
669            [
670                pattern.slice(patternIndex, firstgs),
671                pattern.slice(firstgs + 1),
672                [],
673            ]
674            : [
675                pattern.slice(patternIndex, firstgs),
676                pattern.slice(firstgs + 1, lastgs),
677                pattern.slice(lastgs + 1),
678            ];
679        // check the head, from the current file/pattern index.
680        if (head.length) {
681            const fileHead = file.slice(fileIndex, fileIndex + head.length);
682            if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
683                return false;
684            }
685            fileIndex += head.length;
686            patternIndex += head.length;
687        }
688        // now we know the head matches!
689        // if the last portion is not empty, it MUST match the end
690        // check the tail
691        let fileTailMatch = 0;
692        if (tail.length) {
693            // if head + tail > file, then we cannot possibly match
694            if (tail.length + fileIndex > file.length)
695                return false;
696            // try to match the tail
697            let tailStart = file.length - tail.length;
698            if (this.#matchOne(file, tail, partial, tailStart, 0)) {
699                fileTailMatch = tail.length;
700            }
701            else {
702                // affordance for stuff like a/**/* matching a/b/
703                // if the last file portion is '', and there's more to the pattern
704                // then try without the '' bit.
705                if (file[file.length - 1] !== '' ||
706                    fileIndex + tail.length === file.length) {
707                    return false;
708                }
709                tailStart--;
710                if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
711                    return false;
712                }
713                fileTailMatch = tail.length + 1;
714            }
715        }
716        // now we know the tail matches!
717        // the middle is zero or more portions wrapped in **, possibly
718        // containing more ** sections.
719        // so a/**/b/**/c/**/d has become **/b/**/c/**
720        // if it's empty, it means a/**/b, just verify we have no bad dots
721        // if there's no tail, so it ends on /**, then we must have *something*
722        // after the head, or it's not a matc
723        if (!body.length) {
724            let sawSome = !!fileTailMatch;
725            for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
726                const f = String(file[i]);
727                sawSome = true;
728                if (f === '.' ||
729                    f === '..' ||
730                    (!this.options.dot && f.startsWith('.'))) {
731                    return false;
732                }
733            }
734            // in partial mode, we just need to get past all file parts
735            return partial || sawSome;
736        }
737        // now we know that there's one or more body sections, which can
738        // be matched anywhere from the 0 index (because the head was pruned)
739        // through to the length-fileTailMatch index.
740        // split the body up into sections, and note the minimum index it can
741        // be found at (start with the length of all previous segments)
742        // [section, before, after]
743        const bodySegments = [[[], 0]];
744        let currentBody = bodySegments[0];
745        let nonGsParts = 0;
746        const nonGsPartsSums = [0];
747        for (const b of body) {
748            if (b === GLOBSTAR) {
749                nonGsPartsSums.push(nonGsParts);
750                currentBody = [[], 0];
751                bodySegments.push(currentBody);
752            }
753            else {
754                currentBody[0].push(b);
755                nonGsParts++;
756            }
757        }
758        let i = bodySegments.length - 1;
759        const fileLength = file.length - fileTailMatch;
760        for (const b of bodySegments) {
761            b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
762        }
763        return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
764    }
765    // return false for "nope, not matching"
766    // return null for "not matching, cannot keep trying"
767    #matchGlobStarBodySections(file,
768    // pattern section, last possible position for it
769    bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
770        // take the first body segment, and walk from fileIndex to its "after"
771        // value at the end
772        // If it doesn't match at that position, we increment, until we hit
773        // that final possible position, and give up.
774        // If it does match, then advance and try to rest.
775        // If any of them fail we keep walking forward.
776        // this is still a bit recursively painful, but it's more constrained
777        // than previous implementations, because we never test something that
778        // can't possibly be a valid matching condition.
779        const bs = bodySegments[bodyIndex];
780        if (!bs) {
781            // just make sure that there's no bad dots
782            for (let i = fileIndex; i < file.length; i++) {
783                sawTail = true;
784                const f = file[i];
785                if (f === '.' ||
786                    f === '..' ||
787                    (!this.options.dot && f.startsWith('.'))) {
788                    return false;
789                }
790            }
791            return sawTail;
792        }
793        // have a non-globstar body section to test
794        const [body, after] = bs;
795        while (fileIndex <= after) {
796            const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
797            // if limit exceeded, no match. intentional false negative,
798            // acceptable break in correctness for security.
799            if (m && globStarDepth < this.maxGlobstarRecursion) {
800                // match! see if the rest match. if so, we're done!
801                const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
802                if (sub !== false) {
803                    return sub;
804                }
805            }
806            const f = file[fileIndex];
807            if (f === '.' ||
808                f === '..' ||
809                (!this.options.dot && f.startsWith('.'))) {
810                return false;
811            }
812            fileIndex++;
813        }
814        // walked off. no point continuing
815        return partial || null;
816    }
817    #matchOne(file, pattern, partial, fileIndex, patternIndex) {
818        let fi;
819        let pi;
820        let pl;
821        let fl;
822        for (fi = fileIndex,
823            pi = patternIndex,
824            fl = file.length,
825            pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
826            this.debug('matchOne loop');
827            let p = pattern[pi];
828            let f = file[fi];
829            this.debug(pattern, p, f);
830            // should be impossible.
831            // some invalid regexp stuff in the set.
832            /* c8 ignore start */
833            if (p === false || p === GLOBSTAR) {
834                return false;
835            }
836            /* c8 ignore stop */
837            // something other than **
838            // non-magic patterns just have to match exactly
839            // patterns with magic have been turned into regexps.
840            let hit;
841            if (typeof p === 'string') {
842                hit = f === p;
843                this.debug('string match', p, f, hit);
844            }
845            else {
846                hit = p.test(f);
847                this.debug('pattern match', p, f, hit);
848            }
849            if (!hit)
850                return false;
851        }
852        // Note: ending in / means that we'll get a final ""
853        // at the end of the pattern.  This can only match a
854        // corresponding "" at the end of the file.
855        // If the file ends in /, then it can only match a
856        // a pattern that ends in /, unless the pattern just
857        // doesn't have any more for it. But, a/b/ should *not*
858        // match "a/b/*", even though "" matches against the
859        // [^/]*? pattern, except in partial mode, where it might
860        // simply not be reached yet.
861        // However, a/b/ should still satisfy a/*
862        // now either we fell off the end of the pattern, or we're done.
863        if (fi === fl && pi === pl) {
864            // ran out of pattern and filename at the same time.
865            // an exact hit!
866            return true;
867        }
868        else if (fi === fl) {
869            // ran out of file, but still had pattern left.
870            // this is ok if we're doing the match as part of
871            // a glob fs traversal.
872            return partial;
873        }
874        else if (pi === pl) {
875            // ran out of pattern, still have file left.
876            // this is only acceptable if we're on the very last
877            // empty segment of a file with a trailing slash.
878            // a/* should match a/b/
879            return fi === fl - 1 && file[fi] === '';
880            /* c8 ignore start */
881        }
882        else {
883            // should be unreachable.
884            throw new Error('wtf?');
885        }
886        /* c8 ignore stop */
887    }
888    braceExpand() {
889        return braceExpand(this.pattern, this.options);
890    }
891    parse(pattern) {
892        assertValidPattern(pattern);
893        const options = this.options;
894        // shortcuts
895        if (pattern === '**')
896            return GLOBSTAR;
897        if (pattern === '')
898            return '';
899        // far and away, the most common glob pattern parts are
900        // *, *.*, and *.<ext>  Add a fast check method for those.
901        let m;
902        let fastTest = null;
903        if ((m = pattern.match(starRE))) {
904            fastTest = options.dot ? starTestDot : starTest;
905        }
906        else if ((m = pattern.match(starDotExtRE))) {
907            fastTest = (options.nocase ?
908                options.dot ?
909                    starDotExtTestNocaseDot
910                    : starDotExtTestNocase
911                : options.dot ? starDotExtTestDot
912                    : starDotExtTest)(m[1]);
913        }
914        else if ((m = pattern.match(qmarksRE))) {
915            fastTest = (options.nocase ?
916                options.dot ?
917                    qmarksTestNocaseDot
918                    : qmarksTestNocase
919                : options.dot ? qmarksTestDot
920                    : qmarksTest)(m);
921        }
922        else if ((m = pattern.match(starDotStarRE))) {
923            fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
924        }
925        else if ((m = pattern.match(dotStarRE))) {
926            fastTest = dotStarTest;
927        }
928        const re = AST.fromGlob(pattern, this.options).toMMPattern();
929        if (fastTest && typeof re === 'object') {
930            // Avoids overriding in frozen environments
931            Reflect.defineProperty(re, 'test', { value: fastTest });
932        }
933        return re;
934    }
935    makeRe() {
936        if (this.regexp || this.regexp === false)
937            return this.regexp;
938        // at this point, this.set is a 2d array of partial
939        // pattern strings, or "**".
940        //
941        // It's better to use .match().  This function shouldn't
942        // be used, really, but it's pretty convenient sometimes,
943        // when you just want to work with a regex.
944        const set = this.set;
945        if (!set.length) {
946            this.regexp = false;
947            return this.regexp;
948        }
949        const options = this.options;
950        const twoStar = options.noglobstar ? star
951            : options.dot ? twoStarDot
952                : twoStarNoDot;
953        const flags = new Set(options.nocase ? ['i'] : []);
954        // regexpify non-globstar patterns
955        // if ** is only item, then we just do one twoStar
956        // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
957        // if ** is last, append (\/twoStar|) to previous
958        // if ** is in the middle, append (\/|\/twoStar\/) to previous
959        // then filter out GLOBSTAR symbols
960        let re = set
961            .map(pattern => {
962            const pp = pattern.map(p => {
963                if (p instanceof RegExp) {
964                    for (const f of p.flags.split(''))
965                        flags.add(f);
966                }
967                return (typeof p === 'string' ? regExpEscape(p)
968                    : p === GLOBSTAR ? GLOBSTAR
969                        : p._src);
970            });
971            pp.forEach((p, i) => {
972                const next = pp[i + 1];
973                const prev = pp[i - 1];
974                if (p !== GLOBSTAR || prev === GLOBSTAR) {
975                    return;
976                }
977                if (prev === undefined) {
978                    if (next !== undefined && next !== GLOBSTAR) {
979                        pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
980                    }
981                    else {
982                        pp[i] = twoStar;
983                    }
984                }
985                else if (next === undefined) {
986                    pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + ')?';
987                }
988                else if (next !== GLOBSTAR) {
989                    pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
990                    pp[i + 1] = GLOBSTAR;
991                }
992            });
993            const filtered = pp.filter(p => p !== GLOBSTAR);
994            // For partial matches, we need to make the pattern match
995            // any prefix of the full path. We do this by generating
996            // alternative patterns that match progressively longer prefixes.
997            if (this.partial && filtered.length >= 1) {
998                const prefixes = [];
999                for (let i = 1; i <= filtered.length; i++) {
1000                    prefixes.push(filtered.slice(0, i).join('/'));
1001                }
1002                return '(?:' + prefixes.join('|') + ')';
1003            }
1004            return filtered.join('/');
1005        })
1006            .join('|');
1007        // need to wrap in parens if we had more than one thing with |,
1008        // otherwise only the first will be anchored to ^ and the last to $
1009        const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
1010        // must match entire pattern
1011        // ending in a * or ** will make it less strict.
1012        re = '^' + open + re + close + '$';
1013        // In partial mode, '/' should always match as it's a valid prefix for any pattern
1014        if (this.partial) {
1015            re = '^(?:\\/|' + open + re.slice(1, -1) + close + ')$';
1016        }
1017        // can match anything, as long as it's not this.
1018        if (this.negate)
1019            re = '^(?!' + re + ').+$';
1020        try {
1021            this.regexp = new RegExp(re, [...flags].join(''));
1022            /* c8 ignore start */
1023        }
1024        catch {
1025            // should be impossible
1026            this.regexp = false;
1027        }
1028        /* c8 ignore stop */
1029        return this.regexp;
1030    }
1031    slashSplit(p) {
1032        // if p starts with // on windows, we preserve that
1033        // so that UNC paths aren't broken.  Otherwise, any number of
1034        // / characters are coalesced into one, unless
1035        // preserveMultipleSlashes is set to true.
1036        if (this.preserveMultipleSlashes) {
1037            return p.split('/');
1038        }
1039        else if (this.isWindows && /^\/\/[^/]+/.test(p)) {
1040            // add an extra '' for the one we lose
1041            return ['', ...p.split(/\/+/)];
1042        }
1043        else {
1044            return p.split(/\/+/);
1045        }
1046    }
1047    match(f, partial = this.partial) {
1048        this.debug('match', f, this.pattern);
1049        // short-circuit in the case of busted things.
1050        // comments, etc.
1051        if (this.comment) {
1052            return false;
1053        }
1054        if (this.empty) {
1055            return f === '';
1056        }
1057        if (f === '/' && partial) {
1058            return true;
1059        }
1060        const options = this.options;
1061        // windows: need to use /, not \
1062        if (this.isWindows) {
1063            f = f.split('\\').join('/');
1064        }
1065        // treat the test path as a set of pathparts.
1066        const ff = this.slashSplit(f);
1067        this.debug(this.pattern, 'split', ff);
1068        // just ONE of the pattern sets in this.set needs to match
1069        // in order for it to be valid.  If negating, then just one
1070        // match means that we have failed.
1071        // Either way, return on the first hit.
1072        const set = this.set;
1073        this.debug(this.pattern, 'set', set);
1074        // Find the basename of the path by looking for the last non-empty segment
1075        let filename = ff[ff.length - 1];
1076        if (!filename) {
1077            for (let i = ff.length - 2; !filename && i >= 0; i--) {
1078                filename = ff[i];
1079            }
1080        }
1081        for (const pattern of set) {
1082            let file = ff;
1083            if (options.matchBase && pattern.length === 1) {
1084                file = [filename];
1085            }
1086            const hit = this.matchOne(file, pattern, partial);
1087            if (hit) {
1088                if (options.flipNegate) {
1089                    return true;
1090                }
1091                return !this.negate;
1092            }
1093        }
1094        // didn't get any hits.  this is success if it's a negative
1095        // pattern, failure otherwise.
1096        if (options.flipNegate) {
1097            return false;
1098        }
1099        return this.negate;
1100    }
1101    static defaults(def) {
1102        return minimatch.defaults(def).Minimatch;
1103    }
1104}
1105/* c8 ignore start */
1106export { AST } from './ast.js';
1107export { escape } from './escape.js';
1108export { unescape } from './unescape.js';
1109/* c8 ignore stop */
1110minimatch.AST = AST;
1111minimatch.Minimatch = Minimatch;
1112minimatch.escape = escape;
1113minimatch.unescape = unescape;
1114//# sourceMappingURL=index.js.map
codekingpro/portable-devtools · Team Ai