400 lines
7.3 KiB
JavaScript
400 lines
7.3 KiB
JavaScript
|
/*!
|
||
|
* braces <https://github.com/jonschlinkert/braces>
|
||
|
*
|
||
|
* Copyright (c) 2014-2015, Jon Schlinkert.
|
||
|
* Licensed under the MIT license.
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
/**
|
||
|
* Module dependencies
|
||
|
*/
|
||
|
|
||
|
var expand = require('expand-range');
|
||
|
var repeat = require('repeat-element');
|
||
|
var tokens = require('preserve');
|
||
|
|
||
|
/**
|
||
|
* Expose `braces`
|
||
|
*/
|
||
|
|
||
|
module.exports = function(str, options) {
|
||
|
if (typeof str !== 'string') {
|
||
|
throw new Error('braces expects a string');
|
||
|
}
|
||
|
return braces(str, options);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Expand `{foo,bar}` or `{1..5}` braces in the
|
||
|
* given `string`.
|
||
|
*
|
||
|
* @param {String} `str`
|
||
|
* @param {Array} `arr`
|
||
|
* @param {Object} `options`
|
||
|
* @return {Array}
|
||
|
*/
|
||
|
|
||
|
function braces(str, arr, options) {
|
||
|
if (str === '') {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
if (!Array.isArray(arr)) {
|
||
|
options = arr;
|
||
|
arr = [];
|
||
|
}
|
||
|
|
||
|
var opts = options || {};
|
||
|
arr = arr || [];
|
||
|
|
||
|
if (typeof opts.nodupes === 'undefined') {
|
||
|
opts.nodupes = true;
|
||
|
}
|
||
|
|
||
|
var fn = opts.fn;
|
||
|
var es6;
|
||
|
|
||
|
if (typeof opts === 'function') {
|
||
|
fn = opts;
|
||
|
opts = {};
|
||
|
}
|
||
|
|
||
|
if (!(patternRe instanceof RegExp)) {
|
||
|
patternRe = patternRegex();
|
||
|
}
|
||
|
|
||
|
var matches = str.match(patternRe) || [];
|
||
|
var m = matches[0];
|
||
|
|
||
|
switch(m) {
|
||
|
case '\\,':
|
||
|
return escapeCommas(str, arr, opts);
|
||
|
case '\\.':
|
||
|
return escapeDots(str, arr, opts);
|
||
|
case '\/.':
|
||
|
return escapePaths(str, arr, opts);
|
||
|
case ' ':
|
||
|
return splitWhitespace(str);
|
||
|
case '{,}':
|
||
|
return exponential(str, opts, braces);
|
||
|
case '{}':
|
||
|
return emptyBraces(str, arr, opts);
|
||
|
case '\\{':
|
||
|
case '\\}':
|
||
|
return escapeBraces(str, arr, opts);
|
||
|
case '${':
|
||
|
if (!/\{[^{]+\{/.test(str)) {
|
||
|
return arr.concat(str);
|
||
|
} else {
|
||
|
es6 = true;
|
||
|
str = tokens.before(str, es6Regex());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(braceRe instanceof RegExp)) {
|
||
|
braceRe = braceRegex();
|
||
|
}
|
||
|
|
||
|
var match = braceRe.exec(str);
|
||
|
if (match == null) {
|
||
|
return [str];
|
||
|
}
|
||
|
|
||
|
var outter = match[1];
|
||
|
var inner = match[2];
|
||
|
if (inner === '') { return [str]; }
|
||
|
|
||
|
var segs, segsLength;
|
||
|
|
||
|
if (inner.indexOf('..') !== -1) {
|
||
|
segs = expand(inner, opts, fn) || inner.split(',');
|
||
|
segsLength = segs.length;
|
||
|
|
||
|
} else if (inner[0] === '"' || inner[0] === '\'') {
|
||
|
return arr.concat(str.split(/['"]/).join(''));
|
||
|
|
||
|
} else {
|
||
|
segs = inner.split(',');
|
||
|
if (opts.makeRe) {
|
||
|
return braces(str.replace(outter, wrap(segs, '|')), opts);
|
||
|
}
|
||
|
|
||
|
segsLength = segs.length;
|
||
|
if (segsLength === 1 && opts.bash) {
|
||
|
segs[0] = wrap(segs[0], '\\');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var len = segs.length;
|
||
|
var i = 0, val;
|
||
|
|
||
|
while (len--) {
|
||
|
var path = segs[i++];
|
||
|
|
||
|
if (/(\.[^.\/])/.test(path)) {
|
||
|
if (segsLength > 1) {
|
||
|
return segs;
|
||
|
} else {
|
||
|
return [str];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
val = splice(str, outter, path);
|
||
|
|
||
|
if (/\{[^{}]+?\}/.test(val)) {
|
||
|
arr = braces(val, arr, opts);
|
||
|
} else if (val !== '') {
|
||
|
if (opts.nodupes && arr.indexOf(val) !== -1) { continue; }
|
||
|
arr.push(es6 ? tokens.after(val) : val);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (opts.strict) { return filter(arr, filterEmpty); }
|
||
|
return arr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Expand exponential ranges
|
||
|
*
|
||
|
* `a{,}{,}` => ['a', 'a', 'a', 'a']
|
||
|
*/
|
||
|
|
||
|
function exponential(str, options, fn) {
|
||
|
if (typeof options === 'function') {
|
||
|
fn = options;
|
||
|
options = null;
|
||
|
}
|
||
|
|
||
|
var opts = options || {};
|
||
|
var esc = '__ESC_EXP__';
|
||
|
var exp = 0;
|
||
|
var res;
|
||
|
|
||
|
var parts = str.split('{,}');
|
||
|
if (opts.nodupes) {
|
||
|
return fn(parts.join(''), opts);
|
||
|
}
|
||
|
|
||
|
exp = parts.length - 1;
|
||
|
res = fn(parts.join(esc), opts);
|
||
|
var len = res.length;
|
||
|
var arr = [];
|
||
|
var i = 0;
|
||
|
|
||
|
while (len--) {
|
||
|
var ele = res[i++];
|
||
|
var idx = ele.indexOf(esc);
|
||
|
|
||
|
if (idx === -1) {
|
||
|
arr.push(ele);
|
||
|
|
||
|
} else {
|
||
|
ele = ele.split('__ESC_EXP__').join('');
|
||
|
if (!!ele && opts.nodupes !== false) {
|
||
|
arr.push(ele);
|
||
|
|
||
|
} else {
|
||
|
var num = Math.pow(2, exp);
|
||
|
arr.push.apply(arr, repeat(ele, num));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return arr;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Wrap a value with parens, brackets or braces,
|
||
|
* based on the given character/separator.
|
||
|
*
|
||
|
* @param {String|Array} `val`
|
||
|
* @param {String} `ch`
|
||
|
* @return {String}
|
||
|
*/
|
||
|
|
||
|
function wrap(val, ch) {
|
||
|
if (ch === '|') {
|
||
|
return '(' + val.join(ch) + ')';
|
||
|
}
|
||
|
if (ch === ',') {
|
||
|
return '{' + val.join(ch) + '}';
|
||
|
}
|
||
|
if (ch === '-') {
|
||
|
return '[' + val.join(ch) + ']';
|
||
|
}
|
||
|
if (ch === '\\') {
|
||
|
return '\\{' + val + '\\}';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle empty braces: `{}`
|
||
|
*/
|
||
|
|
||
|
function emptyBraces(str, arr, opts) {
|
||
|
return braces(str.split('{}').join('\\{\\}'), arr, opts);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Filter out empty-ish values
|
||
|
*/
|
||
|
|
||
|
function filterEmpty(ele) {
|
||
|
return !!ele && ele !== '\\';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle patterns with whitespace
|
||
|
*/
|
||
|
|
||
|
function splitWhitespace(str) {
|
||
|
var segs = str.split(' ');
|
||
|
var len = segs.length;
|
||
|
var res = [];
|
||
|
var i = 0;
|
||
|
|
||
|
while (len--) {
|
||
|
res.push.apply(res, braces(segs[i++]));
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle escaped braces: `\\{foo,bar}`
|
||
|
*/
|
||
|
|
||
|
function escapeBraces(str, arr, opts) {
|
||
|
if (!/\{[^{]+\{/.test(str)) {
|
||
|
return arr.concat(str.split('\\').join(''));
|
||
|
} else {
|
||
|
str = str.split('\\{').join('__LT_BRACE__');
|
||
|
str = str.split('\\}').join('__RT_BRACE__');
|
||
|
return map(braces(str, arr, opts), function(ele) {
|
||
|
ele = ele.split('__LT_BRACE__').join('{');
|
||
|
return ele.split('__RT_BRACE__').join('}');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle escaped dots: `{1\\.2}`
|
||
|
*/
|
||
|
|
||
|
function escapeDots(str, arr, opts) {
|
||
|
if (!/[^\\]\..+\\\./.test(str)) {
|
||
|
return arr.concat(str.split('\\').join(''));
|
||
|
} else {
|
||
|
str = str.split('\\.').join('__ESC_DOT__');
|
||
|
return map(braces(str, arr, opts), function(ele) {
|
||
|
return ele.split('__ESC_DOT__').join('.');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle escaped dots: `{1\\.2}`
|
||
|
*/
|
||
|
|
||
|
function escapePaths(str, arr, opts) {
|
||
|
str = str.split('\/.').join('__ESC_PATH__');
|
||
|
return map(braces(str, arr, opts), function(ele) {
|
||
|
return ele.split('__ESC_PATH__').join('\/.');
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle escaped commas: `{a\\,b}`
|
||
|
*/
|
||
|
|
||
|
function escapeCommas(str, arr, opts) {
|
||
|
if (!/\w,/.test(str)) {
|
||
|
return arr.concat(str.split('\\').join(''));
|
||
|
} else {
|
||
|
str = str.split('\\,').join('__ESC_COMMA__');
|
||
|
return map(braces(str, arr, opts), function(ele) {
|
||
|
return ele.split('__ESC_COMMA__').join(',');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Regex for common patterns
|
||
|
*/
|
||
|
|
||
|
function patternRegex() {
|
||
|
return /\${|( (?=[{,}])|(?=[{,}]) )|{}|{,}|\\,(?=.*[{}])|\/\.(?=.*[{}])|\\\.(?={)|\\{|\\}/;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Braces regex.
|
||
|
*/
|
||
|
|
||
|
function braceRegex() {
|
||
|
return /.*(\\?\{([^}]+)\})/;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* es6 delimiter regex.
|
||
|
*/
|
||
|
|
||
|
function es6Regex() {
|
||
|
return /\$\{([^}]+)\}/;
|
||
|
}
|
||
|
|
||
|
var braceRe;
|
||
|
var patternRe;
|
||
|
|
||
|
/**
|
||
|
* Faster alternative to `String.replace()` when the
|
||
|
* index of the token to be replaces can't be supplied
|
||
|
*/
|
||
|
|
||
|
function splice(str, token, replacement) {
|
||
|
var i = str.indexOf(token);
|
||
|
return str.substr(0, i) + replacement
|
||
|
+ str.substr(i + token.length);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fast array map
|
||
|
*/
|
||
|
|
||
|
function map(arr, fn) {
|
||
|
if (arr == null) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
var len = arr.length;
|
||
|
var res = new Array(len);
|
||
|
var i = -1;
|
||
|
|
||
|
while (++i < len) {
|
||
|
res[i] = fn(arr[i], i, arr);
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fast array filter
|
||
|
*/
|
||
|
|
||
|
function filter(arr, cb) {
|
||
|
if (arr == null) return [];
|
||
|
if (typeof cb !== 'function') {
|
||
|
throw new TypeError('braces: filter expects a callback function.');
|
||
|
}
|
||
|
|
||
|
var len = arr.length;
|
||
|
var res = arr.slice();
|
||
|
var i = 0;
|
||
|
|
||
|
while (len--) {
|
||
|
if (!cb(arr[len], i++)) {
|
||
|
res.splice(len, 1);
|
||
|
}
|
||
|
}
|
||
|
return res;
|
||
|
}
|