Team Ai
Datasetpublic

codekingpro/portable-devtools

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