micromatch 2.3.11 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of micromatch might be problematic. Click here for more details.

package/index.js CHANGED
@@ -1,431 +1,847 @@
1
- /*!
2
- * micromatch <https://github.com/jonschlinkert/micromatch>
3
- *
4
- * Copyright (c) 2014-2015, Jon Schlinkert.
5
- * Licensed under the MIT License.
1
+ 'use strict';
2
+
3
+ /**
4
+ * Module dependencies
6
5
  */
7
6
 
8
- 'use strict';
7
+ var util = require('util');
8
+ var braces = require('braces');
9
+ var toRegex = require('to-regex');
10
+ var extend = require('extend-shallow');
9
11
 
10
- var expand = require('./lib/expand');
12
+ /**
13
+ * Local dependencies
14
+ */
15
+
16
+ var compilers = require('./lib/compilers');
17
+ var parsers = require('./lib/parsers');
18
+ var cache = require('./lib/cache');
11
19
  var utils = require('./lib/utils');
20
+ var MAX_LENGTH = 1024 * 64;
12
21
 
13
22
  /**
14
- * The main function. Pass an array of filepaths,
15
- * and a string or array of glob patterns
23
+ * The main function takes a list of strings and one or more
24
+ * glob patterns to use for matching.
25
+ *
26
+ * ```js
27
+ * var mm = require('micromatch');
28
+ * mm(list, patterns[, options]);
16
29
  *
17
- * @param {Array|String} `files`
18
- * @param {Array|String} `patterns`
19
- * @param {Object} `opts`
20
- * @return {Array} Array of matches
30
+ * console.log(mm(['a.js', 'a.txt'], ['*.js']));
31
+ * //=> [ 'a.js' ]
32
+ * ```
33
+ * @param {Array} `list` A list of strings to match
34
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
35
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
36
+ * @return {Array} Returns an array of matches
37
+ * @summary false
38
+ * @api public
21
39
  */
22
40
 
