Comparing version 0.1.3 to 0.2.0
329
index.js
'use strict'; | ||
/** | ||
* Module dependencies | ||
*/ | ||
var path = require('path'); | ||
@@ -7,6 +11,12 @@ var toRegex = require('to-regex'); | ||
var debug = require('debug')('nanomatch'); | ||
var extend = require('extend-shallow'); | ||
/** | ||
* Local dependencies | ||
*/ | ||
var compilers = require('./lib/compilers'); | ||
var parsers = require('./lib/parsers'); | ||
var cache = require('./lib/cache'); | ||
var utils = require('./lib/utils'); | ||
var cache = require('./lib/cache'); | ||
var MAX_LENGTH = 1024 * 64; | ||
@@ -45,3 +55,2 @@ | ||
var opts = utils.extend({}, options); | ||
var negated = false; | ||
@@ -56,6 +65,6 @@ var omit = []; | ||
if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) { | ||
omit.push.apply(omit, nanomatch.match(list, pattern.slice(1), opts)); | ||
omit.push.apply(omit, nanomatch.match(list, pattern.slice(1), options)); | ||
negated = true; | ||
} else { | ||
keep.push.apply(keep, nanomatch.match(list, pattern, opts)); | ||
keep.push.apply(keep, nanomatch.match(list, pattern, options)); | ||
} | ||
@@ -66,10 +75,23 @@ } | ||
if (negated && keep.length === 0) { | ||
keep = list; | ||
keep = list.map(utils.unixify(options)); | ||
} | ||
var matches = utils.diff(keep, omit); | ||
return opts.nodupes !== false ? utils.unique(matches) : matches; | ||
if (!options || options.nodupes !== false) { | ||
return utils.unique(matches); | ||
} | ||
return matches; | ||
} | ||
/** | ||
* Cache | ||
*/ | ||
nanomatch.cache = cache; | ||
nanomatch.clearCache = function() { | ||
nanomatch.cache.__data__ = {}; | ||
}; | ||
/** | ||
* Similar to the main function, but `pattern` must be a string. | ||
@@ -90,31 +112,25 @@ * | ||
nanomatch.match = function(list, pattern, options) { | ||
debug('match <%s>', pattern); | ||
var unixify = utils.unixify(options); | ||
var isMatch = nanomatch.matcher(pattern, options); | ||
list = utils.arrayify(list); | ||
var unixify = utils.unixify(options); | ||
var isMatch = memoize('match', pattern, options, nanomatch.matcher); | ||
var matches = []; | ||
var len = list.length; | ||
var idx = -1; | ||
var matches = []; | ||
while (++idx < len) { | ||
var file = list[idx]; | ||
var ele = list[idx]; | ||
if (file === pattern) { | ||
matches.push(file); | ||
if (ele === pattern) { | ||
matches.push(unixify(ele)); | ||
continue; | ||
} | ||
var unix = unixify(file); | ||
if (unix === pattern) { | ||
matches.push(options && options.unixify ? unix : file); | ||
continue; | ||
var unix = unixify(ele); | ||
if (unix === pattern || isMatch(unix)) { | ||
matches.push(unix); | ||
} | ||
if (isMatch(unix)) { | ||
matches.push(options && options.unixify ? unix : file); | ||
} | ||
} | ||
// if not options were passed, return now | ||
// if no options were passed, uniquify results and return | ||
if (typeof options === 'undefined') { | ||
@@ -124,9 +140,8 @@ return utils.unique(matches); | ||
var opts = utils.extend({}, options); | ||
if (matches.length === 0) { | ||
if (opts.failglob === true) { | ||
if (options.failglob === true) { | ||
throw new Error('no matches found for "' + pattern + '"'); | ||
} | ||
if (opts.nonull === true || opts.nullglob === true) { | ||
return [opts.unescape ? utils.unescape(pattern) : pattern]; | ||
if (options.nonull === true || options.nullglob === true) { | ||
return [options.unescape ? utils.unescape(pattern) : pattern]; | ||
} | ||
@@ -136,71 +151,10 @@ } | ||
// if `opts.ignore` was defined, diff ignored list | ||
if (opts.ignore) { | ||
matches = nanomatch.not(matches, opts.ignore, opts); | ||
if (options.ignore) { | ||
matches = nanomatch.not(matches, options.ignore, options); | ||
} | ||
return opts.nodupes !== false ? utils.unique(matches) : matches; | ||
return options.nodupes !== false ? utils.unique(matches) : matches; | ||
}; | ||
/** | ||
* Creates a matcher function from the given glob `pattern` and `options`. The returned | ||
* function takes a string to match as its only argument. | ||
* | ||
* ```js | ||
* var nanomatch = require('nanomatch'); | ||
* var isMatch = nanomatch.matcher('*.!(*a)'); | ||
* | ||
* console.log(isMatch('a.a')); | ||
* //=> false | ||
* console.log(isMatch('a.b')); | ||
* //=> true | ||
* ``` | ||
* @param {String} `pattern` Glob pattern | ||
* @param {String} `options` | ||
* @return {Function} Returns a matcher function. | ||
* @api public | ||
*/ | ||
nanomatch.matcher = function(pattern, options) { | ||
function matcher() { | ||
if (typeof pattern === 'function') { | ||
return pattern; | ||
} | ||
var opts = utils.extend({}, options); | ||
var unixify = utils.unixify(opts); | ||
// pattern is a regex | ||
if (pattern instanceof RegExp) { | ||
return function(fp) { | ||
return pattern.test(unixify(fp)); | ||
}; | ||
} | ||
if (typeof pattern !== 'string') { | ||
throw new TypeError('expected pattern to be a string, regex or function'); | ||
} | ||
// pattern is a non-glob string | ||
if (!utils.hasSpecialChars(pattern)) { | ||
return utils.matchPath(unixify(pattern), opts); | ||
} | ||
// pattern is a glob string | ||
var re = nanomatch.makeRe(pattern, options); | ||
// `options.matchBase` or `options.basename` is defined | ||
if (nanomatch.matchBase(pattern, options)) { | ||
return utils.matchBasename(re); | ||
} | ||
// everything else... | ||
return function(fp) { | ||
return re.test(unixify(fp)); | ||
}; | ||
} | ||
return memoize('matcher', pattern, options, matcher); | ||
}; | ||
/** | ||
* Returns true if the specified `string` matches the given glob `pattern`. | ||
@@ -223,18 +177,10 @@ * | ||
nanomatch.isMatch = function(str, pattern, options) { | ||
if (typeof str === 'undefined') { | ||
throw new TypeError('expected a string'); | ||
if (pattern === str) { | ||
return true; | ||
} | ||
if (typeof pattern === 'undefined') { | ||
throw new TypeError('expected pattern to be a string, regex or function'); | ||
} | ||
if (pattern === '' || pattern === ' ') { | ||
if (pattern === '' || pattern === ' ' || pattern === '/' || pattern === '.') { | ||
return str === pattern; | ||
} | ||
if (options && nanomatch.matchBase(pattern, options)) { | ||
str = path.basename(str); | ||
} | ||
return nanomatch.matcher(pattern, options)(str); | ||
@@ -259,12 +205,17 @@ }; | ||
nanomatch.not = function(list, patterns, options) { | ||
var opts = utils.extend({}, options); | ||
var opts = extend({}, options); | ||
var ignore = opts.ignore; | ||
delete opts.ignore; | ||
var matches = utils.diff(list.slice(), nanomatch(list, patterns, opts)); | ||
var unixify = utils.unixify(opts); | ||
var unixified = list.map(function(fp) { | ||
return unixify(fp, opts); | ||
}); | ||
var matches = utils.diff(unixified, nanomatch(unixified, patterns, opts)); | ||
if (ignore) { | ||
return utils.diff(matches, nanomatch(list, ignore)); | ||
matches = utils.diff(matches, nanomatch(unixified, ignore)); | ||
} | ||
return matches; | ||
return opts.nodupes !== false ? utils.unique(matches) : matches; | ||
}; | ||
@@ -290,29 +241,5 @@ | ||
nanomatch.any = function(str, patterns, options) { | ||
if (typeof patterns === 'string') { | ||
patterns = [patterns]; | ||
} | ||
if (!Array.isArray(patterns)) { | ||
throw new TypeError('expected patterns to be a string or array'); | ||
} | ||
debug('match <%s>', patterns); | ||
var unixify = utils.unixify(opts); | ||
var opts = utils.extend({}, options); | ||
str = unixify(str); | ||
var len = patterns.length; | ||
for (var i = 0; i < len; i++) { | ||
var pattern = patterns[i]; | ||
if (!utils.isGlob(pattern)) { | ||
if (str === pattern) { | ||
return true; | ||
} | ||
if (opts.contains && str.indexOf(pattern) !== -1) { | ||
return true; | ||
} | ||
continue; | ||
} | ||
if (nanomatch.isMatch(str, pattern, opts)) { | ||
patterns = utils.arrayify(patterns); | ||
for (var i = 0; i < patterns.length; i++) { | ||
if (nanomatch.isMatch(str, patterns[i], options)) { | ||
return true; | ||
@@ -343,20 +270,11 @@ } | ||
nanomatch.contains = function(str, pattern, options) { | ||
if (typeof str !== 'string') { | ||
throw new TypeError('expected a string'); | ||
if (pattern === '' || pattern === ' ') { | ||
return pattern === str; | ||
} | ||
if (typeof pattern !== 'string') { | ||
throw new TypeError('expected pattern to be a string'); | ||
} | ||
var opts = utils.extend({contains: pattern !== ''}, options); | ||
var opts = extend({}, options, {contains: true}); | ||
opts.strictClose = false; | ||
opts.strictOpen = false; | ||
if (opts.contains && !utils.isGlob(pattern)) { | ||
str = utils.unixify(opts)(str); | ||
return str.indexOf(pattern) !== -1; | ||
} | ||
return nanomatch.matcher(pattern, opts)(str); | ||
return nanomatch.match(str, pattern, opts).length > 0; | ||
}; | ||
@@ -401,2 +319,62 @@ | ||
/** | ||
* Creates a matcher function from the given glob `pattern` and `options`. The returned | ||
* function takes a string to match as its only argument. | ||
* | ||
* ```js | ||
* var nanomatch = require('nanomatch'); | ||
* var isMatch = nanomatch.matcher('*.!(*a)'); | ||
* | ||
* console.log(isMatch('a.a')); | ||
* //=> false | ||
* console.log(isMatch('a.b')); | ||
* //=> true | ||
* ``` | ||
* @param {String} `pattern` Glob pattern | ||
* @param {String} `options` | ||
* @return {Function} Returns a matcher function. | ||
* @api public | ||
*/ | ||
nanomatch.matcher = function(pattern, options) { | ||
function matcher() { | ||
var unixify = utils.unixify(options); | ||
// if pattern is a regex | ||
if (pattern instanceof RegExp) { | ||
return function(str) { | ||
return pattern.test(str) || pattern.test(unixify(str)); | ||
}; | ||
} | ||
// if pattern is invalid | ||
if (!utils.isString(pattern)) { | ||
throw new TypeError('expected pattern to be a string or regex'); | ||
} | ||
// if pattern is a non-glob string | ||
if (!utils.hasSpecialChars(pattern)) { | ||
if (options && options.nocase === true) { | ||
pattern = pattern.toLowerCase(); | ||
} | ||
return utils.matchPath(pattern, options); | ||
} | ||
// if pattern is a glob string | ||
var re = nanomatch.makeRe(pattern, options); | ||
// if `options.matchBase` or `options.basename` is defined | ||
if (nanomatch.matchBase(pattern, options)) { | ||
return utils.matchBasename(re, options); | ||
} | ||
// everything else... | ||
return function(str) { | ||
return re.test(str) || re.test(unixify(str)); | ||
}; | ||
} | ||
return memoize('matcher', pattern, options, matcher); | ||
}; | ||
/** | ||
* Create a regular expression from the given glob `pattern`. | ||
@@ -420,2 +398,6 @@ * | ||
if (typeof pattern !== 'string') { | ||
throw new TypeError('expected pattern to be a string'); | ||
} | ||
if (pattern.length > MAX_LENGTH) { | ||
@@ -426,4 +408,5 @@ throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); | ||
function makeRe() { | ||
var res = nanomatch.create(pattern, options); | ||
var opts = utils.extend({strictErrors: false, wrap: false}, options); | ||
var opts = extend({strictErrors: false}, options); | ||
if (opts.strictErrors === true) opts.strict = true; | ||
var res = nanomatch.create(pattern, opts); | ||
return toRegex(res.output, opts); | ||
@@ -478,13 +461,22 @@ } | ||
nanomatch.create = function(pattern, options) { | ||
debug('nanomatch.create <%s>', pattern); | ||
options = options || {}; | ||
if (typeof pattern !== 'string') { | ||
throw new TypeError('expected a string'); | ||
} | ||
var snapdragon = options.snapdragon || new Snapdragon(options); | ||
compilers(snapdragon); | ||
parsers(snapdragon); | ||
function create() { | ||
var snapdragon = (options && options.snapdragon) || new Snapdragon(options); | ||
compilers(snapdragon); | ||
parsers(snapdragon); | ||
var ast = snapdragon.parse(pattern, options); | ||
ast.input = pattern; | ||
var res = snapdragon.compile(ast, options); | ||
return res; | ||
if (pattern.slice(0, 2) === './') { | ||
pattern = pattern.slice(2); | ||
} | ||
pattern = utils.combineDuplicates(pattern, '\\*\\*\\/|\\/\\*\\*'); | ||
var ast = snapdragon.parse(pattern, options); | ||
ast.input = pattern; | ||
return snapdragon.compile(ast, options); | ||
} | ||
return memoize('create', pattern, options, create); | ||
}; | ||
@@ -497,7 +489,4 @@ | ||
function memoize(type, pattern, options, fn) { | ||
if (!utils.isString(pattern)) { | ||
return fn(pattern, options); | ||
} | ||
var key = utils.createKey(type + pattern, options); | ||
var key = createKey(pattern, options); | ||
if (cache.has(type, key)) { | ||
@@ -512,3 +501,2 @@ return cache.get(type, key); | ||
val.key = key; | ||
cache.set(type, key, val); | ||
@@ -519,21 +507,2 @@ return val; | ||
/** | ||
* Create the key to use for memoization. The key is generated | ||
* by iterating over the options and concatenating key-value pairs | ||
* to the pattern string. | ||
*/ | ||
function createKey(pattern, options) { | ||
var key = pattern; | ||
if (typeof options === 'undefined') { | ||
return key; | ||
} | ||
for (var prop in options) { | ||
if (options.hasOwnProperty(prop)) { | ||
key += ';' + prop + '=' + String(options[prop]); | ||
} | ||
} | ||
return key; | ||
} | ||
/** | ||
* Expose parser, compiler and constructor on `nanomatch` | ||
@@ -540,0 +509,0 @@ */ |
@@ -8,4 +8,5 @@ 'use strict'; | ||
module.exports = function(nanomatch) { | ||
var star = '[^\\/]*?'; | ||
var star = '[^\\\\/]*?'; | ||
nanomatch.state = nanomatch.state || {}; | ||
nanomatch.compiler | ||
@@ -18,7 +19,12 @@ | ||
.set('not', function(node) { | ||
if (this.options.nonegate) { | ||
return this.emit('\\' + node.val, node); | ||
} | ||
return this.emit(node.val, node); | ||
}) | ||
.set('escape', function(node) { | ||
var val = this.options.unescape ? node.ch : node.val; | ||
return this.emit(val, node); | ||
if (this.options.unescape && /[\w_.-]/.test(node.val)) { | ||
return this.emit(node.val, node); | ||
} | ||
return this.emit('\\' + node.val, node); | ||
}) | ||
@@ -31,2 +37,5 @@ | ||
.set('dollar', function(node) { | ||
if (node.parent.type === 'bracket') { | ||
return this.emit(node.val, node); | ||
} | ||
return this.emit('\\' + node.val, node); | ||
@@ -40,5 +49,3 @@ }) | ||
.set('dot', function(node) { | ||
if (node.dotfiles === true) { | ||
this.dotfiles = true; | ||
} | ||
if (node.dotfiles === true) this.dotfiles = true; | ||
return this.emit('\\' + node.val, node); | ||
@@ -51,10 +58,16 @@ }) | ||
.set('backslash', function(node) { | ||
return this.emit(node.val, node); | ||
}) | ||
.set('slash', function(node, nodes, i) { | ||
var parsed = node.parsed; | ||
if (parsed === '**') { | ||
// word boundary | ||
if (node.rest.slice(0, 2) === '\\b') { | ||
return this.emit(node.val, node); | ||
} | ||
if (node.parsed === '**' || node.parsed === './**') { | ||
this.output = '(' + this.output; | ||
return this.emit('\\/)?', node); | ||
return this.emit('\\/)*', node); | ||
} | ||
if (parsed === '!**') { | ||
return this.emit('\\/?', node); | ||
if (node.parsed === '!**') { | ||
return this.emit('\\/*', node); | ||
} | ||
@@ -71,11 +84,7 @@ return this.emit('\\/', node); | ||
var open = !node.escaped ? '[' : '\\['; | ||
var prefix = node.prefix; | ||
var negated = node.negated; | ||
var inner = node.inner; | ||
var val = node.val; | ||
var len = inner.length; | ||
if (inner !== '\\\\' && len > 1) { | ||
inner = inner.replace(/\\/, '\\\\'); | ||
} | ||
inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\'); | ||
if (inner === ']-') { | ||
@@ -85,10 +94,10 @@ inner = '\\]\\-'; | ||
if (prefix && inner.indexOf('.') === -1) { | ||
if (negated && inner.indexOf('.') === -1) { | ||
inner += '.'; | ||
} | ||
if (prefix && inner.indexOf('/') === -1) { | ||
if (negated && inner.indexOf('/') === -1) { | ||
inner += '/'; | ||
} | ||
val = open + prefix + inner + close; | ||
val = open + negated + inner + close; | ||
return this.emit(val, node); | ||
@@ -111,5 +120,13 @@ }) | ||
.set('qmark', function(node) { | ||
var val = this.options.dot ? '[^/\\\\]' : '[^/.\\\\]'; | ||
var val = this.options.dot ? '[^\\\\/]' : '[^.\\\\/]'; | ||
var prev = this.prev(); | ||
if (node.parsed.slice(-1) === '(') { | ||
var ch = node.rest.charAt(0); | ||
if (ch !== '!' && ch !== '=' && ch !== ':') { | ||
return this.emit(val, node); | ||
} | ||
return this.emit(node.val, node); | ||
} | ||
if (prev.type === 'text' && prev.val) { | ||
@@ -135,3 +152,3 @@ return this.emit(val, node); | ||
var prev = node.parsed.slice(-1); | ||
if ((prev === ']' || prev === ')')) { | ||
if (prev === ']' || prev === ')') { | ||
return this.emit(node.val, node); | ||
@@ -174,4 +191,2 @@ } | ||
this.output += '?'; | ||
} else if (!node.rest) { | ||
} | ||
@@ -195,2 +210,11 @@ | ||
var isBracket = this.output.slice(-1) === ']' | ||
&& prev.type === 'bracket' | ||
&& prev.escaped !== true | ||
&& next.type === 'eos'; | ||
if (isBracket) { | ||
return this.emit('*?', node); | ||
} | ||
var prefix = !this.dotfiles && type !== 'text' && type !== 'escape' | ||
@@ -202,3 +226,3 @@ ? (this.options.dot ? '(?!(?:^|\\/)\\.{1,2}(?:$|\\/))(?=.)' : '(?!\\.)') | ||
if (prefix !== '(?!\\.)' && isStart && type !== 'dot') { | ||
prefix += '(?!\\.([.\/]|$))'; | ||
prefix += '(?!\\.([./]|$))'; | ||
} | ||
@@ -215,2 +239,5 @@ | ||
if (node.rest === '') { | ||
val += '\\/?'; | ||
} | ||
return this.emit(val, node); | ||
@@ -224,15 +251,9 @@ }) | ||
.set('text', function(node) { | ||
return this.emit(node.val, node); | ||
}) | ||
/** | ||
* end-of-string | ||
*/ | ||
.set('eos', function(node) { | ||
if (typeof this.ast.strict === 'undefined' && this.options.strictOpen !== true) { | ||
this.output = '(\\.[\\\\\\/])?' + this.output; | ||
var val = node.val; | ||
if (node.rest === '' && nanomatch.state.metachar) { | ||
val += '\\/?'; | ||
} | ||
return this.emit(node.val, node); | ||
return this.emit(val, node); | ||
}); | ||
}; | ||
'use strict'; | ||
var regex = require('regex-not'); | ||
var regexNot = require('regex-not'); | ||
var toRegex = require('to-regex'); | ||
var isOdd = require('is-odd'); | ||
/** | ||
* Negation regex cache | ||
*/ | ||
var cache = {}; | ||
/** | ||
* Characters to use in negation regex (we want to "not" match | ||
@@ -17,11 +12,7 @@ * characters that are matched by other parsers) | ||
var NOT_REGEX = '[!*+?$\\[/.\\\\]+'; | ||
var cached; | ||
var NOT_REGEX = '[!*+?$^.\\\\/\\[]+'; | ||
var not = createTextRegex(NOT_REGEX); | ||
/** | ||
* Regex | ||
*/ | ||
var not = createRegex(NOT_REGEX); | ||
/** | ||
* Nanomatch parsers | ||
@@ -31,2 +22,3 @@ */ | ||
module.exports = function(nanomatch) { | ||
nanomatch.state = nanomatch.state || {}; | ||
nanomatch.parser | ||
@@ -44,14 +36,8 @@ | ||
var val = '\\.\\/'; | ||
var strict = true; | ||
this.ast.strictOpen = !!this.options.strictOpen; | ||
this.ast.addPrefix = true; | ||
if (this.options.strictOpen === false) { | ||
val = '(\\.\\/|^)'; | ||
strict = false; | ||
} | ||
this.ast.strictOpen = strict; | ||
return pos({ | ||
type: 'bos', | ||
val: val | ||
val: '' | ||
}); | ||
@@ -65,11 +51,10 @@ }) | ||
.capture('escape', function() { | ||
if (this.isInside('bracket')) return; | ||
var pos = this.position(); | ||
var m = this.match(/^\\(.)/); | ||
var m = this.match(/^(?:\\(.)|([$^]))/); | ||
if (!m) return; | ||
var ch = !/[*?+]/.test(m[1]) ? m[1] : m[0]; | ||
return pos({ | ||
type: 'escape', | ||
val: m[0], | ||
ch: ch | ||
val: m[2] || m[1] | ||
}); | ||
@@ -85,6 +70,6 @@ }) | ||
var pos = this.position(); | ||
var m = this.match(/^\!+/); | ||
var m = this.match(this.notRegex || /^\!+/); | ||
if (!m) return; | ||
var val = m[0]; | ||
var val = m[0]; | ||
var isNegated = isOdd(val.length); | ||
@@ -130,13 +115,35 @@ if (parsed === '' && !isNegated) { | ||
.capture('plus', /^\+/) | ||
.capture('qmark', /^\?+/) | ||
/** | ||
* Plus: "+" | ||
*/ | ||
.capture('plus', /^\+(?!\()/) | ||
/** | ||
* Question mark: "?" | ||
*/ | ||
.capture('qmark', function() { | ||
var parsed = this.parsed; | ||
var pos = this.position(); | ||
var m = this.match(/^\?+(?!\()/); | ||
if (!m) return; | ||
nanomatch.state.metachar = true; | ||
return pos({ | ||
type: 'qmark', | ||
parsed: parsed, | ||
val: m[0] | ||
}); | ||
}) | ||
/** | ||
* Globstar: "**" | ||
*/ | ||
.capture('globstar', function() { | ||
var pos = this.position(); | ||
var m = this.match(/^\*{2}(?!\*)(?=\/|$)/); | ||
var m = this.match(/^\*{2}(?![*(])(?=[,\/)]|$)/); | ||
if (!m) return; | ||
while (this.input.slice(0, 4) === '/**/') { | ||
this.input = this.input.slice(4); | ||
} | ||
nanomatch.state.metachar = true; | ||
return pos({ | ||
@@ -147,7 +154,57 @@ type: 'globstar', | ||
}) | ||
.capture('star', /^(?:\*(?!\*)|[*]{3,}|[*]{2}(?!\/|$))/) | ||
/** | ||
* Star: "*" | ||
*/ | ||
.capture('star', function() { | ||
var pos = this.position(); | ||
var m = this.match(/^(?:\*(?![*(])|[*]{3,}(?!\()|[*]{2}(?![(\/]|$)|\*(?=\*\())/); | ||
if (!m) return; | ||
nanomatch.state.metachar = true; | ||
return pos({ | ||
type: 'star', | ||
val: m[0] | ||
}); | ||
}) | ||
/** | ||
* Slash: "/" | ||
*/ | ||
.capture('slash', /^\//) | ||
/** | ||
* Backslash: "\\" | ||
*/ | ||
.capture('backslash', function() { | ||
if (this.isInside('bracket')) return; | ||
var pos = this.position(); | ||
var m = this.match(/^\\(?![*+?(){}\[\]])/); | ||
if (!m) return; | ||
var match = /^\\{2,}/.exec(this.input); | ||
var val = m[0]; | ||
if (match) { | ||
this.consume(match[0].length); | ||
val += '\\'; | ||
} | ||
return pos({ | ||
type: 'backslash', | ||
val: val | ||
}); | ||
}) | ||
/** | ||
* Square: "[.]" | ||
*/ | ||
.capture('square', function() { | ||
if (this.isInside('bracket')) return; | ||
var pos = this.position(); | ||
var m = this.match(/^\[(?![\^!])(.)\]/); | ||
var m = this.match(/^\[((?!\\)[^!^])\]/); | ||
if (!m) return; | ||
@@ -162,3 +219,3 @@ | ||
/** | ||
* Brackets: "[...]" (can be overridden by parsers in expand-brackets) | ||
* Brackets: "[...]" (basic, this can be overridden by other parsers) | ||
*/ | ||
@@ -172,6 +229,25 @@ | ||
var val = m[0]; | ||
var prefix = m[1] ? '^' : ''; | ||
var negated = m[1] ? '^' : ''; | ||
var inner = m[2] || ''; | ||
var close = m[3] || ''; | ||
var esc = this.input.slice(0, 2); | ||
if (inner === '' && esc === '\\]') { | ||
inner += esc; | ||
this.consume(2); | ||
var str = this.input; | ||
var idx = -1; | ||
var ch; | ||
while ((ch = str[++idx])) { | ||
this.consume(1); | ||
if (ch === ']') { | ||
close = ch; | ||
break; | ||
} | ||
inner += ch; | ||
} | ||
} | ||
return pos({ | ||
@@ -181,3 +257,3 @@ type: 'bracket', | ||
escaped: close !== ']', | ||
prefix: prefix, | ||
negated: negated, | ||
inner: inner, | ||
@@ -189,17 +265,2 @@ close: close | ||
/** | ||
* Dollar: "$" | ||
*/ | ||
.capture('dollar', function() { | ||
var pos = this.position(); | ||
var m = this.match(/^[$]/); | ||
if (!m) return; | ||
return pos({ | ||
type: 'dollar', | ||
val: m[0] | ||
}); | ||
}) | ||
/** | ||
* Text | ||
@@ -209,2 +270,3 @@ */ | ||
.capture('text', function() { | ||
if (this.isInside('bracket')) return; | ||
var pos = this.position(); | ||
@@ -223,13 +285,11 @@ var m = this.match(not); | ||
/** | ||
* Create and cache negation regex | ||
* Create text regex | ||
*/ | ||
function createRegex(str) { | ||
if (cache.hasOwnProperty(str)) { | ||
return cache[str]; | ||
} | ||
function createTextRegex(pattern) { | ||
if (cached) return cached; | ||
var opts = {contains: true, strictClose: false}; | ||
var re = regex(str, opts); | ||
cache[str] = re; | ||
return re; | ||
var not = regexNot.create(pattern, opts); | ||
var re = toRegex('^(?:[*]\\(|' + not + ')', opts); | ||
return (cached = re); | ||
} | ||
@@ -236,0 +296,0 @@ |
185
lib/utils.js
'use strict'; | ||
var path = require('path'); | ||
var unixifyCache = {}; | ||
var cache = {}; | ||
@@ -14,4 +14,3 @@ /** | ||
exports.isGlob = require('is-glob'); | ||
exports.isObject = require('isobject'); | ||
exports.normalize = require('normalize-path'); | ||
exports.typeOf = require('kind-of'); | ||
exports.pick = require('object.pick'); | ||
@@ -22,2 +21,21 @@ exports.union = require('arr-union'); | ||
/** | ||
* Create the key to use for memoization. The key is generated | ||
* by iterating over the options and concatenating key-value pairs | ||
* to the pattern string. | ||
*/ | ||
exports.createKey = function(pattern, options) { | ||
var key = pattern; | ||
if (typeof options === 'undefined') { | ||
return key; | ||
} | ||
for (var prop in options) { | ||
if (options.hasOwnProperty(prop)) { | ||
key += ';' + prop + '=' + String(options[prop]); | ||
} | ||
} | ||
return key; | ||
}; | ||
/** | ||
* Cast `val` to an array | ||
@@ -39,6 +57,44 @@ * @return {Array} | ||
exports.isString = function(val) { | ||
return val && typeof val === 'string'; | ||
return typeof val === 'string'; | ||
}; | ||
/** | ||
* Return true if `val` is a non-empty string | ||
*/ | ||
exports.isRegex = function(val) { | ||
return exports.typeOf(val) === 'regexp'; | ||
}; | ||
/** | ||
* Return true if `val` is a non-empty string | ||
*/ | ||
exports.isObject = function(val) { | ||
return exports.typeOf(val) === 'object'; | ||
}; | ||
/** | ||
* Escape regex characters in the given string | ||
*/ | ||
exports.escapeRegex = function(str) { | ||
return str.replace(/[\-\[\]{}()^$|\s*+?.\\\/]/g, '\\$&'); | ||
}; | ||
/** | ||
* Combines duplicate characters in the provided string. | ||
* @param {String} `str` | ||
* @returns {String} | ||
*/ | ||
exports.combineDuplicates = function(str, val) { | ||
if (typeof val === 'string') { | ||
var re = new RegExp('(' + val + ')(?=(?:' + val + ')*\\1)', 'g'); | ||
return str.replace(re, ''); | ||
} | ||
return str.replace(/(.)(?=.*\1)/g, ''); | ||
}; | ||
/** | ||
* Returns true if the given `str` has special characters | ||
@@ -48,20 +104,28 @@ */ | ||
exports.hasSpecialChars = function(str) { | ||
return /(?:(^|\/)[!.]|[*?()\[\]{}]|[+@]\()/.test(str); | ||
return /(?:(^|\/)[!.]|[*?+()\[\]{}]|[+@]\()/.test(str); | ||
}; | ||
/** | ||
* Returns a function that returns true if the given | ||
* regex matches the `filename` of a file path. | ||
* Strip backslashes from a string. | ||
* | ||
* @param {RegExp} `re` Matching regex | ||
* @return {Function} | ||
* @param {String} `filepath` | ||
* @return {String} | ||
*/ | ||
exports.matchBasename = function(re) { | ||
return function(filepath) { | ||
return re.test(filepath) || re.test(path.basename(filepath)); | ||
}; | ||
exports.unescape = function(str) { | ||
return exports.normalize(str.replace(/\\(\W)/g, '$1')); | ||
}; | ||
/** | ||
* Normalize slashes in the given filepath. | ||
* | ||
* @param {String} `filepath` | ||
* @return {String} | ||
*/ | ||
exports.normalize = function(filepath) { | ||
return filepath.replace(/[\\\/]+(?=[\w._-])(?![*?+\\!])/g, '/'); | ||
}; | ||
/** | ||
* Returns a function that returns true if the given | ||
@@ -76,4 +140,4 @@ * pattern matches or contains a `filepath` | ||
return (options && options.contains) | ||
? exports.containsPath(pattern, options) | ||
: exports.equalsPath(pattern, options); | ||
? exports.containsPattern(pattern, options) | ||
: exports.equalsPattern(pattern, options); | ||
}; | ||
@@ -89,9 +153,10 @@ | ||
exports.equalsPath = function(pattern, options) { | ||
exports.equalsPattern = function(pattern, options) { | ||
var unixify = exports.unixify(options); | ||
return function(filepath) { | ||
if (options.nocase === true) { | ||
return pattern.toLowerCase() === filepath.toLowerCase(); | ||
} else { | ||
return pattern === filepath; | ||
if (options && options.nocase === true) { | ||
filepath = filepath.toLowerCase(); | ||
} | ||
return pattern === filepath || pattern === unixify(filepath); | ||
}; | ||
@@ -108,5 +173,10 @@ }; | ||
exports.containsPath = function(pattern) { | ||
exports.containsPattern = function(pattern, options) { | ||
var unixify = exports.unixify(options); | ||
return function(filepath) { | ||
return pattern.indexOf(filepath) !== -1; | ||
if (options && options.nocase === true) { | ||
return unixify(filepath.toLowerCase()).indexOf(pattern) !== -1; | ||
} else { | ||
return unixify(filepath).indexOf(pattern) !== -1; | ||
} | ||
}; | ||
@@ -116,7 +186,13 @@ }; | ||
/** | ||
* Strip backslashes from a pattern | ||
* Returns a function that returns true if the given | ||
* regex matches the `filename` of a file path. | ||
* | ||
* @param {RegExp} `re` Matching regex | ||
* @return {Function} | ||
*/ | ||
exports.unescape = function(str) { | ||
return str.replace(/\\([^\\*])/g, '$1'); | ||
exports.matchBasename = function(re) { | ||
return function(filepath) { | ||
return re.test(filepath) || re.test(path.basename(filepath)); | ||
}; | ||
}; | ||
@@ -126,16 +202,16 @@ | ||
* Strip the prefix from a filepath | ||
* @param {String} `filepath` | ||
* @return {String} | ||
*/ | ||
exports.stripPrefix = function(filepath, options) { | ||
var opts = exports.extend({}, options); | ||
if (path.sep !== '\\' && opts.normalize !== true && opts.strictOpen !== true) { | ||
return filepath; | ||
exports.stripPrefix = function(fp) { | ||
if (typeof fp !== 'string') { | ||
return fp; | ||
} | ||
filepath = String(filepath || ''); | ||
var prefix = filepath.slice(0, 2); | ||
if (prefix === './' || prefix === '.\\') { | ||
return filepath.slice(2); | ||
if (fp.charAt(0) === '.' && (fp.charAt(1) === '/' || fp.charAt(1) === '\\')) { | ||
return fp.slice(2); | ||
} | ||
return filepath; | ||
return fp; | ||
}; | ||
@@ -149,42 +225,31 @@ | ||
exports.unixify = function(options) { | ||
var unixify; | ||
var key = ''; | ||
var key = exports.createKey('unixify', options); | ||
if (options) { | ||
for (var prop in options) { | ||
if (options.hasOwnProperty(prop)) { | ||
key += ':' + prop + ':' + String(options[prop]); | ||
} | ||
} | ||
if (cache.hasOwnProperty(key) && path.sep === '/') { | ||
return cache[key]; | ||
} | ||
var opts = exports.extend({}, options); | ||
if (unixifyCache.hasOwnProperty(key)) { | ||
return unixifyCache[key]; | ||
} | ||
var fn; | ||
var unixify = function(filepath) { | ||
return exports.stripPrefix(filepath); | ||
}; | ||
if (path.sep !== '/' || opts.unixify === true || opts.normalize === true) { | ||
fn = unixify; | ||
unixify = function(filepath) { | ||
return exports.normalize(exports.stripPrefix(filepath, opts), false); | ||
return fn(exports.normalize(filepath)); | ||
}; | ||
} | ||
} else if (opts.unescape === true) { | ||
if (opts.unescape === true) { | ||
fn = unixify; | ||
unixify = function(filepath) { | ||
return exports.stripPrefix(filepath, opts).replace(/\\([-.\w])/g, '$1'); | ||
return fn(exports.unescape(filepath)); | ||
}; | ||
} else if (opts.normalize === true) { | ||
unixify = function(filepath) { | ||
return exports.stripPrefix(filepath, opts); | ||
}; | ||
} else { | ||
// noop | ||
unixify = function(filepath) { | ||
return filepath; | ||
}; | ||
} | ||
unixifyCache[key] = unixify; | ||
cache[key] = unixify; | ||
return unixify; | ||
}; |
{ | ||
"name": "nanomatch", | ||
"description": "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"homepage": "https://github.com/jonschlinkert/nanomatch", | ||
@@ -31,15 +31,13 @@ "author": "Jon Schlinkert (https://github.com/jonschlinkert)", | ||
"fragment-cache": "^0.1.0", | ||
"is-glob": "^3.0.0", | ||
"is-glob": "^3.1.0", | ||
"is-odd": "^0.1.0", | ||
"isobject": "^2.1.0", | ||
"normalize-path": "^2.0.1", | ||
"kind-of": "^3.0.4", | ||
"object.pick": "^1.1.2", | ||
"regex-not": "^1.0.0", | ||
"snapdragon": "^0.7.2", | ||
"to-regex": "^2.1.0" | ||
"snapdragon": "^0.8.1", | ||
"to-regex": "^3.0.1" | ||
}, | ||
"devDependencies": { | ||
"bash-glob": "^0.1.1", | ||
"delete": "^0.3.2", | ||
"fs-exists-sync": "^0.1.0", | ||
"bash-match": "^0.1.1", | ||
"for-own": "^0.1.4", | ||
"gulp": "^3.9.1", | ||
@@ -52,8 +50,5 @@ "gulp-eslint": "^3.0.1", | ||
"helper-changelog": "^0.3.0", | ||
"micromatch": "^2.3.11", | ||
"minimatch": "^3.0.3", | ||
"mkdirp": "^0.5.1", | ||
"mocha": "^3.1.0", | ||
"multimatch": "^2.1.0", | ||
"write": "^0.3.2", | ||
"yargs-parser": "^4.0.2" | ||
@@ -60,0 +55,0 @@ }, |
@@ -1,2 +0,2 @@ | ||
# nanomatch [![NPM version](https://img.shields.io/npm/v/nanomatch.svg?style=flat)](https://www.npmjs.com/package/nanomatch) [![NPM downloads](https://img.shields.io/npm/dm/nanomatch.svg?style=flat)](https://npmjs.org/package/nanomatch) [![Build Status](https://img.shields.io/travis/jonschlinkert/nanomatch.svg?style=flat)](https://travis-ci.org/jonschlinkert/nanomatch) | ||
# nanomatch [![NPM version](https://img.shields.io/npm/v/nanomatch.svg?style=flat)](https://www.npmjs.com/package/nanomatch) [![NPM downloads](https://img.shields.io/npm/dm/nanomatch.svg?style=flat)](https://npmjs.org/package/nanomatch) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/nanomatch.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/nanomatch) [![Windows Build Status](https://img.shields.io/appveyor/ci/jonschlinkert/nanomatch.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/jonschlinkert/nanomatch) | ||
@@ -72,3 +72,3 @@ > Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces) | ||
### [nanomatch](index.js#L29) | ||
### [nanomatch](index.js#L39) | ||
@@ -92,3 +92,3 @@ The main function takes a list of strings and one or more glob patterns to use for matching. | ||
### [.match](index.js#L85) | ||
### [.match](index.js#L107) | ||
@@ -112,26 +112,4 @@ Similar to the main function, but `pattern` must be a string. | ||
### [.matcher](index.js#L156) | ||
### [.isMatch](index.js#L169) | ||
Creates a matcher function from the given glob `pattern` and `options`. The returned function takes a string to match as its only argument. | ||
**Example** | ||
```js | ||
var nanomatch = require('nanomatch'); | ||
var isMatch = nanomatch.matcher('*.!(*a)'); | ||
console.log(isMatch('a.a')); | ||
//=> false | ||
console.log(isMatch('a.b')); | ||
//=> true | ||
``` | ||
**Params** | ||
* `pattern` **{String}**: Glob pattern | ||
* `options` **{String}** | ||
* `returns` **{Function}**: Returns a matcher function. | ||
### [.isMatch](index.js#L215) | ||
Returns true if the specified `string` matches the given glob `pattern`. | ||
@@ -156,3 +134,3 @@ | ||
### [.not](index.js#L250) | ||
### [.not](index.js#L196) | ||
@@ -176,3 +154,3 @@ Returns a list of strings that do _not_ match any of the given `patterns`. | ||
### [.any](index.js#L280) | ||
### [.any](index.js#L231) | ||
@@ -198,3 +176,3 @@ Returns true if the given `string` matches any of the given glob `patterns`. | ||
### [.contains](index.js#L332) | ||
### [.contains](index.js#L259) | ||
@@ -220,3 +198,3 @@ Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but the pattern can match any part of the string. | ||
### [.matchKeys](index.js#L381) | ||
### [.matchKeys](index.js#L299) | ||
@@ -240,4 +218,26 @@ Filter the keys of the given object with the given `glob` pattern and `options`. Does not attempt to match nested keys. If you need this feature, use [glob-object](https://github.com/jonschlinkert/glob-object) instead. | ||
### [.makeRe](index.js#L403) | ||
### [.matcher](index.js#L326) | ||
Creates a matcher function from the given glob `pattern` and `options`. The returned function takes a string to match as its only argument. | ||
**Example** | ||
```js | ||
var nanomatch = require('nanomatch'); | ||
var isMatch = nanomatch.matcher('*.!(*a)'); | ||
console.log(isMatch('a.a')); | ||
//=> false | ||
console.log(isMatch('a.b')); | ||
//=> true | ||
``` | ||
**Params** | ||
* `pattern` **{String}**: Glob pattern | ||
* `options` **{String}** | ||
* `returns` **{Function}**: Returns a matcher function. | ||
### [.makeRe](index.js#L381) | ||
Create a regular expression from the given glob `pattern`. | ||
@@ -259,3 +259,3 @@ | ||
### [.create](index.js#L463) | ||
### [.create](index.js#L446) | ||
@@ -446,2 +446,2 @@ Parses the given glob `pattern` and returns an object with the compiled `output` and optional source `map`. | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.31, on October 08, 2016._ | ||
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.2.0, on October 18, 2016._ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49058
14
13
1058
+ Addedkind-of@^3.0.4
+ Addeddefine-property@2.0.2(transitive)
+ Addedsnapdragon@0.8.2(transitive)
+ Addedto-regex@3.0.2(transitive)
+ Addeduse@3.1.1(transitive)
- Removedisobject@^2.1.0
- Removednormalize-path@^2.0.1
- Removedlazy-cache@2.0.2(transitive)
- Removednormalize-path@2.1.1(transitive)
- Removedregex-not@0.1.2(transitive)
- Removedremove-trailing-separator@1.1.0(transitive)
- Removedset-getter@0.1.1(transitive)
- Removedsnapdragon@0.7.3(transitive)
- Removedto-regex@2.1.0(transitive)
- Removeduse@2.0.2(transitive)
Updatedis-glob@^3.1.0
Updatedsnapdragon@^0.8.1
Updatedto-regex@^3.0.1