"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.expand = expand; const balanced_match_1 = require("@isaacs/balanced-match"); const escSlash = '\0SLASH' + Math.random() + '\0'; const escOpen = '\0OPEN' + Math.random() + '\0'; const escClose = '\0CLOSE' + Math.random() + '\0'; const escComma = '\0COMMA' + Math.random() + '\0'; const escPeriod = '\0PERIOD' + Math.random() + '\0'; const escSlashPattern = new RegExp(escSlash, 'g'); const escOpenPattern = new RegExp(escOpen, 'g'); const escClosePattern = new RegExp(escClose, 'g'); const escCommaPattern = new RegExp(escComma, 'g'); const escPeriodPattern = new RegExp(escPeriod, 'g'); const slashPattern = /\\\\/g; const openPattern = /\\{/g; const closePattern = /\\}/g; const commaPattern = /\\,/g; const periodPattern = /\\./g; function numeric(str) { return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0); } function escapeBraces(str) { return str .replace(slashPattern, escSlash) .replace(openPattern, escOpen) .replace(closePattern, escClose) .replace(commaPattern, escComma) .replace(periodPattern, escPeriod); } function unescapeBraces(str) { return str .replace(escSlashPattern, '\\') .replace(escOpenPattern, '{') .replace(escClosePattern, '}') .replace(escCommaPattern, ',') .replace(escPeriodPattern, '.'); } /** * Basically just str.split(","), but handling cases * where we have nested braced sections, which should be * treated as individual members, like {a,{b,c},d} */ function parseCommaParts(str) { if (!str) { return ['']; } const parts = []; const m = (0, balanced_match_1.balanced)('{', '}', str); if (!m) { return str.split(','); } const { pre, body, post } = m; const p = pre.split(','); p[p.length - 1] += '{' + body + '}'; const postParts = parseCommaParts(post); if (post.length) { ; p[p.length - 1] += postParts.shift(); p.push.apply(p, postParts); } parts.push.apply(parts, p); return parts; } function expand(str) { if (!str) { return []; } // I don't know why Bash 4.3 does this, but it does. // Anything starting with {} will have the first two bytes preserved // but *only* at the top level, so {},a}b will not expand to anything, // but a{},b}c will be expanded to [a}c,abc]. // One could argue that this is a bug in Bash, but since the goal of // this module is to match Bash's rules, we escape a leading {} if (str.slice(0, 2) === '{}') { str = '\\{\\}' + str.slice(2); } return expand_(escapeBraces(str), true).map(unescapeBraces); } function embrace(str) { return '{' + str + '}'; } function isPadded(el) { return /^-?0\d/.test(el); } function lte(i, y) { return i <= y; } function gte(i, y) { return i >= y; } function expand_(str, isTop) { /** @type {string[]} */ const expansions = []; const m = (0, balanced_match_1.balanced)('{', '}', str); if (!m) return [str]; // no need to expand pre, since it is guaranteed to be free of brace-sets const pre = m.pre; const post = m.post.length ? expand_(m.post, false) : ['']; if (/\$$/.test(m.pre)) { for (let k = 0; k < post.length; k++) { const expansion = pre + '{' + m.body + '}' + post[k]; expansions.push(expansion); } } else { const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); const isSequence = isNumericSequence || isAlphaSequence; const isOptions = m.body.indexOf(',') >= 0; if (!isSequence && !isOptions) { // {a},b} if (m.post.match(/,(?!,).*\}/)) { str = m.pre + '{' + m.body + escClose + m.post; return expand_(str); } return [str]; } let n; if (isSequence) { n = m.body.split(/\.\./); } else { n = parseCommaParts(m.body); if (n.length === 1 && n[0] !== undefined) { // x{{a,b}}y ==> x{a}y x{b}y n = expand_(n[0], false).map(embrace); //XXX is this necessary? Can't seem to hit it in tests. /* c8 ignore start */ if (n.length === 1) { return post.map(p => m.pre + n[0] + p); } /* c8 ignore stop */ } } // at this point, n is the parts, and we know it's not a comma set // with a single entry. let N; if (isSequence && n[0] !== undefined && n[1] !== undefined) { const x = numeric(n[0]); const y = numeric(n[1]); const width = Math.max(n[0].length, n[1].length); let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1; let test = lte; const reverse = y < x; if (reverse) { incr *= -1; test = gte; } const pad = n.some(isPadded); N = []; for (let i = x; test(i, y); i += incr) { let c; if (isAlphaSequence) { c = String.fromCharCode(i); if (c === '\\') { c = ''; } } else { c = String(i); if (pad) { const need = width - c.length; if (need > 0) { const z = new Array(need + 1).join('0'); if (i < 0) { c = '-' + z + c.slice(1); } else { c = z + c; } } } } N.push(c); } } else { N = []; for (let j = 0; j < n.length; j++) { N.push.apply(N, expand_(n[j], false)); } } for (let j = 0; j < N.length; j++) { for (let k = 0; k < post.length; k++) { const expansion = pre + N[j] + post[k]; if (!isTop || isSequence || expansion) { expansions.push(expansion); } } } } return expansions; } //# sourceMappingURL=index.js.map