Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ast.js845 linesDownload Raw Back to commonjs
1"use strict";
2// parse a single path portion
3var _a;
4Object.defineProperty(exports, "__esModule", { value: true });
5exports.AST = void 0;
6const brace_expressions_js_1 = require("./brace-expressions.js");
7const unescape_js_1 = require("./unescape.js");
8const types = new Set(['!', '?', '+', '*', '@']);
9const isExtglobType = (c) => types.has(c);
10const isExtglobAST = (c) => isExtglobType(c.type);
11// Map of which extglob types can adopt the children of a nested extglob
12//
13// anything but ! can adopt a matching type:
14// +(a|+(b|c)|d) => +(a|b|c|d)
15// *(a|*(b|c)|d) => *(a|b|c|d)
16// @(a|@(b|c)|d) => @(a|b|c|d)
17// ?(a|?(b|c)|d) => ?(a|b|c|d)
18//
19// * can adopt anything, because 0 or repetition is allowed
20// *(a|?(b|c)|d) => *(a|b|c|d)
21// *(a|+(b|c)|d) => *(a|b|c|d)
22// *(a|@(b|c)|d) => *(a|b|c|d)
23//
24// + can adopt @, because 1 or repetition is allowed
25// +(a|@(b|c)|d) => +(a|b|c|d)
26//
27// + and @ CANNOT adopt *, because 0 would be allowed
28// +(a|*(b|c)|d) => would match "", on *(b|c)
29// @(a|*(b|c)|d) => would match "", on *(b|c)
30//
31// + and @ CANNOT adopt ?, because 0 would be allowed
32// +(a|?(b|c)|d) => would match "", on ?(b|c)
33// @(a|?(b|c)|d) => would match "", on ?(b|c)
34//
35// ? can adopt @, because 0 or 1 is allowed
36// ?(a|@(b|c)|d) => ?(a|b|c|d)
37//
38// ? and @ CANNOT adopt * or +, because >1 would be allowed
39// ?(a|*(b|c)|d) => would match bbb on *(b|c)
40// @(a|*(b|c)|d) => would match bbb on *(b|c)
41// ?(a|+(b|c)|d) => would match bbb on +(b|c)
42// @(a|+(b|c)|d) => would match bbb on +(b|c)
43//
44// ! CANNOT adopt ! (nothing else can either)
45// !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
46//
47// ! can adopt @
48// !(a|@(b|c)|d) => !(a|b|c|d)
49//
50// ! CANNOT adopt *
51// !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
52//
53// ! CANNOT adopt +
54// !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
55//
56// ! CANNOT adopt ?
57// x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
58const adoptionMap = new Map([
59    ['!', ['@']],
60    ['?', ['?', '@']],
61    ['@', ['@']],
62    ['*', ['*', '+', '?', '@']],
63    ['+', ['+', '@']],
64]);
65// nested extglobs that can be adopted in, but with the addition of
66// a blank '' element.
67const adoptionWithSpaceMap = new Map([
68    ['!', ['?']],
69    ['@', ['?']],
70    ['+', ['?', '*']],
71]);
72// union of the previous two maps
73const adoptionAnyMap = new Map([
74    ['!', ['?', '@']],
75    ['?', ['?', '@']],
76    ['@', ['?', '@']],
77    ['*', ['*', '+', '?', '@']],
78    ['+', ['+', '@', '?', '*']],
79]);
80// Extglobs that can take over their parent if they are the only child
81// the key is parent, value maps child to resulting extglob parent type
82// '@' is omitted because it's a special case. An `@` extglob with a single
83// member can always be usurped by that subpattern.
84const usurpMap = new Map([
85    ['!', new Map([['!', '@']])],
86    [
87        '?',
88        new Map([
89            ['*', '*'],
90            ['+', '*'],
91        ]),
92    ],
93    [
94        '@',
95        new Map([
96            ['!', '!'],
97            ['?', '?'],
98            ['@', '@'],
99            ['*', '*'],
100            ['+', '+'],
101        ]),
102    ],
103    [
104        '+',
105        new Map([
106            ['?', '*'],
107            ['*', '*'],
108        ]),
109    ],
110]);
111// Patterns that get prepended to bind to the start of either the
112// entire string, or just a single path portion, to prevent dots
113// and/or traversal patterns, when needed.
114// Exts don't need the ^ or / bit, because the root binds that already.
115const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
116const startNoDot = '(?!\\.)';
117// characters that indicate a start of pattern needs the "no dots" bit,
118// because a dot *might* be matched. ( is not in the list, because in
119// the case of a child extglob, it will handle the prevention itself.
120const addPatternStart = new Set(['[', '.']);
121// cases where traversal is A-OK, no dot prevention needed
122const justDots = new Set(['..', '.']);
123const reSpecials = new Set('().*{}+?[]^$\\!');
124const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
125// any single thing other than /
126const qmark = '[^/]';
127// * => any number of characters
128const star = qmark + '*?';
129// use + when we need to ensure that *something* matches, because the * is
130// the only thing in the path portion.
131const starNoEmpty = qmark + '+?';
132// remove the \ chars that we added if we end up doing a nonmagic compare
133// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
134let ID = 0;
135class AST {
136    type;
137    #root;
138    #hasMagic;
139    #uflag = false;
140    #parts = [];
141    #parent;
142    #parentIndex;
143    #negs;
144    #filledNegs = false;
145    #options;
146    #toString;
147    // set to true if it's an extglob with no children
148    // (which really means one child of '')
149    #emptyExt = false;
150    id = ++ID;
151    get depth() {
152        return (this.#parent?.depth ?? -1) + 1;
153    }
154    [Symbol.for('nodejs.util.inspect.custom')]() {
155        return {
156            '@@type': 'AST',
157            id: this.id,
158            type: this.type,
159            root: this.#root.id,
160            parent: this.#parent?.id,
161            depth: this.depth,
162            partsLength: this.#parts.length,
163            parts: this.#parts,
164        };
165    }
166    constructor(type, parent, options = {}) {
167        this.type = type;
168        // extglobs are inherently magical
169        if (type)
170            this.#hasMagic = true;
171        this.#parent = parent;
172        this.#root = this.#parent ? this.#parent.#root : this;
173        this.#options = this.#root === this ? options : this.#root.#options;
174        this.#negs = this.#root === this ? [] : this.#root.#negs;
175        if (type === '!' && !this.#root.#filledNegs)
176            this.#negs.push(this);
177        this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
178    }
179    get hasMagic() {
180        /* c8 ignore start */
181        if (this.#hasMagic !== undefined)
182            return this.#hasMagic;
183        /* c8 ignore stop */
184        for (const p of this.#parts) {
185            if (typeof p === 'string')
186                continue;
187            if (p.type || p.hasMagic)
188                return (this.#hasMagic = true);
189        }
190        // note: will be undefined until we generate the regexp src and find out
191        return this.#hasMagic;
192    }
193    // reconstructs the pattern
194    toString() {
195        return (this.#toString !== undefined ? this.#toString
196            : !this.type ?
197                (this.#toString = this.#parts.map(p => String(p)).join(''))
198                : (this.#toString =
199                    this.type +
200                        '(' +
201                        this.#parts.map(p => String(p)).join('|') +
202                        ')'));
203    }
204    #fillNegs() {
205        /* c8 ignore start */
206        if (this !== this.#root)
207            throw new Error('should only call on root');
208        if (this.#filledNegs)
209            return this;
210        /* c8 ignore stop */
211        // call toString() once to fill this out
212        this.toString();
213        this.#filledNegs = true;
214        let n;
215        while ((n = this.#negs.pop())) {
216            if (n.type !== '!')
217                continue;
218            // walk up the tree, appending everthing that comes AFTER parentIndex
219            let p = n;
220            let pp = p.#parent;
221            while (pp) {
222                for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
223                    for (const part of n.#parts) {
224                        /* c8 ignore start */
225                        if (typeof part === 'string') {
226                            throw new Error('string part in extglob AST??');
227                        }
228                        /* c8 ignore stop */
229                        part.copyIn(pp.#parts[i]);
230                    }
231                }
232                p = pp;
233                pp = p.#parent;
234            }
235        }
236        return this;
237    }
238    push(...parts) {
239        for (const p of parts) {
240            if (p === '')
241                continue;
242            /* c8 ignore start */
243            if (typeof p !== 'string' &&
244                !(p instanceof _a && p.#parent === this)) {
245                throw new Error('invalid part: ' + p);
246            }
247            /* c8 ignore stop */
248            this.#parts.push(p);
249        }
250    }
251    toJSON() {
252        const ret = this.type === null ?
253            this.#parts
254                .slice()
255                .map(p => (typeof p === 'string' ? p : p.toJSON()))
256            : [this.type, ...this.#parts.map(p => p.toJSON())];
257        if (this.isStart() && !this.type)
258            ret.unshift([]);
259        if (this.isEnd() &&
260            (this === this.#root ||
261                (this.#root.#filledNegs && this.#parent?.type === '!'))) {
262            ret.push({});
263        }
264        return ret;
265    }
266    isStart() {
267        if (this.#root === this)
268            return true;
269        // if (this.type) return !!this.#parent?.isStart()
270        if (!this.#parent?.isStart())
271            return false;
272        if (this.#parentIndex === 0)
273            return true;
274        // if everything AHEAD of this is a negation, then it's still the "start"
275        const p = this.#parent;
276        for (let i = 0; i < this.#parentIndex; i++) {
277            const pp = p.#parts[i];
278            if (!(pp instanceof _a && pp.type === '!')) {
279                return false;
280            }
281        }
282        return true;
283    }
284    isEnd() {
285        if (this.#root === this)
286            return true;
287        if (this.#parent?.type === '!')
288            return true;
289        if (!this.#parent?.isEnd())
290            return false;
291        if (!this.type)
292            return this.#parent?.isEnd();
293        // if not root, it'll always have a parent
294        /* c8 ignore start */
295        const pl = this.#parent ? this.#parent.#parts.length : 0;
296        /* c8 ignore stop */
297        return this.#parentIndex === pl - 1;
298    }
299    copyIn(part) {
300        if (typeof part === 'string')
301            this.push(part);
302        else
303            this.push(part.clone(this));
304    }
305    clone(parent) {
306        const c = new _a(this.type, parent);
307        for (const p of this.#parts) {
308            c.copyIn(p);
309        }
310        return c;
311    }
312    static #parseAST(str, ast, pos, opt, extDepth) {
313        const maxDepth = opt.maxExtglobRecursion ?? 2;
314        let escaping = false;
315        let inBrace = false;
316        let braceStart = -1;
317        let braceNeg = false;
318        if (ast.type === null) {
319            // outside of a extglob, append until we find a start
320            let i = pos;
321            let acc = '';
322            while (i < str.length) {
323                const c = str.charAt(i++);
324                // still accumulate escapes at this point, but we do ignore
325                // starts that are escaped
326                if (escaping || c === '\\') {
327                    escaping = !escaping;
328                    acc += c;
329                    continue;
330                }
331                if (inBrace) {
332                    if (i === braceStart + 1) {
333                        if (c === '^' || c === '!') {
334                            braceNeg = true;
335                        }
336                    }
337                    else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
338                        inBrace = false;
339                    }
340                    acc += c;
341                    continue;
342                }
343                else if (c === '[') {
344                    inBrace = true;
345                    braceStart = i;
346                    braceNeg = false;
347                    acc += c;
348                    continue;
349                }
350                // we don't have to check for adoption here, because that's
351                // done at the other recursion point.
352                const doRecurse = !opt.noext &&
353                    isExtglobType(c) &&
354                    str.charAt(i) === '(' &&
355                    extDepth <= maxDepth;
356                if (doRecurse) {
357                    ast.push(acc);
358                    acc = '';
359                    const ext = new _a(c, ast);
360                    i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
361                    ast.push(ext);
362                    continue;
363                }
364                acc += c;
365            }
366            ast.push(acc);
367            return i;
368        }
369        // some kind of extglob, pos is at the (
370        // find the next | or )
371        let i = pos + 1;
372        let part = new _a(null, ast);
373        const parts = [];
374        let acc = '';
375        while (i < str.length) {
376            const c = str.charAt(i++);
377            // still accumulate escapes at this point, but we do ignore
378            // starts that are escaped
379            if (escaping || c === '\\') {
380                escaping = !escaping;
381                acc += c;
382                continue;
383            }
384            if (inBrace) {
385                if (i === braceStart + 1) {
386                    if (c === '^' || c === '!') {
387                        braceNeg = true;
388                    }
389                }
390                else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
391                    inBrace = false;
392                }
393                acc += c;
394                continue;
395            }
396            else if (c === '[') {
397                inBrace = true;
398                braceStart = i;
399                braceNeg = false;
400                acc += c;
401                continue;
402            }
403            const doRecurse = !opt.noext &&
404                isExtglobType(c) &&
405                str.charAt(i) === '(' &&
406                /* c8 ignore start - the maxDepth is sufficient here */
407                (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
408            /* c8 ignore stop */
409            if (doRecurse) {
410                const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
411                part.push(acc);
412                acc = '';
413                const ext = new _a(c, part);
414                part.push(ext);
415                i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
416                continue;
417            }
418            if (c === '|') {
419                part.push(acc);
420                acc = '';
421                parts.push(part);
422                part = new _a(null, ast);
423                continue;
424            }
425            if (c === ')') {
426                if (acc === '' && ast.#parts.length === 0) {
427                    ast.#emptyExt = true;
428                }
429                part.push(acc);
430                acc = '';
431                ast.push(...parts, part);
432                return i;
433            }
434            acc += c;
435        }
436        // unfinished extglob
437        // if we got here, it was a malformed extglob! not an extglob, but
438        // maybe something else in there.
439        ast.type = null;
440        ast.#hasMagic = undefined;
441        ast.#parts = [str.substring(pos - 1)];
442        return i;
443    }
444    #canAdoptWithSpace(child) {
445        return this.#canAdopt(child, adoptionWithSpaceMap);
446    }
447    #canAdopt(child, map = adoptionMap) {
448        if (!child ||
449            typeof child !== 'object' ||
450            child.type !== null ||
451            child.#parts.length !== 1 ||
452            this.type === null) {
453            return false;
454        }
455        const gc = child.#parts[0];
456        if (!gc || typeof gc !== 'object' || gc.type === null) {
457            return false;
458        }
459        return this.#canAdoptType(gc.type, map);
460    }
461    #canAdoptType(c, map = adoptionAnyMap) {
462        return !!map.get(this.type)?.includes(c);
463    }
464    #adoptWithSpace(child, index) {
465        const gc = child.#parts[0];
466        const blank = new _a(null, gc, this.options);
467        blank.#parts.push('');
468        gc.push(blank);
469        this.#adopt(child, index);
470    }
471    #adopt(child, index) {
472        const gc = child.#parts[0];
473        this.#parts.splice(index, 1, ...gc.#parts);
474        for (const p of gc.#parts) {
475            if (typeof p === 'object')
476                p.#parent = this;
477        }
478        this.#toString = undefined;
479    }
480    #canUsurpType(c) {
481        const m = usurpMap.get(this.type);
482        return !!m?.has(c);
483    }
484    #canUsurp(child) {
485        if (!child ||
486            typeof child !== 'object' ||
487            child.type !== null ||
488            child.#parts.length !== 1 ||
489            this.type === null ||
490            this.#parts.length !== 1) {
491            return false;
492        }
493        const gc = child.#parts[0];
494        if (!gc || typeof gc !== 'object' || gc.type === null) {
495            return false;
496        }
497        return this.#canUsurpType(gc.type);
498    }
499    #usurp(child) {
500        const m = usurpMap.get(this.type);
501        const gc = child.#parts[0];
502        const nt = m?.get(gc.type);
503        /* c8 ignore start - impossible */
504        if (!nt)
505            return false;
506        /* c8 ignore stop */
507        this.#parts = gc.#parts;
508        for (const p of this.#parts) {
509            if (typeof p === 'object') {
510                p.#parent = this;
511            }
512        }
513        this.type = nt;
514        this.#toString = undefined;
515        this.#emptyExt = false;
516    }
517    static fromGlob(pattern, options = {}) {
518        const ast = new _a(null, undefined, options);
519        _a.#parseAST(pattern, ast, 0, options, 0);
520        return ast;
521    }
522    // returns the regular expression if there's magic, or the unescaped
523    // string if not.
524    toMMPattern() {
525        // should only be called on root
526        /* c8 ignore start */
527        if (this !== this.#root)
528            return this.#root.toMMPattern();
529        /* c8 ignore stop */
530        const glob = this.toString();
531        const [re, body, hasMagic, uflag] = this.toRegExpSource();
532        // if we're in nocase mode, and not nocaseMagicOnly, then we do
533        // still need a regular expression if we have to case-insensitively
534        // match capital/lowercase characters.
535        const anyMagic = hasMagic ||
536            this.#hasMagic ||
537            (this.#options.nocase &&
538                !this.#options.nocaseMagicOnly &&
539                glob.toUpperCase() !== glob.toLowerCase());
540        if (!anyMagic) {
541            return body;
542        }
543        const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
544        return Object.assign(new RegExp(`^${re}$`, flags), {
545            _src: re,
546            _glob: glob,
547        });
548    }
549    get options() {
550        return this.#options;
551    }
552    // returns the string match, the regexp source, whether there's magic
553    // in the regexp (so a regular expression is required) and whether or
554    // not the uflag is needed for the regular expression (for posix classes)
555    // TODO: instead of injecting the start/end at this point, just return
556    // the BODY of the regexp, along with the start/end portions suitable
557    // for binding the start/end in either a joined full-path makeRe context
558    // (where we bind to (^|/), or a standalone matchPart context (where
559    // we bind to ^, and not /).  Otherwise slashes get duped!
560    //
561    // In part-matching mode, the start is:
562    // - if not isStart: nothing
563    // - if traversal possible, but not allowed: ^(?!\.\.?$)
564    // - if dots allowed or not possible: ^
565    // - if dots possible and not allowed: ^(?!\.)
566    // end is:
567    // - if not isEnd(): nothing
568    // - else: $
569    //
570    // In full-path matching mode, we put the slash at the START of the
571    // pattern, so start is:
572    // - if first pattern: same as part-matching mode
573    // - if not isStart(): nothing
574    // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
575    // - if dots allowed or not possible: /
576    // - if dots possible and not allowed: /(?!\.)
577    // end is:
578    // - if last pattern, same as part-matching mode
579    // - else nothing
580    //
581    // Always put the (?:$|/) on negated tails, though, because that has to be
582    // there to bind the end of the negated pattern portion, and it's easier to
583    // just stick it in now rather than try to inject it later in the middle of
584    // the pattern.
585    //
586    // We can just always return the same end, and leave it up to the caller
587    // to know whether it's going to be used joined or in parts.
588    // And, if the start is adjusted slightly, can do the same there:
589    // - if not isStart: nothing
590    // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
591    // - if dots allowed or not possible: (?:/|^)
592    // - if dots possible and not allowed: (?:/|^)(?!\.)
593    //
594    // But it's better to have a simpler binding without a conditional, for
595    // performance, so probably better to return both start options.
596    //
597    // Then the caller just ignores the end if it's not the first pattern,
598    // and the start always gets applied.
599    //
600    // But that's always going to be $ if it's the ending pattern, or nothing,
601    // so the caller can just attach $ at the end of the pattern when building.
602    //
603    // So the todo is:
604    // - better detect what kind of start is needed
605    // - return both flavors of starting pattern
606    // - attach $ at the end of the pattern when creating the actual RegExp
607    //
608    // Ah, but wait, no, that all only applies to the root when the first pattern
609    // is not an extglob. If the first pattern IS an extglob, then we need all
610    // that dot prevention biz to live in the extglob portions, because eg
611    // +(*|.x*) can match .xy but not .yx.
612    //
613    // So, return the two flavors if it's #root and the first child is not an
614    // AST, otherwise leave it to the child AST to handle it, and there,
615    // use the (?:^|/) style of start binding.
616    //
617    // Even simplified further:
618    // - Since the start for a join is eg /(?!\.) and the start for a part
619    // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
620    // or start or whatever) and prepend ^ or / at the Regexp construction.
621    toRegExpSource(allowDot) {
622        const dot = allowDot ?? !!this.#options.dot;
623        if (this.#root === this) {
624            this.#flatten();
625            this.#fillNegs();
626        }
627        if (!isExtglobAST(this)) {
628            const noEmpty = this.isStart() &&
629                this.isEnd() &&
630                !this.#parts.some(s => typeof s !== 'string');
631            const src = this.#parts
632                .map(p => {
633                const [re, _, hasMagic, uflag] = typeof p === 'string' ?
634                    _a.#parseGlob(p, this.#hasMagic, noEmpty)
635                    : p.toRegExpSource(allowDot);
636                this.#hasMagic = this.#hasMagic || hasMagic;
637                this.#uflag = this.#uflag || uflag;
638                return re;
639            })
640                .join('');
641            let start = '';
642            if (this.isStart()) {
643                if (typeof this.#parts[0] === 'string') {
644                    // this is the string that will match the start of the pattern,
645                    // so we need to protect against dots and such.
646                    // '.' and '..' cannot match unless the pattern is that exactly,
647                    // even if it starts with . or dot:true is set.
648                    const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
649                    if (!dotTravAllowed) {
650                        const aps = addPatternStart;
651                        // check if we have a possibility of matching . or ..,
652                        // and prevent that.
653                        const needNoTrav =
654                        // dots are allowed, and the pattern starts with [ or .
655                        (dot && aps.has(src.charAt(0))) ||
656                            // the pattern starts with \., and then [ or .
657                            (src.startsWith('\\.') && aps.has(src.charAt(2))) ||
658                            // the pattern starts with \.\., and then [ or .
659                            (src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
660                        // no need to prevent dots if it can't match a dot, or if a
661                        // sub-pattern will be preventing it anyway.
662                        const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
663                        start =
664                            needNoTrav ? startNoTraversal
665                                : needNoDot ? startNoDot
666                                    : '';
667                    }
668                }
669            }
670            // append the "end of path portion" pattern to negation tails
671            let end = '';
672            if (this.isEnd() &&
673                this.#root.#filledNegs &&
674                this.#parent?.type === '!') {
675                end = '(?:$|\\/)';
676            }
677            const final = start + src + end;
678            return [
679                final,
680                (0, unescape_js_1.unescape)(src),
681                (this.#hasMagic = !!this.#hasMagic),
682                this.#uflag,
683            ];
684        }
685        // We need to calculate the body *twice* if it's a repeat pattern
686        // at the start, once in nodot mode, then again in dot mode, so a
687        // pattern like *(?) can match 'x.y'
688        const repeated = this.type === '*' || this.type === '+';
689        // some kind of extglob
690        const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
691        let body = this.#partsToRegExp(dot);
692        if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
693            // invalid extglob, has to at least be *something* present, if it's
694            // the entire path portion.
695            const s = this.toString();
696            const me = this;
697            me.#parts = [s];
698            me.type = null;
699            me.#hasMagic = undefined;
700            return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];
701        }
702        let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
703            ''
704            : this.#partsToRegExp(true);
705        if (bodyDotAllowed === body) {
706            bodyDotAllowed = '';
707        }
708        if (bodyDotAllowed) {
709            body = `(?:${body})(?:${bodyDotAllowed})*?`;
710        }
711        // an empty !() is exactly equivalent to a starNoEmpty
712        let final = '';
713        if (this.type === '!' && this.#emptyExt) {
714            final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
715        }
716        else {
717            const close = this.type === '!' ?
718                // !() must match something,but !(x) can match ''
719                '))' +
720                    (this.isStart() && !dot && !allowDot ? startNoDot : '') +
721                    star +
722                    ')'
723                : this.type === '@' ? ')'
724                    : this.type === '?' ? ')?'
725                        : this.type === '+' && bodyDotAllowed ? ')'
726                            : this.type === '*' && bodyDotAllowed ? `)?`
727                                : `)${this.type}`;
728            final = start + body + close;
729        }
730        return [
731            final,
732            (0, unescape_js_1.unescape)(body),
733            (this.#hasMagic = !!this.#hasMagic),
734            this.#uflag,
735        ];
736    }
737    #flatten() {
738        if (!isExtglobAST(this)) {
739            for (const p of this.#parts) {
740                if (typeof p === 'object') {
741                    p.#flatten();
742                }
743            }
744        }
745        else {
746            // do up to 10 passes to flatten as much as possible
747            let iterations = 0;
748            let done = false;
749            do {
750                done = true;
751                for (let i = 0; i < this.#parts.length; i++) {
752                    const c = this.#parts[i];
753                    if (typeof c === 'object') {
754                        c.#flatten();
755                        if (this.#canAdopt(c)) {
756                            done = false;
757                            this.#adopt(c, i);
758                        }
759                        else if (this.#canAdoptWithSpace(c)) {
760                            done = false;
761                            this.#adoptWithSpace(c, i);
762                        }
763                        else if (this.#canUsurp(c)) {
764                            done = false;
765                            this.#usurp(c);
766                        }
767                    }
768                }
769            } while (!done && ++iterations < 10);
770        }
771        this.#toString = undefined;
772    }
773    #partsToRegExp(dot) {
774        return this.#parts
775            .map(p => {
776            // extglob ASTs should only contain parent ASTs
777            /* c8 ignore start */
778            if (typeof p === 'string') {
779                throw new Error('string type in extglob ast??');
780            }
781            /* c8 ignore stop */
782            // can ignore hasMagic, because extglobs are already always magic
783            const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
784            this.#uflag = this.#uflag || uflag;
785            return re;
786        })
787            .filter(p => !(this.isStart() && this.isEnd()) || !!p)
788            .join('|');
789    }
790    static #parseGlob(glob, hasMagic, noEmpty = false) {
791        let escaping = false;
792        let re = '';
793        let uflag = false;
794        // multiple stars that aren't globstars coalesce into one *
795        let inStar = false;
796        for (let i = 0; i < glob.length; i++) {
797            const c = glob.charAt(i);
798            if (escaping) {
799                escaping = false;
800                re += (reSpecials.has(c) ? '\\' : '') + c;
801                continue;
802            }
803            if (c === '*') {
804                if (inStar)
805                    continue;
806                inStar = true;
807                re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;
808                hasMagic = true;
809                continue;
810            }
811            else {
812                inStar = false;
813            }
814            if (c === '\\') {
815                if (i === glob.length - 1) {
816                    re += '\\\\';
817                }
818                else {
819                    escaping = true;
820                }
821                continue;
822            }
823            if (c === '[') {
824                const [src, needUflag, consumed, magic] = (0, brace_expressions_js_1.parseClass)(glob, i);
825                if (consumed) {
826                    re += src;
827                    uflag = uflag || needUflag;
828                    i += consumed - 1;
829                    hasMagic = hasMagic || magic;
830                    continue;
831                }
832            }
833            if (c === '?') {
834                re += qmark;
835                hasMagic = true;
836                continue;
837            }
838            re += regExpEscape(c);
839        }
840        return [re, (0, unescape_js_1.unescape)(glob), !!hasMagic, uflag];
841    }
842}
843exports.AST = AST;
844_a = AST;
845//# sourceMappingURL=ast.js.map
codekingpro/portable-devtools · Team Ai