23
- function micromatch(files, patterns, opts) {
24
- if (!files || !patterns) return [];
25
- opts = opts || {};
41
+ function micromatch(list, patterns, options) {
42
+ patterns = utils.arrayify(patterns);
43
+ list = utils.arrayify(list);
26
44
 
27
- if (typeof opts.cache === 'undefined') {
28
- opts.cache = true;
45
+ var len = patterns.length;
46
+ if (list.length === 0 || len === 0) {
47
+ return [];
29
48
  }
30
49
 
31
- if (!Array.isArray(patterns)) {
32
- return match(files, patterns, opts);
50
+ if (len === 1) {
51
+ return micromatch.match(list, patterns[0], options);
33
52
  }
34
53
 
35
- var len = patterns.length, i = 0;
36
- var omit = [], keep = [];
54
+ var negated = false;
55
+ var omit = [];
56
+ var keep = [];
57
+ var idx = -1;
37
58
 
38
- while (len--) {
39
- var glob = patterns[i++];
40
- if (typeof glob === 'string' && glob.charCodeAt(0) === 33 /* ! */) {
41
- omit.push.apply(omit, match(files, glob.slice(1), opts));
59
+ while (++idx < len) {
60
+ var pattern = patterns[idx];
61
+
62
+ if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) {
63
+ omit.push.apply(omit, micromatch.match(list, pattern.slice(1), options));
64
+ negated = true;
42
65
  } else {
43
- keep.push.apply(keep, match(files, glob, opts));
66
+ keep.push.apply(keep, micromatch.match(list, pattern, options));
44
67
  }
45
68
  }
46
- return utils.diff(keep, omit);
69
+
70
+ var matches = utils.diff(keep, omit);
71
+ if (!options || options.nodupes !== false) {
72
+ return utils.unique(matches);
73
+ }
74
+
75
+ return matches;
47
76
  }
48
77
 
49
78
  /**
50
- * Return an array of files that match the given glob pattern.
79
+ * Similar to the main function, but `pattern` must be a string.
51
80
  *
52
- * This function is called by the main `micromatch` function If you only
53
- * need to pass a single pattern you might get very minor speed improvements
54
- * using this function.
81
+ * ```js
82
+ * var mm = require('micromatch');
83
+ * mm.match(list, pattern[, options]);
55
84
  *
56
- * @param {Array} `files`
57
- * @param {String} `pattern`
58
- * @param {Object} `options`
59
- * @return {Array}
85
+ * console.log(mm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a'));
86
+ * //=> ['a.a', 'a.aa']
87
+ * ```
88
+ * @param {Array} `list` Array of strings to match
89
+ * @param {String} `pattern` Glob pattern to use for matching.
90
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
91
+ * @return {Array} Returns an array of matches
92
+ * @api public
60
93
  */
61
94
 
62
- function match(files, pattern, opts) {
63
- if (utils.typeOf(files) !== 'string' && !Array.isArray(files)) {
64
- throw new Error(msg('match', 'files', 'a string or array'));
95
+ micromatch.match = function(list, pattern, options) {
96
+ if (Array.isArray(pattern)) {
97
+ throw new TypeError('expected pattern to be a string');
65
98
  }
66
99
 
67
- files = utils.arrayify(files);
68
- opts = opts || {};
100
+ var unixify = utils.unixify(options);
101
+ var isMatch = memoize('match', pattern, options, micromatch.matcher);
102
+ var matches = [];
69
103
 
70
- var negate = opts.negate || false;
71
- var orig = pattern;
104
+ list = utils.arrayify(list);
105
+ var len = list.length;
106
+ var idx = -1;
72
107
 
73
- if (typeof pattern === 'string') {
74
- negate = pattern.charAt(0) === '!';
75
- if (negate) {
76
- pattern = pattern.slice(1);
108
+ while (++idx < len) {
109
+ var ele = list[idx];
110
+ if (ele === pattern || isMatch(ele)) {
111
+ matches.push(utils.value(ele, unixify, options));
77
112
  }
113
+ }
114
+
115
+ // if no options were passed, uniquify results and return
116
+ if (typeof options === 'undefined') {
117
+ return utils.unique(matches);
118
+ }
78
119
 
79
- // we need to remove the character regardless,
80
- // so the above logic is still needed
81
- if (opts.nonegate === true) {
82
- negate = false;
120
+ if (matches.length === 0) {
121
+ if (options.failglob === true) {
122
+ throw new Error('no matches found for "' + pattern + '"');
123
+ }
124
+ if (options.nonull === true || options.nullglob === true) {
125
+ return [options.unescape ? utils.unescape(pattern) : pattern];
83
126
  }
84
127
  }
85
128
 
86
- var _isMatch = matcher(pattern, opts);
87
- var len = files.length, i = 0;
88
- var res = [];
129
+ // if `opts.ignore` was defined, diff ignored list
130
+ if (options.ignore) {
131
+ matches = micromatch.not(matches, options.ignore, options);
132
+ }
89
133
 
90
- while (i < len) {
91
- var file = files[i++];
92
- var fp = utils.unixify(file, opts);
134
+ return options.nodupes !== false ? utils.unique(matches) : matches;
135
+ };
93
136
 
94
- if (!_isMatch(fp)) { continue; }
95
- res.push(fp);
137
+ /**
138
+ * Returns true if the specified `string` matches the given glob `pattern`.
139
+ *
140
+ * ```js
141
+ * var mm = require('micromatch');
142
+ * mm.isMatch(string, pattern[, options]);
143
+ *
144
+ * console.log(mm.isMatch('a.a', '*.a'));
145
+ * //=> true
146
+ * console.log(mm.isMatch('a.b', '*.a'));
147
+ * //=> false
148
+ * ```
149
+ * @param {String} `string` String to match
150
+ * @param {String} `pattern` Glob pattern to use for matching.
151
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
152
+ * @return {Boolean} Returns true if the string matches the glob pattern.
153
+ * @api public
154
+ */
155
+
156
+ micromatch.isMatch = function(str, pattern, options) {
157
+ if (typeof str !== 'string') {
158
+ throw new TypeError('expected a string: "' + util.inspect(str) + '"');
96
159
  }
97
160
 
98
- if (res.length === 0) {
99
- if (opts.failglob === true) {
100
- throw new Error('micromatch.match() found no matches for: "' + orig + '".');
101
- }
161
+ if (isEmptyString(str) || isEmptyString(pattern)) {
162
+ return false;
163
+ }
102
164
 
103
- if (opts.nonull || opts.nullglob) {
104
- res.push(utils.unescapeGlob(orig));
105
- }
165
+ var equals = utils.equalsPattern(options);
166
+ if (equals(str)) {
167
+ return true;
106
168
  }
107
169
 
108
- // if `negate` was defined, diff negated files
109
- if (negate) { res = utils.diff(files, res); }
170
+ var isMatch = memoize('isMatch', pattern, options, micromatch.matcher);
171
+ return isMatch(str);
172
+ };
110
173
 
111
- // if `ignore` was defined, diff ignored filed
112
- if (opts.ignore && opts.ignore.length) {
113
- pattern = opts.ignore;
114
- opts = utils.omit(opts, ['ignore']);
115
- res = utils.diff(res, micromatch(res, pattern, opts));
116
- }
174
+ /**
175
+ * Returns true if some of the strings in the given `list` match any of the
176
+ * given glob `patterns`.
177
+ *
178
+ * ```js
179
+ * var mm = require('micromatch');
180
+ * mm.some(list, patterns[, options]);
181
+ *
182
+ * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
183
+ * // true
184
+ * console.log(mm.some(['foo.js'], ['*.js', '!foo.js']));
185
+ * // false
186
+ * ```
187
+ * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
188
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
189
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
190
+ * @return {Boolean} Returns true if any patterns match `str`
191
+ * @api public
192
+ */
117
193
 
118
- if (opts.nodupes) {
119
- return utils.unique(res);
194
+ micromatch.some = function(list, patterns, options) {
195
+ if (typeof list === 'string') {
196
+ list = [list];
120
197
  }
121
- return res;
122
- }
198
+ for (var i = 0; i < list.length; i++) {
199
+ if (micromatch(list[i], patterns, options).length === 1) {
200
+ return true;
201
+ }
202
+ }
203
+ return false;
204
+ };
123
205
 
124
206
  /**
125
- * Returns a function that takes a glob pattern or array of glob patterns
126
- * to be used with `Array#filter()`. (Internally this function generates
127
- * the matching function using the [matcher] method).
207
+ * Returns true if every string in the given `list` matches
208
+ * any of the given glob `patterns`.
128
209
  *
129
210
  * ```js
130
- * var fn = mm.filter('[a-c]');
131
- * ['a', 'b', 'c', 'd', 'e'].filter(fn);
132
- * //=> ['a', 'b', 'c']
211
+ * var mm = require('micromatch');
212
+ * mm.every(list, patterns[, options]);
213
+ *
214
+ * console.log(mm.every('foo.js', ['foo.js']));
215
+ * // true
216
+ * console.log(mm.every(['foo.js', 'bar.js'], ['*.js']));
217
+ * // true
218
+ * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
219
+ * // false
220
+ * console.log(mm.every(['foo.js'], ['*.js', '!foo.js']));
221
+ * // false
133
222
  * ```
134
- * @param {String|Array} `patterns` Can be a glob or array of globs.
135
- * @param {Options} `opts` Options to pass to the [matcher] method.
136
- * @return {Function} Filter function to be passed to `Array#filter()`.
223
+ * @param {String|Array} `list` The string or array of strings to test.
224
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
225
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
226
+ * @return {Boolean} Returns true if any patterns match `str`
227
+ * @api public
137
228
  */
138
229
 
139
- function filter(patterns, opts) {
140
- if (!Array.isArray(patterns) && typeof patterns !== 'string') {
141
- throw new TypeError(msg('filter', 'patterns', 'a string or array'));
230
+ micromatch.every = function(list, patterns, options) {
231
+ if (typeof list === 'string') {
232
+ list = [list];
142
233
  }
143
-
144
- patterns = utils.arrayify(patterns);
145
- var len = patterns.length, i = 0;
146
- var patternMatchers = Array(len);
147
- while (i < len) {
148
- patternMatchers[i] = matcher(patterns[i++], opts);
149
- }
150
-
151
- return function(fp) {
152
- if (fp == null) return [];
153
- var len = patternMatchers.length, i = 0;
154
- var res = true;
155
-
156
- fp = utils.unixify(fp, opts);
157
- while (i < len) {
158
- var fn = patternMatchers[i++];
159
- if (!fn(fp)) {
160
- res = false;
161
- break;
162
- }
234
+ for (var i = 0; i < list.length; i++) {
235
+ if (micromatch(list[i], patterns, options).length !== 1) {
236
+ return false;
163
237
  }
164
- return res;
165
- };
166
- }
238
+ }
239
+ return true;
240
+ };
167
241
 
168
242
  /**
169
- * Returns true if the filepath contains the given
170
- * pattern. Can also return a function for matching.
243
+ * Returns true if **any** of the given glob `patterns`
244
+ * match the specified `string`.
171
245
  *
172
246
  * ```js
173
- * isMatch('foo.md', '*.md', {});
174
- * //=> true
247
+ * var mm = require('micromatch');
248
+ * mm.any(string, patterns[, options]);
175
249
  *
176
- * isMatch('*.md', {})('foo.md')
250
+ * console.log(mm.any('a.a', ['b.*', '*.a']));
177
251
  * //=> true
252
+ * console.log(mm.any('a.a', 'b.*'));
253
+ * //=> false
178
254
  * ```
179
- * @param {String} `fp`
180
- * @param {String} `pattern`
181
- * @param {Object} `opts`
182
- * @return {Boolean}
255
+ * @param {String|Array} `str` The string to test.
256
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
257
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
258
+ * @return {Boolean} Returns true if any patterns match `str`
259
+ * @api public
183
260
  */
184
261
 
185
- function isMatch(fp, pattern, opts) {
186
- if (typeof fp !== 'string') {
187
- throw new TypeError(msg('isMatch', 'filepath', 'a string'));
262
+ micromatch.any = function(str, patterns, options) {
263
+ if (typeof str !== 'string') {
264
+ throw new TypeError('expected a string: "' + util.inspect(str) + '"');
188
265
  }
189
266
 
190
- fp = utils.unixify(fp, opts);
191
- if (utils.typeOf(pattern) === 'object') {
192
- return matcher(fp, pattern);
267
+ if (isEmptyString(str) || isEmptyString(patterns)) {
268
+ return false;
193
269
  }
194
- return matcher(pattern, opts)(fp);
195
- }
270
+
271
+ if (typeof patterns === 'string') {
272
+ patterns = [patterns];
273
+ }
274
+
275
+ for (var i = 0; i < patterns.length; i++) {
276
+ if (micromatch.isMatch(str, patterns[i], options)) {
277
+ return true;
278
+ }
279
+ }
280
+ return false;
281
+ };
196
282
 
197
283
  /**
198
- * Returns true if the filepath matches the
199
- * given pattern.
284
+ * Returns true if **all** of the given `patterns` match
285
+ * the specified string.
286
+ *
287
+ * ```js
288
+ * var mm = require('micromatch');
289
+ * mm.all(string, patterns[, options]);
290
+ *
291
+ * console.log(mm.all('foo.js', ['foo.js']));
292
+ * // true
293
+ *
294
+ * console.log(mm.all('foo.js', ['*.js', '!foo.js']));
295
+ * // false
296
+ *
297
+ * console.log(mm.all('foo.js', ['*.js', 'foo.js']));
298
+ * // true
299
+ *
300
+ * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
301
+ * // true
302
+ * ```
303
+ * @param {String|Array} `str` The string to test.
304
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
305
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
306
+ * @return {Boolean} Returns true if any patterns match `str`
307
+ * @api public
200
308
  */
201
309
 
202
- function contains(fp, pattern, opts) {
203
- if (typeof fp !== 'string') {
204
- throw new TypeError(msg('contains', 'pattern', 'a string'));
310
+ micromatch.all = function(str, patterns, options) {
311
+ if (typeof str !== 'string') {
312
+ throw new TypeError('expected a string: "' + util.inspect(str) + '"');
313
+ }
314
+ if (typeof patterns === 'string') {
315
+ patterns = [patterns];
205
316
  }
317
+ for (var i = 0; i < patterns.length; i++) {
318
+ if (!micromatch.isMatch(str, patterns[i], options)) {
319
+ return false;
320
+ }
321
+ }
322
+ return true;
323
+ };
324
+
325
+ /**
326
+ * Returns a list of strings that _**do not match any**_ of the given `patterns`.
327
+ *
328
+ * ```js
329
+ * var mm = require('micromatch');
330
+ * mm.not(list, patterns[, options]);
331
+ *
332
+ * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a'));
333
+ * //=> ['b.b', 'c.c']
334
+ * ```
335
+ * @param {Array} `list` Array of strings to match.
336
+ * @param {String|Array} `patterns` One or more glob pattern to use for matching.
337
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
338
+ * @return {Array} Returns an array of strings that **do not match** the given patterns.
339
+ * @api public
340
+ */
341
+
342
+ micromatch.not = function(list, patterns, options) {
343
+ var opts = extend({}, options);
344
+ var ignore = opts.ignore;
345
+ delete opts.ignore;
206
346
 
207
- opts = opts || {};
208
- opts.contains = (pattern !== '');
209
- fp = utils.unixify(fp, opts);
347
+ list = utils.arrayify(list);
210
348
 
211
- if (opts.contains && !utils.isGlob(pattern)) {
212
- return fp.indexOf(pattern) !== -1;
349
+ var matches = utils.diff(list, micromatch(list, patterns, opts));
350
+ if (ignore) {
351
+ matches = utils.diff(matches, micromatch(list, ignore));
213
352
  }
214
- return matcher(pattern, opts)(fp);
215
- }
353
+
354
+ return opts.nodupes !== false ? utils.unique(matches) : matches;
355
+ };
216
356
 
217
357
  /**
218
- * Returns true if a file path matches any of the
219
- * given patterns.
358
+ * Returns true if the given `string` contains the given pattern. Similar
359
+ * to [.isMatch](#isMatch) but the pattern can match any part of the string.
360
+ *
361
+ * ```js
362
+ * var mm = require('micromatch');
363
+ * mm.contains(string, pattern[, options]);
220
364
  *
221
- * @param {String} `fp` The filepath to test.
222
- * @param {String|Array} `patterns` Glob patterns to use.
223
- * @param {Object} `opts` Options to pass to the `matcher()` function.
224
- * @return {String}
365
+ * console.log(mm.contains('aa/bb/cc', '*b'));
366
+ * //=> true
367
+ * console.log(mm.contains('aa/bb/cc', '*d'));
368
+ * //=> false
369
+ * ```
370
+ * @param {String} `str` The string to match.
371
+ * @param {String|Array} `patterns` Glob pattern to use for matching.
372
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
373
+ * @return {Boolean} Returns true if the patter matches any part of `str`.
374
+ * @api public
225
375
  */
226
376
 
227
- function any(fp, patterns, opts) {
228
- if (!Array.isArray(patterns) && typeof patterns !== 'string') {
229
- throw new TypeError(msg('any', 'patterns', 'a string or array'));
377
+ micromatch.contains = function(str, patterns, options) {
378
+ if (typeof str !== 'string') {
379
+ throw new TypeError('expected a string: "' + util.inspect(str) + '"');
230
380
  }
231
381
 
232
- patterns = utils.arrayify(patterns);
233
- var len = patterns.length;
382
+ if (typeof patterns === 'string') {
383
+ if (isEmptyString(str) || isEmptyString(patterns)) {
384
+ return false;
385
+ }
234
386
 
235
- fp = utils.unixify(fp, opts);
236
- while (len--) {
237
- var isMatch = matcher(patterns[len], opts);
238
- if (isMatch(fp)) {
387
+ var equals = utils.equalsPattern(patterns, options);
388
+ if (equals(str)) {
389
+ return true;
390
+ }
391
+ var contains = utils.containsPattern(patterns, options);
392
+ if (contains(str)) {
239
393
  return true;
240
394
  }
241
395
  }
242
- return false;
243
- }
396
+
397
+ var opts = extend({}, options, {contains: true});
398
+ return micromatch.any(str, patterns, opts);
399
+ };
244
400
 
245
401
  /**
246
- * Filter the keys of an object with the given `glob` pattern
247
- * and `options`
248
- *
249
- * @param {Object} `object`
250
- * @param {Pattern} `object`
251
- * @return {Array}
402
+ * Returns true if the given pattern and options should enable
403
+ * the `matchBase` option.
404
+ * @return {Boolean}
405
+ * @api private
252
406
  */
253
407
 
254
- function matchKeys(obj, glob, options) {
255
- if (utils.typeOf(obj) !== 'object') {
256
- throw new TypeError(msg('matchKeys', 'first argument', 'an object'));
257
- }
408
+ micromatch.matchBase = function(pattern, options) {
409
+ if (pattern && pattern.indexOf('/') !== -1 || !options) return false;
410
+ return options.basename === true || options.matchBase === true;
411
+ };
258
412
 
259
- var fn = matcher(glob, options);
260
- var res = {};
413
+ /**
414
+ * Filter the keys of the given object with the given `glob` pattern
415
+ * and `options`. Does not attempt to match nested keys. If you need this feature,
416
+ * use [glob-object][] instead.
417
+ *
418
+ * ```js
419
+ * var mm = require('micromatch');
420
+ * mm.matchKeys(object, patterns[, options]);
421
+ *
422
+ * var obj = { aa: 'a', ab: 'b', ac: 'c' };
423
+ * console.log(mm.matchKeys(obj, '*b'));
424
+ * //=> { ab: 'b' }
425
+ * ```
426
+ * @param {Object} `object` The object with keys to filter.
427
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
428
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
429
+ * @return {Object} Returns an object with only keys that match the given patterns.
430
+ * @api public
431
+ */
261
432
 
262
- for (var key in obj) {
263
- if (obj.hasOwnProperty(key) && fn(key)) {
264
- res[key] = obj[key];
265
- }
433
+ micromatch.matchKeys = function(obj, patterns, options) {
434
+ if (!utils.isObject(obj)) {
435
+ throw new TypeError('expected the first argument to be an object');
266
436
  }
267
- return res;
268
- }
437
+ var keys = micromatch(Object.keys(obj), patterns, options);
438
+ return utils.pick(obj, keys);
439
+ };
269
440
 
270
441
  /**
271
- * Return a function for matching based on the
272
- * given `pattern` and `options`.
442
+ * Returns a memoized matcher function from the given glob `pattern` and `options`.
443
+ * The returned function takes a string to match as its only argument and returns
444
+ * true if the string is a match.
445
+ *
446
+ * ```js
447
+ * var mm = require('micromatch');
448
+ * mm.matcher(pattern[, options]);
273
449
  *
274
- * @param {String} `pattern`
275
- * @param {Object} `options`
276
- * @return {Function}
450
+ * var isMatch = mm.matcher('*.!(*a)');
451
+ * console.log(isMatch('a.a'));
452
+ * //=> false
453
+ * console.log(isMatch('a.b'));
454
+ * //=> true
455
+ * ```
456
+ * @param {String} `pattern` Glob pattern
457
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed.
458
+ * @return {Function} Returns a matcher function.
459
+ * @api public
277
460
  */
278
461
 
279
- function matcher(pattern, opts) {
280
- // pattern is a function
281
- if (typeof pattern === 'function') {
282
- return pattern;
462
+ micromatch.matcher = function matcher(pattern, options) {
463
+ if (Array.isArray(pattern)) {
464
+ return compose(pattern, options, matcher);
283
465
  }
284
- // pattern is a regex
466
+
467
+ // if pattern is a regex
285
468
  if (pattern instanceof RegExp) {
286
- return function(fp) {
287
- return pattern.test(fp);
288
- };
469
+ return test(pattern);
289
470
  }
290
471
 
291
- if (typeof pattern !== 'string') {
292
- throw new TypeError(msg('matcher', 'pattern', 'a string, regex, or function'));
472
+ // if pattern is invalid
473
+ if (!utils.isString(pattern)) {
474
+ throw new TypeError('expected pattern to be an array, string or regex');
475
+ }
476
+
477
+ // if pattern is a non-glob string
478
+ if (!utils.hasSpecialChars(pattern)) {
479
+ if (options && options.nocase === true) {
480
+ pattern = pattern.toLowerCase();
481
+ }
482
+ return utils.matchPath(pattern, options);
293
483
  }
294
484
 
295
- // strings, all the way down...
296
- pattern = utils.unixify(pattern, opts);
485
+ // if pattern is a glob string
486
+ var re = micromatch.makeRe(pattern, options);
297
487
 
298
- // pattern is a non-glob string
299
- if (!utils.isGlob(pattern)) {
300
- return utils.matchPath(pattern, opts);
488
+ // if `options.matchBase` or `options.basename` is defined
489
+ if (micromatch.matchBase(pattern, options)) {
490
+ return utils.matchBasename(re, options);
301
491
  }
302
- // pattern is a glob string
303
- var re = makeRe(pattern, opts);
304
492
 
305
- // `matchBase` is defined
306
- if (opts && opts.matchBase) {
307
- return utils.hasFilename(re, opts);
493
+ function test(regex) {
494
+ var equals = utils.equalsPattern(options);
495
+ var unixify = utils.unixify(options);
496
+
497
+ return function(str) {
498
+ if (equals(str)) {
499
+ return true;
500
+ }
501
+
502
+ if (regex.test(unixify(str))) {
503
+ return true;
504
+ }
505
+ return false;
506
+ };
308
507
  }
309
- // `matchBase` is not defined
310
- return function(fp) {
311
- fp = utils.unixify(fp, opts);
312
- return re.test(fp);
313
- };
314
- }
508
+
509
+ var fn = test(re);
510
+ Object.defineProperty(fn, 'result', {
511
+ configurable: true,
512
+ enumerable: false,
513
+ value: re.result
514
+ });
515
+ return fn;
516
+ };
315
517
 
316
518
  /**
317
- * Create and cache a regular expression for matching
318
- * file paths.
519
+ * Create a regular expression from the given glob `pattern`.
319
520
  *
320
- * If the leading character in the `glob` is `!`, a negation
321
- * regex is returned.
521
+ * ```js
522
+ * var mm = require('micromatch');
523
+ * mm.makeRe(pattern[, options]);
322
524
  *
323
- * @param {String} `glob`
324
- * @param {Object} `options`
325
- * @return {RegExp}
525
+ * console.log(mm.makeRe('*.js'));
526
+ * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
527
+ * ```
528
+ * @param {String} `pattern` A glob pattern to convert to regex.
529
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed.
530
+ * @return {RegExp} Returns a regex created from the given pattern.
531
+ * @api public
326
532
  */
327
533
 
328
- function toRegex(glob, options) {
329
- // clone options to prevent mutating the original object
330
- var opts = Object.create(options || {});
331
- var flags = opts.flags || '';
332
- if (opts.nocase && flags.indexOf('i') === -1) {
333
- flags += 'i';
534
+ micromatch.makeRe = function(pattern, options) {
535
+ if (typeof pattern !== 'string') {
536
+ throw new TypeError('expected pattern to be a string');
334
537
  }
335
538
 
336
- var parsed = expand(glob, opts);
337
-
338
- // pass in tokens to avoid parsing more than once
339
- opts.negated = opts.negated || parsed.negated;
340
- opts.negate = opts.negated;
341
- glob = wrapGlob(parsed.pattern, opts);
342
- var re;
539
+ if (pattern.length > MAX_LENGTH) {
540
+ throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
541
+ }
343
542
 
344
- try {
345
- re = new RegExp(glob, flags);
346
- return re;
347
- } catch (err) {
348
- err.reason = 'micromatch invalid regex: (' + re + ')';
349
- if (opts.strict) throw new SyntaxError(err);
543
+ function makeRe() {
544
+ var result = micromatch.create(pattern, options);
545
+ var asts = [];
546
+ var output = result.map(function(obj) {
547
+ obj.ast.state = obj.state;
548
+ asts.push(obj.ast);
549
+ return obj.output;
550
+ });
551
+
552
+ var regex = toRegex(output.join('|'), options);
553
+ Object.defineProperty(regex, 'result', {
554
+ configurable: true,
555
+ enumerable: false,
556
+ value: asts
557
+ });
558
+ return regex;
350
559
  }
351
560
 
352
- // we're only here if a bad pattern was used and the user
353
- // passed `options.silent`, so match nothing
354
- return /$^/;
355
- }
561
+ return memoize('makeRe', pattern, options, makeRe);
562
+ };
356
563
 
357
564
  /**
358
- * Create the regex to do the matching. If the leading
359
- * character in the `glob` is `!` a negation regex is returned.
565
+ * Expand the given brace `pattern`.
360
566
  *
361
- * @param {String} `glob`
362
- * @param {Boolean} `negate`
567
+ * ```js
568
+ * var mm = require('micromatch');
569
+ * console.log(mm.braces('foo/{a,b}/bar'));
570
+ * //=> ['foo/(a|b)/bar']
571
+ *
572
+ * console.log(mm.braces('foo/{a,b}/bar', {expand: true}));
573
+ * //=> ['foo/(a|b)/bar']
574
+ * ```
575
+ * @param {String} `pattern` String with brace pattern to expand.
576
+ * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options.
577
+ * @return {Array}
578
+ * @api public
363
579
  */
364
580
 
365
- function wrapGlob(glob, opts) {
366
- var prefix = (opts && !opts.contains) ? '^' : '';
367
- var after = (opts && !opts.contains) ? '$' : '';
368
- glob = ('(?:' + glob + ')' + after);
369
- if (opts && opts.negate) {
370
- return prefix + ('(?!^' + glob + ').*$');
581
+ micromatch.braces = function(pattern, options) {
582
+ if (typeof pattern !== 'string') {
583
+ throw new TypeError('expected a string');
371
584
  }
372
- return prefix + glob;
373
- }
585
+
586
+ function expand() {
587
+ if (options && options.nobrace === true) return [pattern];
588
+ if (!/\{.*\}/.test(pattern)) return [pattern];
589
+ // if (/[!@*?+]\{/.test(pattern)) {
590
+ // options = utils.extend({}, options, {expand: true});
591
+ // }
592
+ return braces(pattern, options);
593
+ }
594
+
595
+ return memoize('braces', pattern, options, expand);
596
+ };
597
+
598
+ /**
599
+ * Proxy to the [micromatch.braces](#method), for parity with
600
+ * minimatch.
601
+ */
602
+
603
+ micromatch.braceExpand = function(pattern, options) {
604
+ var opts = extend({}, options, {expand: true});
605
+ return micromatch.braces(pattern, opts);
606
+ };
607
+
608
+ /**
609
+ * Parses the given glob `pattern` and returns an array of abstract syntax
610
+ * trees (ASTs), with the compiled `output` and optional source `map` on
611
+ * each AST.
612
+ *
613
+ * ```js
614
+ * var mm = require('micromatch');
615
+ * mm.create(pattern[, options]);
616
+ *
617
+ * console.log(mm.create('abc/*.js'));
618
+ * // [{ options: { source: 'string', sourcemap: true },
619
+ * // state: {},
620
+ * // compilers:
621
+ * // { ... },
622
+ * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js',
623
+ * // ast:
624
+ * // { type: 'root',
625
+ * // errors: [],
626
+ * // nodes:
627
+ * // [ ... ],
628
+ * // dot: false,
629
+ * // input: 'abc/*.js' },
630
+ * // parsingErrors: [],
631
+ * // map:
632
+ * // { version: 3,
633
+ * // sources: [ 'string' ],
634
+ * // names: [],
635
+ * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE',
636
+ * // sourcesContent: [ 'abc/*.js' ] },
637
+ * // position: { line: 1, column: 28 },
638
+ * // content: {},
639
+ * // files: {},
640
+ * // idx: 6 }]
641
+ * ```
642
+ * @param {String} `pattern` Glob pattern to parse and compile.
643
+ * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed.
644
+ * @return {Object} Returns an object with the parsed AST, compiled string and optional source map.
645
+ * @api public
646
+ */
647
+
648
+ micromatch.create = function(pattern, options) {
649
+ return memoize('create', pattern, options, function() {
650
+ function create(str, opts) {
651
+ return micromatch.compile(micromatch.parse(str, opts), opts);
652
+ }
653
+
654
+ pattern = micromatch.braces(pattern, options);
655
+ var len = pattern.length;
656
+ var idx = -1;
657
+ var res = [];
658
+
659
+ while (++idx < len) {
660
+ res.push(create(pattern[idx], options));
661
+ }
662
+ return res;
663
+ });
664
+ };
374
665
 
375
666
  /**
376
- * Create and cache a regular expression for matching file paths.
377
- * If the leading character in the `glob` is `!`, a negation
378
- * regex is returned.
667
+ * Parse the given `str` with the given `options`.
379
668
  *
380
- * @param {String} `glob`
381
- * @param {Object} `options`
382
- * @return {RegExp}
669
+ * ```js
670
+ * var mm = require('micromatch');
671
+ * mm.parse(pattern[, options]);
672
+ *
673
+ * var ast = mm.parse('a/{b,c}/d');
674
+ * console.log(ast);
675
+ * // { type: 'root',
676
+ * // errors: [],
677
+ * // input: 'a/{b,c}/d',
678
+ * // nodes:
679
+ * // [ { type: 'bos', val: '' },
680
+ * // { type: 'text', val: 'a/' },
681
+ * // { type: 'brace',
682
+ * // nodes:
683
+ * // [ { type: 'brace.open', val: '{' },
684
+ * // { type: 'text', val: 'b,c' },
685
+ * // { type: 'brace.close', val: '}' } ] },
686
+ * // { type: 'text', val: '/d' },
687
+ * // { type: 'eos', val: '' } ] }
688
+ * ```
689
+ * @param {String} `str`
690
+ * @param {Object} `options`
691
+ * @return {Object} Returns an AST
692
+ * @api public
383
693
  */
384
694
 
385
- function makeRe(glob, opts) {
386
- if (utils.typeOf(glob) !== 'string') {
387
- throw new Error(msg('makeRe', 'glob', 'a string'));
695
+ micromatch.parse = function(pattern, options) {
696
+ if (typeof pattern !== 'string') {
697
+ throw new TypeError('expected a string');
388
698
  }
389
- return utils.cache(toRegex, glob, opts);
390
- }
699
+
700
+ function parse() {
701
+ var snapdragon = utils.instantiate(null, options);
702
+ parsers(snapdragon, options);
703
+
704
+ if (pattern.slice(0, 2) === './') {
705
+ pattern = pattern.slice(2);
706
+ }
707
+
708
+ pattern = utils.combineDuplicates(pattern, '\\*\\*\\/|\\/\\*\\*');
709
+ var ast = snapdragon.parse(pattern, options);
710
+ utils.define(ast, 'snapdragon', snapdragon);
711
+ ast.input = pattern;
712
+ return ast;
713
+ }
714
+
715
+ return memoize('parse', pattern, options, parse);
716
+ };
391
717
 
392
718
  /**
393
- * Make error messages consistent. Follows this format:
719
+ * Compile the given `ast` or string with the given `options`.
394
720
  *
395
721
  * ```js
396
- * msg(methodName, argNumber, nativeType);
397
- * // example:
398
- * msg('matchKeys', 'first', 'an object');
722
+ * var mm = require('micromatch');
723
+ * mm.compile(ast[, options]);
724
+ *
725
+ * var ast = mm.parse('a/{b,c}/d');
726
+ * console.log(mm.compile(ast));
727
+ * // { options: { source: 'string' },
728
+ * // state: {},
729
+ * // compilers:
730
+ * // { eos: [Function],
731
+ * // noop: [Function],
732
+ * // bos: [Function],
733
+ * // brace: [Function],
734
+ * // 'brace.open': [Function],
735
+ * // text: [Function],
736
+ * // 'brace.close': [Function] },
737
+ * // output: [ 'a/(b|c)/d' ],
738
+ * // ast:
739
+ * // { ... },
740
+ * // parsingErrors: [] }
399
741
  * ```
742
+ * @param {Object|String} `ast`
743
+ * @param {Object} `options`
744
+ * @return {Object} Returns an object that has an `output` property with the compiled string.
745
+ * @api public
746
+ */
747
+
748
+ micromatch.compile = function(ast, options) {
749
+ if (typeof ast === 'string') {
750
+ ast = micromatch.parse(ast, options);
751
+ }
752
+
753
+ return memoize('compile', ast.input, options, function() {
754
+ var snapdragon = utils.instantiate(ast, options);
755
+ compilers(snapdragon, options);
756
+ return snapdragon.compile(ast, options);
757
+ });
758
+ };
759
+
760
+ /**
761
+ * Clear the regex cache.
400
762
  *
401
- * @param {String} `method`
402
- * @param {String} `num`
403
- * @param {String} `type`
404
- * @return {String}
763
+ * ```js
764
+ * mm.clearCache();
765
+ * ```
766
+ * @api public
405
767
  */
406
768
 
407
- function msg(method, what, type) {
408
- return 'micromatch.' + method + '(): ' + what + ' should be ' + type + '.';
769
+ micromatch.clearCache = function() {
770
+ micromatch.cache.caches = {};
771
+ };
772
+
773
+ /**
774
+ * Returns true if the given value is effectively an empty string
775
+ */
776
+
777
+ function isEmptyString(val) {
778
+ return String(val) === '' || String(val) === './';
779
+ }
780
+
781
+ /**
782
+ * Compose a matcher function with the given patterns.
783
+ * This allows matcher functions to be compiled once and
784
+ * called multiple times.
785
+ */
786
+
787
+ function compose(patterns, options, matcher) {
788
+ var matchers;
789
+
790
+ return memoize('compose', String(patterns), options, function() {
791
+ return function(file) {
792
+ // delay composition until it's invoked the first time,
793
+ // after that it won't be called again
794
+ if (!matchers) {
795
+ matchers = [];
796
+ for (var i = 0; i < patterns.length; i++) {
797
+ matchers.push(matcher(patterns[i], options));
798
+ }
799
+ }
800
+
801
+ var len = matchers.length;
802
+ while (len--) {
803
+ if (matchers[len](file) === true) {
804
+ return true;
805
+ }
806
+ }
807
+ return false;
808
+ };
809
+ });
810
+ }
811
+
812
+ /**
813
+ * Memoize a generated regex or function. A unique key is generated
814
+ * from the `type` (usually method name), the `pattern`, and
815
+ * user-defined options.
816
+ */
817
+
818
+ function memoize(type, pattern, options, fn) {
819
+ var key = utils.createKey(type + '=' + pattern, options);
820
+
821
+ if (options && options.cache === false) {
822
+ return fn(pattern, options);
823
+ }
824
+
825
+ if (cache.has(type, key)) {
826
+ return cache.get(type, key);
827
+ }
828
+
829
+ var val = fn(pattern, options);
830
+ cache.set(type, key, val);
831
+ return val;
409
832
  }
410
833
 
411
834
  /**
412
- * Public methods
835
+ * Expose compiler, parser and cache on `micromatch`
413
836
  */
414
837
 
415
- /* eslint no-multi-spaces: 0 */
416
- micromatch.any = any;
417
- micromatch.braces = micromatch.braceExpand = utils.braces;
418
- micromatch.contains = contains;
419
- micromatch.expand = expand;
420
- micromatch.filter = filter;
421
- micromatch.isMatch = isMatch;
422
- micromatch.makeRe = makeRe;
423
- micromatch.match = match;
424
- micromatch.matcher = matcher;
425
- micromatch.matchKeys = matchKeys;
838
+ micromatch.compilers = compilers;
839
+ micromatch.parsers = parsers;
840
+ micromatch.caches = cache.caches;
426
841
 
427
842
  /**
428
843
  * Expose `micromatch`
844
+ * @type {Function}
429
845
  */
430
846
 
431
847
  module.exports = micromatch;