You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
7.3 KiB
399 lines
7.3 KiB
/*! |
|
* 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; |
|
}
|
|
|