minimatch 3.0.6 → 4.0.0

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.
Files changed (2) hide show
  1. package/minimatch.js +710 -780
  2. package/package.json +2 -2
package/minimatch.js CHANGED
@@ -1,15 +1,25 @@
1
+ const minimatch = module.exports = (p, pattern, options = {}) => {
2
+ assertValidPattern(pattern)
3
+
4
+ // shortcut: comments match nothing.
5
+ if (!options.nocomment && pattern.charAt(0) === '#') {
6
+ return false
7
+ }
8
+
9
+ return new Minimatch(pattern, options).match(p)
10
+ }
11
+
1
12
  module.exports = minimatch
2
- minimatch.Minimatch = Minimatch
3
13
 
4
- var path = (function () { try { return require('path') } catch (e) {}}()) || {
14
+ const path = (() => { try { return require('path') } catch (e) {}})() || {
5
15
  sep: '/'
6
16
  }
7
17
  minimatch.sep = path.sep
8
18
 
9
- var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
10
- var expand = require('brace-expansion')
19
+ const GLOBSTAR = Symbol('globstar **')
20
+ const expand = require('brace-expansion')
11
21
 
12
- var plTypes = {
22
+ const plTypes = {
13
23
  '!': { open: '(?:(?!(?:', close: '))[^/]*?)'},
14
24
  '?': { open: '(?:', close: ')?' },
15
25
  '+': { open: '(?:', close: ')+' },
@@ -19,213 +29,71 @@ var plTypes = {
19
29
 
20
30
  // any single thing other than /
21
31
  // don't need to escape / when using new RegExp()
22
- var qmark = '[^/]'
32
+ const qmark = '[^/]'
23
33
 
24
34
  // * => any number of characters
25
- var star = qmark + '*?'
35
+ const star = qmark + '*?'
26
36
 
27
37
  // ** when dots are allowed. Anything goes, except .. and .
28
38
  // not (^ or / followed by one or two dots followed by $ or /),
29
39
  // followed by anything, any number of times.
30
- var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
40
+ const twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
31
41
 
32
42
  // not a ^ or / followed by a dot,
33
43
  // followed by anything, any number of times.
34
- var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
44
+ const twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
45
+
46
+ // "abc" -> { a:true, b:true, c:true }
47
+ const charSet = s => s.split('').reduce((set, c) => {
48
+ set[c] = true
49
+ return set
50
+ }, {})
35
51
 
36
52
  // characters that need to be escaped in RegExp.
37
- var reSpecials = charSet('().*{}+?[]^$\\!')
53
+ const reSpecials = charSet('().*{}+?[]^$\\!')
38
54
 
39
- // "abc" -> { a:true, b:true, c:true }
40
- function charSet (s) {
41
- return s.split('').reduce(function (set, c) {
42
- set[c] = true
43
- return set
44
- }, {})
45
- }
55
+ // characters that indicate we have to add the pattern start
56
+ const addPatternStartSet = charSet('[.(')
46
57
 
47
58
  // normalizes slashes.
48
- var slashSplit = /\/+/
59
+ const slashSplit = /\/+/
49
60
 
50
- minimatch.filter = filter
51
- function filter (pattern, options) {
52
- options = options || {}
53
- return function (p, i, list) {
54
- return minimatch(p, pattern, options)
55
- }
56
- }
61
+ minimatch.filter = (pattern, options = {}) =>
62
+ (p, i, list) => minimatch(p, pattern, options)
57
63
 
58
- function ext (a, b) {
59
- b = b || {}
60
- var t = {}
61
- Object.keys(a).forEach(function (k) {
62
- t[k] = a[k]
63
- })
64
- Object.keys(b).forEach(function (k) {
65
- t[k] = b[k]
66
- })
64
+ const ext = (a, b = {}) => {
65
+ const t = {}
66
+ Object.keys(a).forEach(k => t[k] = a[k])
67
+ Object.keys(b).forEach(k => t[k] = b[k])
67
68
  return t
68
69
  }
69
70
 
70
- minimatch.defaults = function (def) {
71
+ minimatch.defaults = def => {
71
72
  if (!def || typeof def !== 'object' || !Object.keys(def).length) {
72
73
  return minimatch
73
74
  }
74
75
 
75
- var orig = minimatch
76
-
77
- var m = function minimatch (p, pattern, options) {
78
- return orig(p, pattern, ext(def, options))
79
- }
80
-
81
- m.Minimatch = function Minimatch (pattern, options) {
82
- return new orig.Minimatch(pattern, ext(def, options))
83
- }
84
- m.Minimatch.defaults = function defaults (options) {
85
- return orig.defaults(ext(def, options)).Minimatch
86
- }
87
-
88
- m.filter = function filter (pattern, options) {
89
- return orig.filter(pattern, ext(def, options))
90
- }
91
-
92
- m.defaults = function defaults (options) {
93
- return orig.defaults(ext(def, options))
94
- }
95
-
96
- m.makeRe = function makeRe (pattern, options) {
97
- return orig.makeRe(pattern, ext(def, options))
98
- }
99
-
100
- m.braceExpand = function braceExpand (pattern, options) {
101
- return orig.braceExpand(pattern, ext(def, options))
102
- }
76
+ const orig = minimatch
103
77
 
104
- m.match = function (list, pattern, options) {
105
- return orig.match(list, pattern, ext(def, options))
78
+ const m = (p, pattern, options) => orig(p, pattern, ext(def, options))
79
+ m.Minimatch = class Minimatch extends orig.Minimatch {
80
+ constructor (pattern, options) {
81
+ super(pattern, ext(def, options))
82
+ }
106
83
  }
84
+ m.Minimatch.defaults = options => orig.defaults(ext(def, options)).Minimatch
85
+ m.filter = (pattern, options) => orig.filter(pattern, ext(def, options))
86
+ m.defaults = options => orig.defaults(ext(def, options))
87
+ m.makeRe = (pattern, options) => orig.makeRe(pattern, ext(def, options))
88
+ m.braceExpand = (pattern, options) => orig.braceExpand(pattern, ext(def, options))
89
+ m.match = (list, pattern, options) => orig.match(list, pattern, ext(def, options))
107
90
 
108
91
  return m
109
92
  }
110
93
 
111
- Minimatch.defaults = function (def) {
112
- return minimatch.defaults(def).Minimatch
113
- }
114
-
115
- function minimatch (p, pattern, options) {
116
- assertValidPattern(pattern)
117
-
118
- if (!options) options = {}
119
-
120
- // shortcut: comments match nothing.
121
- if (!options.nocomment && pattern.charAt(0) === '#') {
122
- return false
123
- }
124
-
125
- return new Minimatch(pattern, options).match(p)
126
- }
127
-
128
- function Minimatch (pattern, options) {
129
- if (!(this instanceof Minimatch)) {
130
- return new Minimatch(pattern, options)
131
- }
132
-
133
- assertValidPattern(pattern)
134
-
135
- if (!options) options = {}
136
-
137
- // windows support: need to use /, not \
138
- if (path.sep !== '/') {
139
- pattern = pattern.split(path.sep).join('/')
140
- }
141
-
142
- this.options = options
143
- this.set = []
144
- this.pattern = pattern
145
- this.regexp = null
146
- this.negate = false
147
- this.comment = false
148
- this.empty = false
149
- this.partial = !!options.partial
150
-
151
- // make the set of regexps etc.
152
- this.make()
153
- }
154
-
155
- Minimatch.prototype.debug = function () {}
156
-
157
- Minimatch.prototype.make = make
158
- function make () {
159
- var pattern = this.pattern
160
- var options = this.options
161
-
162
- // empty patterns and comments match nothing.
163
- if (!options.nocomment && pattern.charAt(0) === '#') {
164
- this.comment = true
165
- return
166
- }
167
- if (!pattern) {
168
- this.empty = true
169
- return
170
- }
171
-
172
- // step 1: figure out negation, etc.
173
- this.parseNegate()
174
-
175
- // step 2: expand braces
176
- var set = this.globSet = this.braceExpand()
177
-
178
- if (options.debug) this.debug = function debug() { console.error.apply(console, arguments) }
179
-
180
- this.debug(this.pattern, set)
181
-
182
- // step 3: now we have a set, so turn each one into a series of path-portion
183
- // matching patterns.
184
- // These will be regexps, except in the case of "**", which is
185
- // set to the GLOBSTAR object for globstar behavior,
186
- // and will not contain any / characters
187
- set = this.globParts = set.map(function (s) {
188
- return s.split(slashSplit)
189
- })
190
-
191
- this.debug(this.pattern, set)
192
-
193
- // glob --> regexps
194
- set = set.map(function (s, si, set) {
195
- return s.map(this.parse, this)
196
- }, this)
197
-
198
- this.debug(this.pattern, set)
199
-
200
- // filter out everything that didn't compile properly.
201
- set = set.filter(function (s) {
202
- return s.indexOf(false) === -1
203
- })
204
-
205
- this.debug(this.pattern, set)
206
-
207
- this.set = set
208
- }
209
-
210
- Minimatch.prototype.parseNegate = parseNegate
211
- function parseNegate () {
212
- var pattern = this.pattern
213
- var negate = false
214
- var options = this.options
215
- var negateOffset = 0
216
94
 
217
- if (options.nonegate) return
218
95
 
219
- for (var i = 0, l = pattern.length
220
- ; i < l && pattern.charAt(i) === '!'
221
- ; i++) {
222
- negate = !negate
223
- negateOffset++
224
- }
225
96
 
226
- if (negateOffset) this.pattern = pattern.substr(negateOffset)
227
- this.negate = negate
228
- }
229
97
 
230
98
  // Brace expansion:
231
99
  // a{b,c}d -> abd acd
@@ -237,24 +105,9 @@ function parseNegate () {
237
105
  // Invalid sets are not expanded.
238
106
  // a{2..}b -> a{2..}b
239
107
  // a{b}c -> a{b}c
240
- minimatch.braceExpand = function (pattern, options) {
241
- return braceExpand(pattern, options)
242
- }
243
-
244
- Minimatch.prototype.braceExpand = braceExpand
245
-
246
- function braceExpand (pattern, options) {
247
- if (!options) {
248
- if (this instanceof Minimatch) {
249
- options = this.options
250
- } else {
251
- options = {}
252
- }
253
- }
254
-
255
- pattern = typeof pattern === 'undefined'
256
- ? this.pattern : pattern
108
+ minimatch.braceExpand = (pattern, options) => braceExpand(pattern, options)
257
109
 
110
+ const braceExpand = (pattern, options = {}) => {
258
111
  assertValidPattern(pattern)
259
112
 
260
113
  // Thanks to Yeting Li <https://github.com/yetingli> for
@@ -267,8 +120,8 @@ function braceExpand (pattern, options) {
267
120
  return expand(pattern)
268
121
  }
269
122
 
270
- var MAX_PATTERN_LENGTH = 1024 * 64
271
- var assertValidPattern = function (pattern) {
123
+ const MAX_PATTERN_LENGTH = 1024 * 64
124
+ const assertValidPattern = pattern => {
272
125
  if (typeof pattern !== 'string') {
273
126
  throw new TypeError('invalid pattern')
274
127
  }
@@ -289,661 +142,738 @@ var assertValidPattern = function (pattern) {
289
142
  // when it is the *only* thing in a path portion. Otherwise, any series
290
143
  // of * is equivalent to a single *. Globstar behavior is enabled by
291
144
  // default, and can be disabled by setting options.noglobstar.
292
- Minimatch.prototype.parse = parse
293
- var SUBPARSE = {}
294
- function parse (pattern, isSub) {
295
- assertValidPattern(pattern)
145
+ const SUBPARSE = Symbol('subparse')
296
146
 
297
- var options = this.options
147
+ minimatch.makeRe = (pattern, options) =>
148
+ new Minimatch(pattern, options || {}).makeRe()
298
149
 
299
- // shortcuts
300
- if (pattern === '**') {
301
- if (!options.noglobstar)
302
- return GLOBSTAR
303
- else
304
- pattern = '*'
150
+ minimatch.match = (list, pattern, options = {}) => {
151
+ const mm = new Minimatch(pattern, options)
152
+ list = list.filter(f => mm.match(f))
153
+ if (mm.options.nonull && !list.length) {
154
+ list.push(pattern)
305
155
  }
306
- if (pattern === '') return ''
307
-
308
- var re = ''
309
- var hasMagic = false
310
- var escaping = false
311
- // ? => one single character
312
- var patternListStack = []
313
- var negativeLists = []
314
- var stateChar
315
- var inClass = false
316
- var reClassStart = -1
317
- var classStart = -1
318
- // . and .. never match anything that doesn't start with .,
319
- // even when options.dot is set.
320
- var patternStart = pattern.charAt(0) === '.' ? '' // anything
321
- // not (start or / followed by . or .. followed by / or end)
322
- : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
323
- : '(?!\\.)'
324
- var self = this
325
-
326
- function clearStateChar () {
327
- if (stateChar) {
328
- // we had some state-tracking character
329
- // that wasn't consumed by this pass.
330
- switch (stateChar) {
331
- case '*':
332
- re += star
333
- hasMagic = true
334
- break
335
- case '?':
336
- re += qmark
337
- hasMagic = true
338
- break
339
- default:
340
- re += '\\' + stateChar
341
- break
342
- }
343
- self.debug('clearStateChar %j %j', stateChar, re)
344
- stateChar = false
156
+ return list
157
+ }
158
+
159
+ // replace stuff like \* with *
160
+ const globUnescape = s => s.replace(/\\(.)/g, '$1')
161
+ const regExpEscape = s => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
162
+
163
+ class Minimatch {
164
+ constructor (pattern, options) {
165
+ assertValidPattern(pattern)
166
+
167
+ if (!options) options = {}
168
+
169
+ // windows support: need to use /, not \
170
+ if (path.sep !== '/') {
171
+ pattern = pattern.split(path.sep).join('/')
345
172
  }
173
+
174
+ this.options = options
175
+ this.set = []
176
+ this.pattern = pattern
177
+ this.regexp = null
178
+ this.negate = false
179
+ this.comment = false
180
+ this.empty = false
181
+ this.partial = !!options.partial
182
+
183
+ // make the set of regexps etc.
184
+ this.make()
346
185
  }
347
186
 
348
- for (var i = 0, len = pattern.length, c
349
- ; (i < len) && (c = pattern.charAt(i))
350
- ; i++) {
351
- this.debug('%s\t%s %s %j', pattern, i, re, c)
187
+ debug () {}
188
+
189
+ make () {
190
+ const pattern = this.pattern
191
+ const options = this.options
352
192
 
353
- // skip over any that are escaped.
354
- if (escaping && reSpecials[c]) {
355
- re += '\\' + c
356
- escaping = false
357
- continue
193
+ // empty patterns and comments match nothing.
194
+ if (!options.nocomment && pattern.charAt(0) === '#') {
195
+ this.comment = true
196
+ return
197
+ }
198
+ if (!pattern) {
199
+ this.empty = true
200
+ return
358
201
  }
359
202
 
360
- switch (c) {
361
- /* istanbul ignore next */
362
- case '/': {
363
- // completely not allowed, even escaped.
364
- // Should already be path-split by now.
365
- return false
366
- }
203
+ // step 1: figure out negation, etc.
204
+ this.parseNegate()
367
205
 
368
- case '\\':
369
- clearStateChar()
370
- escaping = true
371
- continue
372
-
373
- // the various stateChar values
374
- // for the "extglob" stuff.
375
- case '?':
376
- case '*':
377
- case '+':
378
- case '@':
379
- case '!':
380
- this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
381
-
382
- // all of those are literals inside a class, except that
383
- // the glob [!a] means [^a] in regexp
384
- if (inClass) {
385
- this.debug(' in class')
386
- if (c === '!' && i === classStart + 1) c = '^'
387
- re += c
388
- continue
389
- }
206
+ // step 2: expand braces
207
+ let set = this.globSet = this.braceExpand()
390
208
 
391
- // if we already have a stateChar, then it means
392
- // that there was something like ** or +? in there.
393
- // Handle the stateChar, then proceed with this one.
394
- self.debug('call clearStateChar %j', stateChar)
395
- clearStateChar()
396
- stateChar = c
397
- // if extglob is disabled, then +(asdf|foo) isn't a thing.
398
- // just clear the statechar *now*, rather than even diving into
399
- // the patternList stuff.
400
- if (options.noext) clearStateChar()
401
- continue
402
-
403
- case '(':
404
- if (inClass) {
405
- re += '('
406
- continue
407
- }
209
+ if (options.debug) this.debug = (...args) => console.error(...args)
408
210
 
409
- if (!stateChar) {
410
- re += '\\('
411
- continue
412
- }
211
+ this.debug(this.pattern, set)
413
212
 
414
- patternListStack.push({
415
- type: stateChar,
416
- start: i - 1,
417
- reStart: re.length,
418
- open: plTypes[stateChar].open,
419
- close: plTypes[stateChar].close
420
- })
421
- // negation is (?:(?!js)[^/]*)
422
- re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
423
- this.debug('plType %j %j', stateChar, re)
424
- stateChar = false
425
- continue
213
+ // step 3: now we have a set, so turn each one into a series of path-portion
214
+ // matching patterns.
215
+ // These will be regexps, except in the case of "**", which is
216
+ // set to the GLOBSTAR object for globstar behavior,
217
+ // and will not contain any / characters
218
+ set = this.globParts = set.map(s => s.split(slashSplit))
426
219
 
427
- case ')':
428
- if (inClass || !patternListStack.length) {
429
- re += '\\)'
430
- continue
431
- }
220
+ this.debug(this.pattern, set)
432
221
 
433
- clearStateChar()
434
- hasMagic = true
435
- var pl = patternListStack.pop()
436
- // negation is (?:(?!js)[^/]*)
437
- // The others are (?:<pattern>)<type>
438
- re += pl.close
439
- if (pl.type === '!') {
440
- negativeLists.push(pl)
441
- }
442
- pl.reEnd = re.length
443
- continue
444
-
445
- case '|':
446
- if (inClass || !patternListStack.length || escaping) {
447
- re += '\\|'
448
- escaping = false
449
- continue
450
- }
222
+ // glob --> regexps
223
+ set = set.map((s, si, set) => s.map(this.parse, this))
224
+
225
+ this.debug(this.pattern, set)
226
+
227
+ // filter out everything that didn't compile properly.
228
+ set = set.filter(s => s.indexOf(false) === -1)
229
+
230
+ this.debug(this.pattern, set)
451
231
 
452
- clearStateChar()
453
- re += '|'
454
- continue
232
+ this.set = set
233
+ }
234
+
235
+ parseNegate () {
236
+ if (this.options.nonegate) return
237
+
238
+ const pattern = this.pattern
239
+ let negate = false
240
+ let negateOffset = 0
241
+
242
+ for (let i = 0; i < pattern.length && pattern.charAt(i) === '!'; i++) {
243
+ negate = !negate
244
+ negateOffset++
245
+ }
246
+
247
+ if (negateOffset) this.pattern = pattern.substr(negateOffset)
248
+ this.negate = negate
249
+ }
455
250
 
456
- // these are mostly the same in regexp and glob
457
- case '[':
458
- // swallow any state-tracking char before the [
459
- clearStateChar()
251
+ // set partial to true to test if, for example,
252
+ // "/a/b" matches the start of "/*/b/*/d"
253
+ // Partial means, if you run out of file before you run
254
+ // out of pattern, then that's fine, as long as all
255
+ // the parts match.
256
+ matchOne (file, pattern, partial) {
257
+ var options = this.options
460
258
 
461
- if (inClass) {
462
- re += '\\' + c
463
- continue
259
+ this.debug('matchOne',
260
+ { 'this': this, file: file, pattern: pattern })
261
+
262
+ this.debug('matchOne', file.length, pattern.length)
263
+
264
+ for (var fi = 0,
265
+ pi = 0,
266
+ fl = file.length,
267
+ pl = pattern.length
268
+ ; (fi < fl) && (pi < pl)
269
+ ; fi++, pi++) {
270
+ this.debug('matchOne loop')
271
+ var p = pattern[pi]
272
+ var f = file[fi]
273
+
274
+ this.debug(pattern, p, f)
275
+
276
+ // should be impossible.
277
+ // some invalid regexp stuff in the set.
278
+ /* istanbul ignore if */
279
+ if (p === false) return false
280
+
281
+ if (p === GLOBSTAR) {
282
+ this.debug('GLOBSTAR', [pattern, p, f])
283
+
284
+ // "**"
285
+ // a/**/b/**/c would match the following:
286
+ // a/b/x/y/z/c
287
+ // a/x/y/z/b/c
288
+ // a/b/x/b/x/c
289
+ // a/b/c
290
+ // To do this, take the rest of the pattern after
291
+ // the **, and see if it would match the file remainder.
292
+ // If so, return success.
293
+ // If not, the ** "swallows" a segment, and try again.
294
+ // This is recursively awful.
295
+ //
296
+ // a/**/b/**/c matching a/b/x/y/z/c
297
+ // - a matches a
298
+ // - doublestar
299
+ // - matchOne(b/x/y/z/c, b/**/c)
300
+ // - b matches b
301
+ // - doublestar
302
+ // - matchOne(x/y/z/c, c) -> no
303
+ // - matchOne(y/z/c, c) -> no
304
+ // - matchOne(z/c, c) -> no
305
+ // - matchOne(c, c) yes, hit
306
+ var fr = fi
307
+ var pr = pi + 1
308
+ if (pr === pl) {
309
+ this.debug('** at the end')
310
+ // a ** at the end will just swallow the rest.
311
+ // We have found a match.
312
+ // however, it will not swallow /.x, unless
313
+ // options.dot is set.
314
+ // . and .. are *never* matched by **, for explosively
315
+ // exponential reasons.
316
+ for (; fi < fl; fi++) {
317
+ if (file[fi] === '.' || file[fi] === '..' ||
318
+ (!options.dot && file[fi].charAt(0) === '.')) return false
319
+ }
320
+ return true
464
321
  }
465
322
 
466
- inClass = true
467
- classStart = i
468
- reClassStart = re.length
469
- re += c
470
- continue
471
-
472
- case ']':
473
- // a right bracket shall lose its special
474
- // meaning and represent itself in
475
- // a bracket expression if it occurs
476
- // first in the list. -- POSIX.2 2.8.3.2
477
- if (i === classStart + 1 || !inClass) {
478
- re += '\\' + c
479
- escaping = false
480
- continue
323
+ // ok, let's see if we can swallow whatever we can.
324
+ while (fr < fl) {
325
+ var swallowee = file[fr]
326
+
327
+ this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
328
+
329
+ // XXX remove this slice. Just pass the start index.
330
+ if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
331
+ this.debug('globstar found match!', fr, fl, swallowee)
332
+ // found a match.
333
+ return true
334
+ } else {
335
+ // can't swallow "." or ".." ever.
336
+ // can only swallow ".foo" when explicitly asked.
337
+ if (swallowee === '.' || swallowee === '..' ||
338
+ (!options.dot && swallowee.charAt(0) === '.')) {
339
+ this.debug('dot detected!', file, fr, pattern, pr)
340
+ break
341
+ }
342
+
343
+ // ** swallows a segment, and continue.
344
+ this.debug('globstar swallow a segment, and continue')
345
+ fr++
346
+ }
481
347
  }
482
348
 
483
- // handle the case where we left a class open.
484
- // "[z-a]" is valid, equivalent to "\[z-a\]"
485
- // split where the last [ was, make sure we don't have
486
- // an invalid re. if so, re-walk the contents of the
487
- // would-be class to re-translate any characters that
488
- // were passed through as-is
489
- // TODO: It would probably be faster to determine this
490
- // without a try/catch and a new RegExp, but it's tricky
491
- // to do safely. For now, this is safe and works.
492
- var cs = pattern.substring(classStart + 1, i)
493
- try {
494
- RegExp('[' + cs + ']')
495
- } catch (er) {
496
- // not a valid class!
497
- var sp = this.parse(cs, SUBPARSE)
498
- re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
499
- hasMagic = hasMagic || sp[1]
500
- inClass = false
501
- continue
349
+ // no match was found.
350
+ // However, in partial mode, we can't say this is necessarily over.
351
+ // If there's more *pattern* left, then
352
+ /* istanbul ignore if */
353
+ if (partial) {
354
+ // ran out of file
355
+ this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
356
+ if (fr === fl) return true
502
357
  }
358
+ return false
359
+ }
503
360
 
504
- // finish up the class.
505
- hasMagic = true
506
- inClass = false
507
- re += c
508
- continue
509
-
510
- default:
511
- // swallow any state char that wasn't consumed
512
- clearStateChar()
513
-
514
- if (escaping) {
515
- // no need
516
- escaping = false
517
- } else if (reSpecials[c]
518
- && !(c === '^' && inClass)) {
519
- re += '\\'
361
+ // something other than **
362
+ // non-magic patterns just have to match exactly
363
+ // patterns with magic have been turned into regexps.
364
+ var hit
365
+ if (typeof p === 'string') {
366
+ if (options.nocase) {
367
+ hit = f.toLowerCase() === p.toLowerCase()
368
+ } else {
369
+ hit = f === p
520
370
  }
371
+ this.debug('string match', p, f, hit)
372
+ } else {
373
+ hit = f.match(p)
374
+ this.debug('pattern match', p, f, hit)
375
+ }
521
376
 
522
- re += c
523
-
524
- } // switch
525
- } // for
526
-
527
- // handle the case where we left a class open.
528
- // "[abc" is valid, equivalent to "\[abc"
529
- if (inClass) {
530
- // split where the last [ was, and escape it
531
- // this is a huge pita. We now have to re-walk
532
- // the contents of the would-be class to re-translate
533
- // any characters that were passed through as-is
534
- cs = pattern.substr(classStart + 1)
535
- sp = this.parse(cs, SUBPARSE)
536
- re = re.substr(0, reClassStart) + '\\[' + sp[0]
537
- hasMagic = hasMagic || sp[1]
538
- }
377
+ if (!hit) return false
378
+ }
539
379
 
540
- // handle the case where we had a +( thing at the *end*
541
- // of the pattern.
542
- // each pattern list stack adds 3 chars, and we need to go through
543
- // and escape any | chars that were passed through as-is for the regexp.
544
- // Go through and escape them, taking care not to double-escape any
545
- // | chars that were already escaped.
546
- for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
547
- var tail = re.slice(pl.reStart + pl.open.length)
548
- this.debug('setting tail', re, pl)
549
- // maybe some even number of \, then maybe 1 \, followed by a |
550
- tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) {
551
- if (!$2) {
552
- // the | isn't already escaped, so escape it.
553
- $2 = '\\'
554
- }
380
+ // Note: ending in / means that we'll get a final ""
381
+ // at the end of the pattern. This can only match a
382
+ // corresponding "" at the end of the file.
383
+ // If the file ends in /, then it can only match a
384
+ // a pattern that ends in /, unless the pattern just
385
+ // doesn't have any more for it. But, a/b/ should *not*
386
+ // match "a/b/*", even though "" matches against the
387
+ // [^/]*? pattern, except in partial mode, where it might
388
+ // simply not be reached yet.
389
+ // However, a/b/ should still satisfy a/*
390
+
391
+ // now either we fell off the end of the pattern, or we're done.
392
+ if (fi === fl && pi === pl) {
393
+ // ran out of pattern and filename at the same time.
394
+ // an exact hit!
395
+ return true
396
+ } else if (fi === fl) {
397
+ // ran out of file, but still had pattern left.
398
+ // this is ok if we're doing the match as part of
399
+ // a glob fs traversal.
400
+ return partial
401
+ } else /* istanbul ignore else */ if (pi === pl) {
402
+ // ran out of pattern, still have file left.
403
+ // this is only acceptable if we're on the very last
404
+ // empty segment of a file with a trailing slash.
405
+ // a/* should match a/b/
406
+ return (fi === fl - 1) && (file[fi] === '')
407
+ }
555
408
 
556
- // need to escape all those slashes *again*, without escaping the
557
- // one that we need for escaping the | character. As it works out,
558
- // escaping an even number of slashes can be done by simply repeating
559
- // it exactly after itself. That's why this trick works.
560
- //
561
- // I am sorry that you have to see this.
562
- return $1 + $1 + $2 + '|'
563
- })
564
-
565
- this.debug('tail=%j\n %s', tail, tail, pl, re)
566
- var t = pl.type === '*' ? star
567
- : pl.type === '?' ? qmark
568
- : '\\' + pl.type
569
-
570
- hasMagic = true
571
- re = re.slice(0, pl.reStart) + t + '\\(' + tail
409
+ // should be unreachable.
410
+ /* istanbul ignore next */
411
+ throw new Error('wtf?')
572
412
  }
573
413
 
574
- // handle trailing things that only matter at the very end.
575
- clearStateChar()
576
- if (escaping) {
577
- // trailing \\
578
- re += '\\\\'
414
+ braceExpand () {
415
+ return braceExpand(this.pattern, this.options)
579
416
  }
580
417
 
581
- // only need to apply the nodot start if the re starts with
582
- // something that could conceivably capture a dot
583
- var addPatternStart = false
584
- switch (re.charAt(0)) {
585
- case '[': case '.': case '(': addPatternStart = true
586
- }
418
+ parse (pattern, isSub) {
419
+ assertValidPattern(pattern)
587
420
 
588
- // Hack to work around lack of negative lookbehind in JS
589
- // A pattern like: *.!(x).!(y|z) needs to ensure that a name
590
- // like 'a.xyz.yz' doesn't match. So, the first negative
591
- // lookahead, has to look ALL the way ahead, to the end of
592
- // the pattern.
593
- for (var n = negativeLists.length - 1; n > -1; n--) {
594
- var nl = negativeLists[n]
595
-
596
- var nlBefore = re.slice(0, nl.reStart)
597
- var nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
598
- var nlLast = re.slice(nl.reEnd - 8, nl.reEnd)
599
- var nlAfter = re.slice(nl.reEnd)
600
-
601
- nlLast += nlAfter
602
-
603
- // Handle nested stuff like *(*.js|!(*.json)), where open parens
604
- // mean that we should *not* include the ) in the bit that is considered
605
- // "after" the negated section.
606
- var openParensBefore = nlBefore.split('(').length - 1
607
- var cleanAfter = nlAfter
608
- for (i = 0; i < openParensBefore; i++) {
609
- cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
610
- }
611
- nlAfter = cleanAfter
421
+ const options = this.options
612
422
 
613
- var dollar = ''
614
- if (nlAfter === '' && isSub !== SUBPARSE) {
615
- dollar = '$'
423
+ // shortcuts
424
+ if (pattern === '**') {
425
+ if (!options.noglobstar)
426
+ return GLOBSTAR
427
+ else
428
+ pattern = '*'
429
+ }
430
+ if (pattern === '') return ''
431
+
432
+ let re = ''
433
+ let hasMagic = false
434
+ let escaping = false
435
+ // ? => one single character
436
+ const patternListStack = []
437
+ const negativeLists = []
438
+ let stateChar
439
+ let inClass = false
440
+ let reClassStart = -1
441
+ let classStart = -1
442
+ let cs
443
+ let pl
444
+ let sp
445
+ // . and .. never match anything that doesn't start with .,
446
+ // even when options.dot is set.
447
+ const patternStart = pattern.charAt(0) === '.' ? '' // anything
448
+ // not (start or / followed by . or .. followed by / or end)
449
+ : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
450
+ : '(?!\\.)'
451
+
452
+ const clearStateChar = () => {
453
+ if (stateChar) {
454
+ // we had some state-tracking character
455
+ // that wasn't consumed by this pass.
456
+ switch (stateChar) {
457
+ case '*':
458
+ re += star
459
+ hasMagic = true
460
+ break
461
+ case '?':
462
+ re += qmark
463
+ hasMagic = true
464
+ break
465
+ default:
466
+ re += '\\' + stateChar
467
+ break
468
+ }
469
+ this.debug('clearStateChar %j %j', stateChar, re)
470
+ stateChar = false
471
+ }
616
472
  }
617
- var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast
618
- re = newRe
619
- }
620
473
 
621
- // if the re is not "" at this point, then we need to make sure
622
- // it doesn't match against an empty path part.
623
- // Otherwise a/* will match a/, which it should not.
624
- if (re !== '' && hasMagic) {
625
- re = '(?=.)' + re
626
- }
474
+ for (let i = 0, c; (i < pattern.length) && (c = pattern.charAt(i)); i++) {
475
+ this.debug('%s\t%s %s %j', pattern, i, re, c)
627
476
 
628
- if (addPatternStart) {
629
- re = patternStart + re
630
- }
477
+ // skip over any that are escaped.
478
+ if (escaping && reSpecials[c]) {
479
+ re += '\\' + c
480
+ escaping = false
481
+ continue
482
+ }
631
483
 
632
- // parsing just a piece of a larger pattern.
633
- if (isSub === SUBPARSE) {
634
- return [re, hasMagic]
635
- }
484
+ switch (c) {
485
+ /* istanbul ignore next */
486
+ case '/': {
487
+ // completely not allowed, even escaped.
488
+ // Should already be path-split by now.
489
+ return false
490
+ }
636
491
 
637
- // skip the regexp for non-magical patterns
638
- // unescape anything in it, though, so that it'll be
639
- // an exact match against a file etc.
640
- if (!hasMagic) {
641
- return globUnescape(pattern)
642
- }
492
+ case '\\':
493
+ clearStateChar()
494
+ escaping = true
495
+ continue
643
496
 
644
- var flags = options.nocase ? 'i' : ''
645
- try {
646
- var regExp = new RegExp('^' + re + '$', flags)
647
- } catch (er) /* istanbul ignore next - should be impossible */ {
648
- // If it was an invalid regular expression, then it can't match
649
- // anything. This trick looks for a character after the end of
650
- // the string, which is of course impossible, except in multi-line
651
- // mode, but it's not a /m regex.
652
- return new RegExp('$.')
653
- }
497
+ // the various stateChar values
498
+ // for the "extglob" stuff.
499
+ case '?':
500
+ case '*':
501
+ case '+':
502
+ case '@':
503
+ case '!':
504
+ this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
505
+
506
+ // all of those are literals inside a class, except that
507
+ // the glob [!a] means [^a] in regexp
508
+ if (inClass) {
509
+ this.debug(' in class')
510
+ if (c === '!' && i === classStart + 1) c = '^'
511
+ re += c
512
+ continue
513
+ }
654
514
 
655
- regExp._glob = pattern
656
- regExp._src = re
515
+ // if we already have a stateChar, then it means
516
+ // that there was something like ** or +? in there.
517
+ // Handle the stateChar, then proceed with this one.
518
+ this.debug('call clearStateChar %j', stateChar)
519
+ clearStateChar()
520
+ stateChar = c
521
+ // if extglob is disabled, then +(asdf|foo) isn't a thing.
522
+ // just clear the statechar *now*, rather than even diving into
523
+ // the patternList stuff.
524
+ if (options.noext) clearStateChar()
525
+ continue
526
+
527
+ case '(':
528
+ if (inClass) {
529
+ re += '('
530
+ continue
531
+ }
657
532
 
658
- return regExp
659
- }
533
+ if (!stateChar) {
534
+ re += '\\('
535
+ continue
536
+ }
660
537
 
661
- minimatch.makeRe = function (pattern, options) {
662
- return new Minimatch(pattern, options || {}).makeRe()
663
- }
538
+ patternListStack.push({
539
+ type: stateChar,
540
+ start: i - 1,
541
+ reStart: re.length,
542
+ open: plTypes[stateChar].open,
543
+ close: plTypes[stateChar].close
544
+ })
545
+ // negation is (?:(?!js)[^/]*)
546
+ re += stateChar === '!' ? '(?:(?!(?:' : '(?:'
547
+ this.debug('plType %j %j', stateChar, re)
548
+ stateChar = false
549
+ continue
550
+
551
+ case ')':
552
+ if (inClass || !patternListStack.length) {
553
+ re += '\\)'
554
+ continue
555
+ }
664
556
 
665
- Minimatch.prototype.makeRe = makeRe
666
- function makeRe () {
667
- if (this.regexp || this.regexp === false) return this.regexp
557
+ clearStateChar()
558
+ hasMagic = true
559
+ pl = patternListStack.pop()
560
+ // negation is (?:(?!js)[^/]*)
561
+ // The others are (?:<pattern>)<type>
562
+ re += pl.close
563
+ if (pl.type === '!') {
564
+ negativeLists.push(pl)
565
+ }
566
+ pl.reEnd = re.length
567
+ continue
568
+
569
+ case '|':
570
+ if (inClass || !patternListStack.length || escaping) {
571
+ re += '\\|'
572
+ escaping = false
573
+ continue
574
+ }
668
575
 
669
- // at this point, this.set is a 2d array of partial
670
- // pattern strings, or "**".
671
- //
672
- // It's better to use .match(). This function shouldn't
673
- // be used, really, but it's pretty convenient sometimes,
674
- // when you just want to work with a regex.
675
- var set = this.set
576
+ clearStateChar()
577
+ re += '|'
578
+ continue
676
579
 
677
- if (!set.length) {
678
- this.regexp = false
679
- return this.regexp
680
- }
681
- var options = this.options
682
-
683
- var twoStar = options.noglobstar ? star
684
- : options.dot ? twoStarDot
685
- : twoStarNoDot
686
- var flags = options.nocase ? 'i' : ''
687
-
688
- var re = set.map(function (pattern) {
689
- return pattern.map(function (p) {
690
- return (p === GLOBSTAR) ? twoStar
691
- : (typeof p === 'string') ? regExpEscape(p)
692
- : p._src
693
- }).join('\\\/')
694
- }).join('|')
695
-
696
- // must match entire pattern
697
- // ending in a * or ** will make it less strict.
698
- re = '^(?:' + re + ')$'
699
-
700
- // can match anything, as long as it's not this.
701
- if (this.negate) re = '^(?!' + re + ').*$'
702
-
703
- try {
704
- this.regexp = new RegExp(re, flags)
705
- } catch (ex) /* istanbul ignore next - should be impossible */ {
706
- this.regexp = false
707
- }
708
- return this.regexp
709
- }
580
+ // these are mostly the same in regexp and glob
581
+ case '[':
582
+ // swallow any state-tracking char before the [
583
+ clearStateChar()
710
584
 
711
- minimatch.match = function (list, pattern, options) {
712
- options = options || {}
713
- var mm = new Minimatch(pattern, options)
714
- list = list.filter(function (f) {
715
- return mm.match(f)
716
- })
717
- if (mm.options.nonull && !list.length) {
718
- list.push(pattern)
719
- }
720
- return list
721
- }
585
+ if (inClass) {
586
+ re += '\\' + c
587
+ continue
588
+ }
589
+
590
+ inClass = true
591
+ classStart = i
592
+ reClassStart = re.length
593
+ re += c
594
+ continue
595
+
596
+ case ']':
597
+ // a right bracket shall lose its special
598
+ // meaning and represent itself in
599
+ // a bracket expression if it occurs
600
+ // first in the list. -- POSIX.2 2.8.3.2
601
+ if (i === classStart + 1 || !inClass) {
602
+ re += '\\' + c
603
+ escaping = false
604
+ continue
605
+ }
722
606
 
723
- Minimatch.prototype.match = function match (f, partial) {
724
- if (typeof partial === 'undefined') partial = this.partial
725
- this.debug('match', f, this.pattern)
726
- // short-circuit in the case of busted things.
727
- // comments, etc.
728
- if (this.comment) return false
729
- if (this.empty) return f === ''
607
+ // handle the case where we left a class open.
608
+ // "[z-a]" is valid, equivalent to "\[z-a\]"
609
+ // split where the last [ was, make sure we don't have
610
+ // an invalid re. if so, re-walk the contents of the
611
+ // would-be class to re-translate any characters that
612
+ // were passed through as-is
613
+ // TODO: It would probably be faster to determine this
614
+ // without a try/catch and a new RegExp, but it's tricky
615
+ // to do safely. For now, this is safe and works.
616
+ cs = pattern.substring(classStart + 1, i)
617
+ try {
618
+ RegExp('[' + cs + ']')
619
+ } catch (er) {
620
+ // not a valid class!
621
+ sp = this.parse(cs, SUBPARSE)
622
+ re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
623
+ hasMagic = hasMagic || sp[1]
624
+ inClass = false
625
+ continue
626
+ }
730
627
 
731
- if (f === '/' && partial) return true
628
+ // finish up the class.
629
+ hasMagic = true
630
+ inClass = false
631
+ re += c
632
+ continue
732
633
 
733
- var options = this.options
634
+ default:
635
+ // swallow any state char that wasn't consumed
636
+ clearStateChar()
637
+
638
+ if (escaping) {
639
+ // no need
640
+ escaping = false
641
+ } else if (reSpecials[c]
642
+ && !(c === '^' && inClass)) {
643
+ re += '\\'
644
+ }
734
645
 
735
- // windows: need to use /, not \
736
- if (path.sep !== '/') {
737
- f = f.split(path.sep).join('/')
738
- }
646
+ re += c
647
+
648
+ } // switch
649
+ } // for
650
+
651
+ // handle the case where we left a class open.
652
+ // "[abc" is valid, equivalent to "\[abc"
653
+ if (inClass) {
654
+ // split where the last [ was, and escape it
655
+ // this is a huge pita. We now have to re-walk
656
+ // the contents of the would-be class to re-translate
657
+ // any characters that were passed through as-is
658
+ cs = pattern.substr(classStart + 1)
659
+ sp = this.parse(cs, SUBPARSE)
660
+ re = re.substr(0, reClassStart) + '\\[' + sp[0]
661
+ hasMagic = hasMagic || sp[1]
662
+ }
663
+
664
+ // handle the case where we had a +( thing at the *end*
665
+ // of the pattern.
666
+ // each pattern list stack adds 3 chars, and we need to go through
667
+ // and escape any | chars that were passed through as-is for the regexp.
668
+ // Go through and escape them, taking care not to double-escape any
669
+ // | chars that were already escaped.
670
+ for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
671
+ let tail
672
+ tail = re.slice(pl.reStart + pl.open.length)
673
+ this.debug('setting tail', re, pl)
674
+ // maybe some even number of \, then maybe 1 \, followed by a |
675
+ tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, (_, $1, $2) => {
676
+ if (!$2) {
677
+ // the | isn't already escaped, so escape it.
678
+ $2 = '\\'
679
+ }
680
+
681
+ // need to escape all those slashes *again*, without escaping the
682
+ // one that we need for escaping the | character. As it works out,
683
+ // escaping an even number of slashes can be done by simply repeating
684
+ // it exactly after itself. That's why this trick works.
685
+ //
686
+ // I am sorry that you have to see this.
687
+ return $1 + $1 + $2 + '|'
688
+ })
689
+
690
+ this.debug('tail=%j\n %s', tail, tail, pl, re)
691
+ const t = pl.type === '*' ? star
692
+ : pl.type === '?' ? qmark
693
+ : '\\' + pl.type
694
+
695
+ hasMagic = true
696
+ re = re.slice(0, pl.reStart) + t + '\\(' + tail
697
+ }
698
+
699
+ // handle trailing things that only matter at the very end.
700
+ clearStateChar()
701
+ if (escaping) {
702
+ // trailing \\
703
+ re += '\\\\'
704
+ }
705
+
706
+ // only need to apply the nodot start if the re starts with
707
+ // something that could conceivably capture a dot
708
+ const addPatternStart = addPatternStartSet[re.charAt(0)]
709
+
710
+ // Hack to work around lack of negative lookbehind in JS
711
+ // A pattern like: *.!(x).!(y|z) needs to ensure that a name
712
+ // like 'a.xyz.yz' doesn't match. So, the first negative
713
+ // lookahead, has to look ALL the way ahead, to the end of
714
+ // the pattern.
715
+ for (let n = negativeLists.length - 1; n > -1; n--) {
716
+ const nl = negativeLists[n]
717
+
718
+ const nlBefore = re.slice(0, nl.reStart)
719
+ const nlFirst = re.slice(nl.reStart, nl.reEnd - 8)
720
+ let nlAfter = re.slice(nl.reEnd)
721
+ const nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + nlAfter
722
+
723
+ // Handle nested stuff like *(*.js|!(*.json)), where open parens
724
+ // mean that we should *not* include the ) in the bit that is considered
725
+ // "after" the negated section.
726
+ const openParensBefore = nlBefore.split('(').length - 1
727
+ let cleanAfter = nlAfter
728
+ for (let i = 0; i < openParensBefore; i++) {
729
+ cleanAfter = cleanAfter.replace(/\)[+*?]?/, '')
730
+ }
731
+ nlAfter = cleanAfter
732
+
733
+ const dollar = nlAfter === '' && isSub !== SUBPARSE ? '$' : ''
734
+ re = nlBefore + nlFirst + nlAfter + dollar + nlLast
735
+ }
736
+
737
+ // if the re is not "" at this point, then we need to make sure
738
+ // it doesn't match against an empty path part.
739
+ // Otherwise a/* will match a/, which it should not.
740
+ if (re !== '' && hasMagic) {
741
+ re = '(?=.)' + re
742
+ }
739
743
 
740
- // treat the test path as a set of pathparts.
741
- f = f.split(slashSplit)
742
- this.debug(this.pattern, 'split', f)
744
+ if (addPatternStart) {
745
+ re = patternStart + re
746
+ }
743
747
 
744
- // just ONE of the pattern sets in this.set needs to match
745
- // in order for it to be valid. If negating, then just one
746
- // match means that we have failed.
747
- // Either way, return on the first hit.
748
+ // parsing just a piece of a larger pattern.
749
+ if (isSub === SUBPARSE) {
750
+ return [re, hasMagic]
751
+ }
748
752
 
749
- var set = this.set
750
- this.debug(this.pattern, 'set', set)
753
+ // skip the regexp for non-magical patterns
754
+ // unescape anything in it, though, so that it'll be
755
+ // an exact match against a file etc.
756
+ if (!hasMagic) {
757
+ return globUnescape(pattern)
758
+ }
751
759
 
752
- // Find the basename of the path by looking for the last non-empty segment
753
- var filename
754
- var i
755
- for (i = f.length - 1; i >= 0; i--) {
756
- filename = f[i]
757
- if (filename) break
760
+ const flags = options.nocase ? 'i' : ''
761
+ try {
762
+ return Object.assign(new RegExp('^' + re + '$', flags), {
763
+ _glob: pattern,
764
+ _src: re,
765
+ })
766
+ } catch (er) /* istanbul ignore next - should be impossible */ {
767
+ // If it was an invalid regular expression, then it can't match
768
+ // anything. This trick looks for a character after the end of
769
+ // the string, which is of course impossible, except in multi-line
770
+ // mode, but it's not a /m regex.
771
+ return new RegExp('$.')
772
+ }
758
773
  }
759
774
 
760
- for (i = 0; i < set.length; i++) {
761
- var pattern = set[i]
762
- var file = f
763
- if (options.matchBase && pattern.length === 1) {
764
- file = [filename]
775
+ makeRe () {
776
+ if (this.regexp || this.regexp === false) return this.regexp
777
+
778
+ // at this point, this.set is a 2d array of partial
779
+ // pattern strings, or "**".
780
+ //
781
+ // It's better to use .match(). This function shouldn't
782
+ // be used, really, but it's pretty convenient sometimes,
783
+ // when you just want to work with a regex.
784
+ const set = this.set
785
+
786
+ if (!set.length) {
787
+ this.regexp = false
788
+ return this.regexp
765
789
  }
766
- var hit = this.matchOne(file, pattern, partial)
767
- if (hit) {
768
- if (options.flipNegate) return true
769
- return !this.negate
790
+ const options = this.options
791
+
792
+ const twoStar = options.noglobstar ? star
793
+ : options.dot ? twoStarDot
794
+ : twoStarNoDot
795
+ const flags = options.nocase ? 'i' : ''
796
+
797
+ let re = set.map(pattern =>
798
+ pattern.map(p =>
799
+ (p === GLOBSTAR) ? twoStar
800
+ : (typeof p === 'string') ? regExpEscape(p)
801
+ : p._src
802
+ ).join('\\\/')
803
+ ).join('|')
804
+
805
+ // must match entire pattern
806
+ // ending in a * or ** will make it less strict.
807
+ re = '^(?:' + re + ')$'
808
+
809
+ // can match anything, as long as it's not this.
810
+ if (this.negate) re = '^(?!' + re + ').*$'
811
+
812
+ try {
813
+ this.regexp = new RegExp(re, flags)
814
+ } catch (ex) /* istanbul ignore next - should be impossible */ {
815
+ this.regexp = false
770
816
  }
817
+ return this.regexp
771
818
  }
772
819
 
773
- // didn't get any hits. this is success if it's a negative
774
- // pattern, failure otherwise.
775
- if (options.flipNegate) return false
776
- return this.negate
777
- }
820
+ match (f, partial = this.partial) {
821
+ this.debug('match', f, this.pattern)
822
+ // short-circuit in the case of busted things.
823
+ // comments, etc.
824
+ if (this.comment) return false
825
+ if (this.empty) return f === ''
778
826
 
779
- // set partial to true to test if, for example,
780
- // "/a/b" matches the start of "/*/b/*/d"
781
- // Partial means, if you run out of file before you run
782
- // out of pattern, then that's fine, as long as all
783
- // the parts match.
784
- Minimatch.prototype.matchOne = function (file, pattern, partial) {
785
- var options = this.options
786
-
787
- this.debug('matchOne',
788
- { 'this': this, file: file, pattern: pattern })
789
-
790
- this.debug('matchOne', file.length, pattern.length)
791
-
792
- for (var fi = 0,
793
- pi = 0,
794
- fl = file.length,
795
- pl = pattern.length
796
- ; (fi < fl) && (pi < pl)
797
- ; fi++, pi++) {
798
- this.debug('matchOne loop')
799
- var p = pattern[pi]
800
- var f = file[fi]
801
-
802
- this.debug(pattern, p, f)
803
-
804
- // should be impossible.
805
- // some invalid regexp stuff in the set.
806
- /* istanbul ignore if */
807
- if (p === false) return false
808
-
809
- if (p === GLOBSTAR) {
810
- this.debug('GLOBSTAR', [pattern, p, f])
811
-
812
- // "**"
813
- // a/**/b/**/c would match the following:
814
- // a/b/x/y/z/c
815
- // a/x/y/z/b/c
816
- // a/b/x/b/x/c
817
- // a/b/c
818
- // To do this, take the rest of the pattern after
819
- // the **, and see if it would match the file remainder.
820
- // If so, return success.
821
- // If not, the ** "swallows" a segment, and try again.
822
- // This is recursively awful.
823
- //
824
- // a/**/b/**/c matching a/b/x/y/z/c
825
- // - a matches a
826
- // - doublestar
827
- // - matchOne(b/x/y/z/c, b/**/c)
828
- // - b matches b
829
- // - doublestar
830
- // - matchOne(x/y/z/c, c) -> no
831
- // - matchOne(y/z/c, c) -> no
832
- // - matchOne(z/c, c) -> no
833
- // - matchOne(c, c) yes, hit
834
- var fr = fi
835
- var pr = pi + 1
836
- if (pr === pl) {
837
- this.debug('** at the end')
838
- // a ** at the end will just swallow the rest.
839
- // We have found a match.
840
- // however, it will not swallow /.x, unless
841
- // options.dot is set.
842
- // . and .. are *never* matched by **, for explosively
843
- // exponential reasons.
844
- for (; fi < fl; fi++) {
845
- if (file[fi] === '.' || file[fi] === '..' ||
846
- (!options.dot && file[fi].charAt(0) === '.')) return false
847
- }
848
- return true
849
- }
827
+ if (f === '/' && partial) return true
850
828
 
851
- // ok, let's see if we can swallow whatever we can.
852
- while (fr < fl) {
853
- var swallowee = file[fr]
829
+ const options = this.options
854
830
 
855
- this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
831
+ // windows: need to use /, not \
832
+ if (path.sep !== '/') {
833
+ f = f.split(path.sep).join('/')
834
+ }
856
835
 
857
- // XXX remove this slice. Just pass the start index.
858
- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
859
- this.debug('globstar found match!', fr, fl, swallowee)
860
- // found a match.
861
- return true
862
- } else {
863
- // can't swallow "." or ".." ever.
864
- // can only swallow ".foo" when explicitly asked.
865
- if (swallowee === '.' || swallowee === '..' ||
866
- (!options.dot && swallowee.charAt(0) === '.')) {
867
- this.debug('dot detected!', file, fr, pattern, pr)
868
- break
869
- }
836
+ // treat the test path as a set of pathparts.
837
+ f = f.split(slashSplit)
838
+ this.debug(this.pattern, 'split', f)
870
839
 
871
- // ** swallows a segment, and continue.
872
- this.debug('globstar swallow a segment, and continue')
873
- fr++
874
- }
875
- }
840
+ // just ONE of the pattern sets in this.set needs to match
841
+ // in order for it to be valid. If negating, then just one
842
+ // match means that we have failed.
843
+ // Either way, return on the first hit.
876
844
 
877
- // no match was found.
878
- // However, in partial mode, we can't say this is necessarily over.
879
- // If there's more *pattern* left, then
880
- /* istanbul ignore if */
881
- if (partial) {
882
- // ran out of file
883
- this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
884
- if (fr === fl) return true
885
- }
886
- return false
845
+ const set = this.set
846
+ this.debug(this.pattern, 'set', set)
847
+
848
+ // Find the basename of the path by looking for the last non-empty segment
849
+ let filename
850
+ for (let i = f.length - 1; i >= 0; i--) {
851
+ filename = f[i]
852
+ if (filename) break
887
853
  }
888
854
 
889
- // something other than **
890
- // non-magic patterns just have to match exactly
891
- // patterns with magic have been turned into regexps.
892
- var hit
893
- if (typeof p === 'string') {
894
- if (options.nocase) {
895
- hit = f.toLowerCase() === p.toLowerCase()
896
- } else {
897
- hit = f === p
855
+ for (let i = 0; i < set.length; i++) {
856
+ const pattern = set[i]
857
+ let file = f
858
+ if (options.matchBase && pattern.length === 1) {
859
+ file = [filename]
860
+ }
861
+ const hit = this.matchOne(file, pattern, partial)
862
+ if (hit) {
863
+ if (options.flipNegate) return true
864
+ return !this.negate
898
865
  }
899
- this.debug('string match', p, f, hit)
900
- } else {
901
- hit = f.match(p)
902
- this.debug('pattern match', p, f, hit)
903
866
  }
904
867
 
905
- if (!hit) return false
868
+ // didn't get any hits. this is success if it's a negative
869
+ // pattern, failure otherwise.
870
+ if (options.flipNegate) return false
871
+ return this.negate
906
872
  }
907
873
 
908
- // Note: ending in / means that we'll get a final ""
909
- // at the end of the pattern. This can only match a
910
- // corresponding "" at the end of the file.
911
- // If the file ends in /, then it can only match a
912
- // a pattern that ends in /, unless the pattern just
913
- // doesn't have any more for it. But, a/b/ should *not*
914
- // match "a/b/*", even though "" matches against the
915
- // [^/]*? pattern, except in partial mode, where it might
916
- // simply not be reached yet.
917
- // However, a/b/ should still satisfy a/*
918
-
919
- // now either we fell off the end of the pattern, or we're done.
920
- if (fi === fl && pi === pl) {
921
- // ran out of pattern and filename at the same time.
922
- // an exact hit!
923
- return true
924
- } else if (fi === fl) {
925
- // ran out of file, but still had pattern left.
926
- // this is ok if we're doing the match as part of
927
- // a glob fs traversal.
928
- return partial
929
- } else /* istanbul ignore else */ if (pi === pl) {
930
- // ran out of pattern, still have file left.
931
- // this is only acceptable if we're on the very last
932
- // empty segment of a file with a trailing slash.
933
- // a/* should match a/b/
934
- return (fi === fl - 1) && (file[fi] === '')
874
+ static defaults (def) {
875
+ return minimatch.defaults(def).Minimatch
935
876
  }
936
-
937
- // should be unreachable.
938
- /* istanbul ignore next */
939
- throw new Error('wtf?')
940
877
  }
941
878
 
942
- // replace stuff like \* with *
943
- function globUnescape (s) {
944
- return s.replace(/\\(.)/g, '$1')
945
- }
946
-
947
- function regExpEscape (s) {
948
- return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
949
- }
879
+ minimatch.Minimatch = Minimatch