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.
178 lines
3.8 KiB
178 lines
3.8 KiB
/*! |
|
* extglob <https://github.com/jonschlinkert/extglob> |
|
* |
|
* Copyright (c) 2015, Jon Schlinkert. |
|
* Licensed under the MIT License. |
|
*/ |
|
|
|
'use strict'; |
|
|
|
/** |
|
* Module dependencies |
|
*/ |
|
|
|
var isExtglob = require('is-extglob'); |
|
var re, cache = {}; |
|
|
|
/** |
|
* Expose `extglob` |
|
*/ |
|
|
|
module.exports = extglob; |
|
|
|
/** |
|
* Convert the given extglob `string` to a regex-compatible |
|
* string. |
|
* |
|
* ```js |
|
* var extglob = require('extglob'); |
|
* extglob('!(a?(b))'); |
|
* //=> '(?!a(?:b)?)[^/]*?' |
|
* ``` |
|
* |
|
* @param {String} `str` The string to convert. |
|
* @param {Object} `options` |
|
* @option {Boolean} [options] `esc` If `false` special characters will not be escaped. Defaults to `true`. |
|
* @option {Boolean} [options] `regex` If `true` a regular expression is returned instead of a string. |
|
* @return {String} |
|
* @api public |
|
*/ |
|
|
|
|
|
function extglob(str, opts) { |
|
opts = opts || {}; |
|
var o = {}, i = 0; |
|
|
|
// fix common character reversals |
|
// '*!(.js)' => '*.!(js)' |
|
str = str.replace(/!\(([^\w*()])/g, '$1!('); |
|
|
|
// support file extension negation |
|
str = str.replace(/([*\/])\.!\([*]\)/g, function (m, ch) { |
|
if (ch === '/') { |
|
return escape('\\/[^.]+'); |
|
} |
|
return escape('[^.]+'); |
|
}); |
|
|
|
// create a unique key for caching by |
|
// combining the string and options |
|
var key = str |
|
+ String(!!opts.regex) |
|
+ String(!!opts.contains) |
|
+ String(!!opts.escape); |
|
|
|
if (cache.hasOwnProperty(key)) { |
|
return cache[key]; |
|
} |
|
|
|
if (!(re instanceof RegExp)) { |
|
re = regex(); |
|
} |
|
|
|
opts.negate = false; |
|
var m; |
|
|
|
while (m = re.exec(str)) { |
|
var prefix = m[1]; |
|
var inner = m[3]; |
|
if (prefix === '!') { |
|
opts.negate = true; |
|
} |
|
|
|
var id = '__EXTGLOB_' + (i++) + '__'; |
|
// use the prefix of the _last_ (outtermost) pattern |
|
o[id] = wrap(inner, prefix, opts.escape); |
|
str = str.split(m[0]).join(id); |
|
} |
|
|
|
var keys = Object.keys(o); |
|
var len = keys.length; |
|
|
|
// we have to loop again to allow us to convert |
|
// patterns in reverse order (starting with the |
|
// innermost/last pattern first) |
|
while (len--) { |
|
var prop = keys[len]; |
|
str = str.split(prop).join(o[prop]); |
|
} |
|
|
|
var result = opts.regex |
|
? toRegex(str, opts.contains, opts.negate) |
|
: str; |
|
|
|
result = result.split('.').join('\\.'); |
|
|
|
// cache the result and return it |
|
return (cache[key] = result); |
|
} |
|
|
|
/** |
|
* Convert `string` to a regex string. |
|
* |
|
* @param {String} `str` |
|
* @param {String} `prefix` Character that determines how to wrap the string. |
|
* @param {Boolean} `esc` If `false` special characters will not be escaped. Defaults to `true`. |
|
* @return {String} |
|
*/ |
|
|
|
function wrap(inner, prefix, esc) { |
|
if (esc) inner = escape(inner); |
|
|
|
switch (prefix) { |
|
case '!': |
|
return '(?!' + inner + ')[^/]' + (esc ? '%%%~' : '*?'); |
|
case '@': |
|
return '(?:' + inner + ')'; |
|
case '+': |
|
return '(?:' + inner + ')+'; |
|
case '*': |
|
return '(?:' + inner + ')' + (esc ? '%%' : '*') |
|
case '?': |
|
return '(?:' + inner + '|)'; |
|
default: |
|
return inner; |
|
} |
|
} |
|
|
|
function escape(str) { |
|
str = str.split('*').join('[^/]%%%~'); |
|
str = str.split('.').join('\\.'); |
|
return str; |
|
} |
|
|
|
/** |
|
* extglob regex. |
|
*/ |
|
|
|
function regex() { |
|
return /(\\?[@?!+*$]\\?)(\(([^()]*?)\))/; |
|
} |
|
|
|
/** |
|
* Negation regex |
|
*/ |
|
|
|
function negate(str) { |
|
return '(?!^' + str + ').*$'; |
|
} |
|
|
|
/** |
|
* Create the regex to do the matching. If |
|
* the leading character in the `pattern` is `!` |
|
* a negation regex is returned. |
|
* |
|
* @param {String} `pattern` |
|
* @param {Boolean} `contains` Allow loose matching. |
|
* @param {Boolean} `isNegated` True if the pattern is a negation pattern. |
|
*/ |
|
|
|
function toRegex(pattern, contains, isNegated) { |
|
var prefix = contains ? '^' : ''; |
|
var after = contains ? '$' : ''; |
|
pattern = ('(?:' + pattern + ')' + after); |
|
if (isNegated) { |
|
pattern = prefix + negate(pattern); |
|
} |
|
return new RegExp(prefix + pattern); |
|
}
|
|
|