File Explorer

/var/runtime/node_modules/@aws-sdk/node_modules/glob/node_modules/minimatch/dist/commonjs

This explorer reads the filesystem of the server it runs on, so /workspace/user isn't present here. Browsing and the terminal still work against this server's own disk from /.

ast.js27.1 KB · 747 lines
"use strict";// parse a single path portionvar _a;Object.defineProperty(exports, "__esModule", { value: true });exports.AST = void 0;const brace_expressions_js_1 = require("./brace-expressions.js");const unescape_js_1 = require("./unescape.js");const types = new Set(['!', '?', '+', '*', '@']);const isExtglobType = (c) => types.has(c);const isExtglobAST = (c) => isExtglobType(c.type);const adoptionMap = new Map([    ['!', ['@']],    ['?', ['?', '@']],    ['@', ['@']],    ['*', ['*', '+', '?', '@']],    ['+', ['+', '@']],]);const adoptionWithSpaceMap = new Map([    ['!', ['?']],    ['@', ['?']],    ['+', ['?', '*']],]);const adoptionAnyMap = new Map([    ['!', ['?', '@']],    ['?', ['?', '@']],    ['@', ['?', '@']],    ['*', ['*', '+', '?', '@']],    ['+', ['+', '@', '?', '*']],]);const usurpMap = new Map([    ['!', new Map([['!', '@']])],    ['?', new Map([['*', '*'], ['+', '*']])],    ['@', new Map([['!', '!'], ['?', '?'], ['@', '@'], ['*', '*'], ['+', '+']])],    ['+', new Map([['?', '*'], ['*', '*']])],]);// Patterns that get prepended to bind to the start of either the// entire string, or just a single path portion, to prevent dots// and/or traversal patterns, when needed.// Exts don't need the ^ or / bit, because the root binds that already.const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';const startNoDot = '(?!\\.)';// characters that indicate a start of pattern needs the "no dots" bit,// because a dot *might* be matched. ( is not in the list, because in// the case of a child extglob, it will handle the prevention itself.const addPatternStart = new Set(['[', '.']);// cases where traversal is A-OK, no dot prevention neededconst justDots = new Set(['..', '.']);const reSpecials = new Set('().*{}+?[]^$\\!');const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');// any single thing other than /const qmark = '[^/]';// * => any number of charactersconst star = qmark + '*?';// use + when we need to ensure that *something* matches, because the * is// the only thing in the path portion.const starNoEmpty = qmark + '+?';// remove the \ chars that we added if we end up doing a nonmagic compare// const deslash = (s: string) => s.replace(/\\(.)/g, '$1')class AST {    type;    #root;    #hasMagic;    #uflag = false;    #parts = [];    #parent;    #parentIndex;    #negs;    #filledNegs = false;    #options;    #toString;    // set to true if it's an extglob with no children    // (which really means one child of '')    #emptyExt = false;    constructor(type, parent, options = {}) {        this.type = type;        // extglobs are inherently magical        if (type)            this.#hasMagic = true;        this.#parent = parent;        this.#root = this.#parent ? this.#parent.#root : this;        this.#options = this.#root === this ? options : this.#root.#options;        this.#negs = this.#root === this ? [] : this.#root.#negs;        if (type === '!' && !this.#root.#filledNegs)            this.#negs.push(this);        this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;    }    get hasMagic() {        /* c8 ignore start */        if (this.#hasMagic !== undefined)            return this.#hasMagic;        /* c8 ignore stop */        for (const p of this.#parts) {            if (typeof p === 'string')                continue;            if (p.type || p.hasMagic)                return (this.#hasMagic = true);        }        // note: will be undefined until we generate the regexp src and find out        return this.#hasMagic;    }    // reconstructs the pattern    toString() {        if (this.#toString !== undefined)            return this.#toString;        if (!this.type) {            return (this.#toString = this.#parts.map(p => String(p)).join(''));        }        else {            return (this.#toString =                this.type + '(' + this.#parts.map(p => String(p)).join('|') + ')');        }    }    #fillNegs() {        /* c8 ignore start */        if (this !== this.#root)            throw new Error('should only call on root');        if (this.#filledNegs)            return this;        /* c8 ignore stop */        // call toString() once to fill this out        this.toString();        this.#filledNegs = true;        let n;        while ((n = this.#negs.pop())) {            if (n.type !== '!')                continue;            // walk up the tree, appending everthing that comes AFTER parentIndex            let p = n;            let pp = p.#parent;            while (pp) {                for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {                    for (const part of n.#parts) {                        /* c8 ignore start */                        if (typeof part === 'string') {                            throw new Error('string part in extglob AST??');                        }                        /* c8 ignore stop */                        part.copyIn(pp.#parts[i]);                    }                }                p = pp;                pp = p.#parent;            }        }        return this;    }    push(...parts) {        for (const p of parts) {            if (p === '')                continue;            /* c8 ignore start */            if (typeof p !== 'string' && !(p instanceof _a && p.#parent === this)) {                throw new Error('invalid part: ' + p);            }            /* c8 ignore stop */            this.#parts.push(p);        }    }    toJSON() {        const ret = this.type === null            ? this.#parts.slice().map(p => (typeof p === 'string' ? p : p.toJSON()))            : [this.type, ...this.#parts.map(p => p.toJSON())];        if (this.isStart() && !this.type)            ret.unshift([]);        if (this.isEnd() &&            (this === this.#root ||                (this.#root.#filledNegs && this.#parent?.type === '!'))) {            ret.push({});        }        return ret;    }    isStart() {        if (this.#root === this)            return true;        // if (this.type) return !!this.#parent?.isStart()        if (!this.#parent?.isStart())            return false;        if (this.#parentIndex === 0)            return true;        // if everything AHEAD of this is a negation, then it's still the "start"        const p = this.#parent;        for (let i = 0; i < this.#parentIndex; i++) {            const pp = p.#parts[i];            if (!(pp instanceof _a && pp.type === '!')) {                return false;            }        }        return true;    }    isEnd() {        if (this.#root === this)            return true;        if (this.#parent?.type === '!')            return true;        if (!this.#parent?.isEnd())            return false;        if (!this.type)            return this.#parent?.isEnd();        // if not root, it'll always have a parent        /* c8 ignore start */        const pl = this.#parent ? this.#parent.#parts.length : 0;        /* c8 ignore stop */        return this.#parentIndex === pl - 1;    }    copyIn(part) {        if (typeof part === 'string')            this.push(part);        else            this.push(part.clone(this));    }    clone(parent) {        const c = new _a(this.type, parent);        for (const p of this.#parts) {            c.copyIn(p);        }        return c;    }    static #parseAST(str, ast, pos, opt, extDepth) {        const maxDepth = opt.maxExtglobRecursion ?? 2;        let escaping = false;        let inBrace = false;        let braceStart = -1;        let braceNeg = false;        if (ast.type === null) {            // outside of a extglob, append until we find a start            let i = pos;            let acc = '';            while (i < str.length) {                const c = str.charAt(i++);                // still accumulate escapes at this point, but we do ignore                // starts that are escaped                if (escaping || c === '\\') {                    escaping = !escaping;                    acc += c;                    continue;                }                if (inBrace) {                    if (i === braceStart + 1) {                        if (c === '^' || c === '!') {                            braceNeg = true;                        }                    }                    else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {                        inBrace = false;                    }                    acc += c;                    continue;                }                else if (c === '[') {                    inBrace = true;                    braceStart = i;                    braceNeg = false;                    acc += c;                    continue;                }                const doRecurse = !opt.noext &&                    isExtglobType(c) &&                    str.charAt(i) === '(' &&                    extDepth <= maxDepth;                if (doRecurse) {                    ast.push(acc);                    acc = '';                    const ext = new _a(c, ast);                    i = _a.#parseAST(str, ext, i, opt, extDepth + 1);                    ast.push(ext);                    continue;                }                acc += c;            }            ast.push(acc);            return i;        }        // some kind of extglob, pos is at the (        // find the next | or )        let i = pos + 1;        let part = new _a(null, ast);        const parts = [];        let acc = '';        while (i < str.length) {            const c = str.charAt(i++);            // still accumulate escapes at this point, but we do ignore            // starts that are escaped            if (escaping || c === '\\') {                escaping = !escaping;                acc += c;                continue;            }            if (inBrace) {                if (i === braceStart + 1) {                    if (c === '^' || c === '!') {                        braceNeg = true;                    }                }                else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {                    inBrace = false;                }                acc += c;                continue;            }            else if (c === '[') {                inBrace = true;                braceStart = i;                braceNeg = false;                acc += c;                continue;            }            const doRecurse = isExtglobType(c) &&                str.charAt(i) === '(' &&                /* c8 ignore start - the maxDepth is sufficient here */                (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));            /* c8 ignore stop */            if (doRecurse) {                const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;                part.push(acc);                acc = '';                const ext = new _a(c, part);                part.push(ext);                i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);                continue;            }            if (c === '|') {                part.push(acc);                acc = '';                parts.push(part);                part = new _a(null, ast);                continue;            }            if (c === ')') {                if (acc === '' && ast.#parts.length === 0) {                    ast.#emptyExt = true;                }                part.push(acc);                acc = '';                ast.push(...parts, part);                return i;            }            acc += c;        }        // unfinished extglob        // if we got here, it was a malformed extglob! not an extglob, but        // maybe something else in there.        ast.type = null;        ast.#hasMagic = undefined;        ast.#parts = [str.substring(pos - 1)];        return i;    }    #canAdoptWithSpace(child) {        return this.#canAdopt(child, adoptionWithSpaceMap);    }    #canAdopt(child, map = adoptionMap) {        if (!child ||            typeof child !== 'object' ||            child.type !== null ||            child.#parts.length !== 1 ||            this.type === null) {            return false;        }        const gc = child.#parts[0];        if (!gc || typeof gc !== 'object' || gc.type === null) {            return false;        }        return this.#canAdoptType(gc.type, map);    }    #canAdoptType(c, map = adoptionAnyMap) {        return !!map.get(this.type)?.includes(c);    }    #adoptWithSpace(child, index) {        const gc = child.#parts[0];        const blank = new _a(null, gc, this.options);        blank.#parts.push('');        gc.push(blank);        this.#adopt(child, index);    }    #adopt(child, index) {        const gc = child.#parts[0];        this.#parts.splice(index, 1, ...gc.#parts);        for (const p of gc.#parts) {            if (typeof p === 'object')                p.#parent = this;        }        this.#toString = undefined;    }    #canUsurpType(c) {        const m = usurpMap.get(this.type);        return !!(m?.has(c));    }    #canUsurp(child) {        if (!child ||            typeof child !== 'object' ||            child.type !== null ||            child.#parts.length !== 1 ||            this.type === null ||            this.#parts.length !== 1) {            return false;        }        const gc = child.#parts[0];        if (!gc || typeof gc !== 'object' || gc.type === null) {            return false;        }        return this.#canUsurpType(gc.type);    }    #usurp(child) {        const m = usurpMap.get(this.type);        const gc = child.#parts[0];        const nt = m?.get(gc.type);        /* c8 ignore start - impossible */        if (!nt)            return false;        /* c8 ignore stop */        this.#parts = gc.#parts;        for (const p of this.#parts) {            if (typeof p === 'object')                p.#parent = this;        }        this.type = nt;        this.#toString = undefined;        this.#emptyExt = false;    }    #flatten() {        if (!isExtglobAST(this)) {            for (const p of this.#parts) {                if (typeof p === 'object')                    p.#flatten();            }        }        else {            let iterations = 0;            let done = false;            do {                done = true;                for (let i = 0; i < this.#parts.length; i++) {                    const c = this.#parts[i];                    if (typeof c === 'object') {                        c.#flatten();                        if (this.#canAdopt(c)) {                            done = false;                            this.#adopt(c, i);                        }                        else if (this.#canAdoptWithSpace(c)) {                            done = false;                            this.#adoptWithSpace(c, i);                        }                        else if (this.#canUsurp(c)) {                            done = false;                            this.#usurp(c);                        }                    }                }            } while (!done && ++iterations < 10);        }        this.#toString = undefined;    }    static fromGlob(pattern, options = {}) {        const ast = new _a(null, undefined, options);        _a.#parseAST(pattern, ast, 0, options, 0);        return ast;    }    // returns the regular expression if there's magic, or the unescaped    // string if not.    toMMPattern() {        // should only be called on root        /* c8 ignore start */        if (this !== this.#root)            return this.#root.toMMPattern();        /* c8 ignore stop */        const glob = this.toString();        const [re, body, hasMagic, uflag] = this.toRegExpSource();        // if we're in nocase mode, and not nocaseMagicOnly, then we do        // still need a regular expression if we have to case-insensitively        // match capital/lowercase characters.        const anyMagic = hasMagic ||            this.#hasMagic ||            (this.#options.nocase &&                !this.#options.nocaseMagicOnly &&                glob.toUpperCase() !== glob.toLowerCase());        if (!anyMagic) {            return body;        }        const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');        return Object.assign(new RegExp(`^${re}$`, flags), {            _src: re,            _glob: glob,        });    }    get options() {        return this.#options;    }    // returns the string match, the regexp source, whether there's magic    // in the regexp (so a regular expression is required) and whether or    // not the uflag is needed for the regular expression (for posix classes)    // TODO: instead of injecting the start/end at this point, just return    // the BODY of the regexp, along with the start/end portions suitable    // for binding the start/end in either a joined full-path makeRe context    // (where we bind to (^|/), or a standalone matchPart context (where    // we bind to ^, and not /).  Otherwise slashes get duped!    //    // In part-matching mode, the start is:    // - if not isStart: nothing    // - if traversal possible, but not allowed: ^(?!\.\.?$)    // - if dots allowed or not possible: ^    // - if dots possible and not allowed: ^(?!\.)    // end is:    // - if not isEnd(): nothing    // - else: $    //    // In full-path matching mode, we put the slash at the START of the    // pattern, so start is:    // - if first pattern: same as part-matching mode    // - if not isStart(): nothing    // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))    // - if dots allowed or not possible: /    // - if dots possible and not allowed: /(?!\.)    // end is:    // - if last pattern, same as part-matching mode    // - else nothing    //    // Always put the (?:$|/) on negated tails, though, because that has to be    // there to bind the end of the negated pattern portion, and it's easier to    // just stick it in now rather than try to inject it later in the middle of    // the pattern.    //    // We can just always return the same end, and leave it up to the caller    // to know whether it's going to be used joined or in parts.    // And, if the start is adjusted slightly, can do the same there:    // - if not isStart: nothing    // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)    // - if dots allowed or not possible: (?:/|^)    // - if dots possible and not allowed: (?:/|^)(?!\.)    //    // But it's better to have a simpler binding without a conditional, for    // performance, so probably better to return both start options.    //    // Then the caller just ignores the end if it's not the first pattern,    // and the start always gets applied.    //    // But that's always going to be $ if it's the ending pattern, or nothing,    // so the caller can just attach $ at the end of the pattern when building.    //    // So the todo is:    // - better detect what kind of start is needed    // - return both flavors of starting pattern    // - attach $ at the end of the pattern when creating the actual RegExp    //    // Ah, but wait, no, that all only applies to the root when the first pattern    // is not an extglob. If the first pattern IS an extglob, then we need all    // that dot prevention biz to live in the extglob portions, because eg    // +(*|.x*) can match .xy but not .yx.    //    // So, return the two flavors if it's #root and the first child is not an    // AST, otherwise leave it to the child AST to handle it, and there,    // use the (?:^|/) style of start binding.    //    // Even simplified further:    // - Since the start for a join is eg /(?!\.) and the start for a part    // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root    // or start or whatever) and prepend ^ or / at the Regexp construction.    toRegExpSource(allowDot) {        const dot = allowDot ?? !!this.#options.dot;        if (this.#root === this) {            this.#flatten();            this.#fillNegs();        }        if (!isExtglobAST(this)) {            const noEmpty = this.isStart() && this.isEnd();            const src = this.#parts                .map(p => {                const [re, _, hasMagic, uflag] = typeof p === 'string'                    ? _a.#parseGlob(p, this.#hasMagic, noEmpty)                    : p.toRegExpSource(allowDot);                this.#hasMagic = this.#hasMagic || hasMagic;                this.#uflag = this.#uflag || uflag;                return re;            })                .join('');            let start = '';            if (this.isStart()) {                if (typeof this.#parts[0] === 'string') {                    // this is the string that will match the start of the pattern,                    // so we need to protect against dots and such.                    // '.' and '..' cannot match unless the pattern is that exactly,                    // even if it starts with . or dot:true is set.                    const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);                    if (!dotTravAllowed) {                        const aps = addPatternStart;                        // check if we have a possibility of matching . or ..,                        // and prevent that.                        const needNoTrav =                         // dots are allowed, and the pattern starts with [ or .                        (dot && aps.has(src.charAt(0))) ||                            // the pattern starts with \., and then [ or .                            (src.startsWith('\\.') && aps.has(src.charAt(2))) ||                            // the pattern starts with \.\., and then [ or .                            (src.startsWith('\\.\\.') && aps.has(src.charAt(4)));                        // no need to prevent dots if it can't match a dot, or if a                        // sub-pattern will be preventing it anyway.                        const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));                        start = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : '';                    }                }            }            // append the "end of path portion" pattern to negation tails            let end = '';            if (this.isEnd() &&                this.#root.#filledNegs &&                this.#parent?.type === '!') {                end = '(?:$|\\/)';            }            const final = start + src + end;            return [                final,                (0, unescape_js_1.unescape)(src),                (this.#hasMagic = !!this.#hasMagic),                this.#uflag,            ];        }        // We need to calculate the body *twice* if it's a repeat pattern        // at the start, once in nodot mode, then again in dot mode, so a        // pattern like *(?) can match 'x.y'        const repeated = this.type === '*' || this.type === '+';        // some kind of extglob        const start = this.type === '!' ? '(?:(?!(?:' : '(?:';        let body = this.#partsToRegExp(dot);        if (this.isStart() && this.isEnd() && !body && this.type !== '!') {            // invalid extglob, has to at least be *something* present, if it's            // the entire path portion.            const s = this.toString();            const me = this;            me.#parts = [s];            me.type = null;            me.#hasMagic = undefined;            return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];        }        // XXX abstract out this map method        let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot            ? ''            : this.#partsToRegExp(true);        if (bodyDotAllowed === body) {            bodyDotAllowed = '';        }        if (bodyDotAllowed) {            body = `(?:${body})(?:${bodyDotAllowed})*?`;        }        // an empty !() is exactly equivalent to a starNoEmpty        let final = '';        if (this.type === '!' && this.#emptyExt) {            final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;        }        else {            const close = this.type === '!'                ? // !() must match something,but !(x) can match ''                    '))' +                        (this.isStart() && !dot && !allowDot ? startNoDot : '') +                        star +                        ')'                : this.type === '@'                    ? ')'                    : this.type === '?'                        ? ')?'                        : this.type === '+' && bodyDotAllowed                            ? ')'                            : this.type === '*' && bodyDotAllowed                                ? `)?`                                : `)${this.type}`;            final = start + body + close;        }        return [            final,            (0, unescape_js_1.unescape)(body),            (this.#hasMagic = !!this.#hasMagic),            this.#uflag,        ];    }    #partsToRegExp(dot) {        return this.#parts            .map(p => {            // extglob ASTs should only contain parent ASTs            /* c8 ignore start */            if (typeof p === 'string') {                throw new Error('string type in extglob ast??');            }            /* c8 ignore stop */            // can ignore hasMagic, because extglobs are already always magic            const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);            this.#uflag = this.#uflag || uflag;            return re;        })            .filter(p => !(this.isStart() && this.isEnd()) || !!p)            .join('|');    }    static #parseGlob(glob, hasMagic, noEmpty = false) {        let escaping = false;        let re = '';        let uflag = false;        // multiple stars that aren't globstars coalesce into one *        let inStar = false;        for (let i = 0; i < glob.length; i++) {            const c = glob.charAt(i);            if (escaping) {                escaping = false;                re += (reSpecials.has(c) ? '\\' : '') + c;                inStar = false;                continue;            }            if (c === '\\') {                if (i === glob.length - 1) {                    re += '\\\\';                }                else {                    escaping = true;                }                continue;            }            if (c === '[') {                const [src, needUflag, consumed, magic] = (0, brace_expressions_js_1.parseClass)(glob, i);                if (consumed) {                    re += src;                    uflag = uflag || needUflag;                    i += consumed - 1;                    hasMagic = hasMagic || magic;                    inStar = false;                    continue;                }            }            if (c === '*') {                if (inStar)                    continue;                inStar = true;                re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;                hasMagic = true;                continue;            }            else {                inStar = false;            }            if (c === '?') {                re += qmark;                hasMagic = true;                continue;            }            re += regExpEscape(c);        }        return [re, (0, unescape_js_1.unescape)(glob), !!hasMagic, uflag];    }}exports.AST = AST;_a = AST;//# sourceMappingURL=ast.js.map