179 lines
3.8 KiB
JavaScript
179 lines
3.8 KiB
JavaScript
|
/*!
|
||
|
* 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);
|
||
|
}
|