minimatch 7.0.0 → 7.0.1

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.
@@ -0,0 +1,787 @@
1
+ // this is a much more "mini" minimatch, optimized for use in a recursive
2
+ // glob walk.
3
+ //
4
+ // String parts of the pattern set are only parsed and used on demand,
5
+ // so that we don't have to bother parsing parts of the pattern that
6
+ // don't ever end up being used.
7
+ import expand from 'brace-expansion';
8
+ import { GLOBSTAR } from './index.js';
9
+ /* c8 ignore start */
10
+ const defaultPlatform = typeof process === 'object' &&
11
+ !!process &&
12
+ typeof process.platform === 'string'
13
+ ? process.platform
14
+ : 'linux';
15
+ /* c8 ignore stop */
16
+ export class Mini {
17
+ options;
18
+ globLists;
19
+ patterns;
20
+ dot;
21
+ nocase;
22
+ noext;
23
+ noglobstar;
24
+ nobrace;
25
+ windowsPathsNoEscape;
26
+ platform;
27
+ isWindows;
28
+ constructor(patterns, opts = {}) {
29
+ const { dot = false, nocase, noext = false, noglobstar = false, nobrace = false, windowsPathsNoEscape = false, platform = defaultPlatform, } = opts;
30
+ this.dot = dot;
31
+ this.noext = noext;
32
+ this.noglobstar = noglobstar;
33
+ this.nobrace = nobrace;
34
+ this.windowsPathsNoEscape = windowsPathsNoEscape;
35
+ this.platform = platform;
36
+ this.nocase =
37
+ nocase !== undefined
38
+ ? nocase
39
+ : this.platform === 'win32' || this.platform === 'darwin';
40
+ this.isWindows = this.platform === 'win32';
41
+ opts = {
42
+ dot: this.dot,
43
+ nocase: this.nocase,
44
+ noext: this.noext,
45
+ noglobstar: this.noglobstar,
46
+ nobrace: this.nobrace,
47
+ windowsPathsNoEscape: this.windowsPathsNoEscape,
48
+ platform: this.platform,
49
+ };
50
+ this.options = opts;
51
+ if (!patterns)
52
+ throw new TypeError('pattern required');
53
+ if (!Array.isArray(patterns))
54
+ patterns = [patterns];
55
+ if (opts.windowsPathsNoEscape) {
56
+ patterns = patterns.map(p => p.replace(/\\/g, '/'));
57
+ }
58
+ this.globLists = this.preprocess(patterns
59
+ .map(p => braceExpand(p, this.options))
60
+ .reduce((ps, p) => ps.concat(p), [])
61
+ .map(p => this.splitGlobString(p))
62
+ .filter(gl => !!gl.length));
63
+ const parsed = new Map();
64
+ this.patterns = this.globLists.map(gl => new Pattern(gl, 0, parsed, opts));
65
+ }
66
+ splitGlobString(globString) {
67
+ const parts = globString.split('/');
68
+ // canonincalize UNC paths and drives, make the first
69
+ // pattern the whole root ending in / for absolute patterns.
70
+ if (this.isUNC(parts)) {
71
+ const [p0, p1, p2] = parts;
72
+ parts.shift();
73
+ parts.shift();
74
+ parts.shift();
75
+ parts.unshift([p0, p1, p2, ''].join('/').toUpperCase());
76
+ }
77
+ else if (this.isDrive(parts)) {
78
+ const drive = parts[0].toUpperCase() + '/';
79
+ parts.shift();
80
+ parts.unshift(drive);
81
+ }
82
+ else if (parts[0] === '') {
83
+ parts[0] = '/';
84
+ }
85
+ // now strip any empty parts
86
+ return parts.filter(p => !!p);
87
+ }
88
+ isDrive(pl) {
89
+ return (this.isWindows && typeof pl[0] === 'string' && /^[a-z]:$/i.test(pl[0]));
90
+ }
91
+ isUNC(pl) {
92
+ return (this.isWindows &&
93
+ pl[0] === '' &&
94
+ pl[1] === '' &&
95
+ typeof pl[2] === 'string' &&
96
+ !!pl[2] &&
97
+ typeof pl[3] === 'string' &&
98
+ !!pl[3]);
99
+ }
100
+ preprocess(globParts) {
101
+ // if we're not in globstar mode, then turn all ** into *
102
+ if (this.noglobstar) {
103
+ for (let i = 0; i < globParts.length; i++) {
104
+ for (let j = 0; j < globParts[i].length; j++) {
105
+ if (globParts[i][j] === '**') {
106
+ globParts[i][j] = '*';
107
+ }
108
+ }
109
+ }
110
+ }
111
+ globParts = this.firstPhasePreProcess(globParts);
112
+ globParts = this.secondPhasePreProcess(globParts);
113
+ return globParts;
114
+ }
115
+ // First phase: single-pattern processing
116
+ // <pre> is 1 or more portions
117
+ // <rest> is 1 or more portions
118
+ // <p> is any portion other than ., .., '', or **
119
+ // <e> is . or ''
120
+ //
121
+ // **/.. is *brutal* for filesystem walking performance, because
122
+ // it effectively resets the recursive walk each time it occurs,
123
+ // and ** cannot be reduced out by a .. pattern part like a regexp
124
+ // or most strings (other than .., ., and '') can be.
125
+ //
126
+ // <pre>/**/../<p>/<rest> -> {<pre>/../<p>/<rest>,<pre>/**/<p>/<rest>}
127
+ // <pre>/<e>/<rest> -> <pre>/<rest>
128
+ // <pre>/<p>/../<rest> -> <pre>/<rest>
129
+ // **/**/<rest> -> **/<rest>
130
+ //
131
+ // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
132
+ // this WOULD be allowed if ** did follow symlinks, or * didn't
133
+ firstPhasePreProcess(globParts) {
134
+ let didSomething = false;
135
+ do {
136
+ didSomething = false;
137
+ // <pre>/**/../<p>/<rest> -> {<pre>/../<p>/<rest>,<pre>/**/<p>/<rest>}
138
+ for (let parts of globParts) {
139
+ let gs = -1;
140
+ while (-1 !== (gs = parts.indexOf('**', gs + 1))) {
141
+ let gss = gs;
142
+ while (parts[gss + 1] === '**') {
143
+ // <pre>/**/**/<rest> -> <pre>/**/<rest>
144
+ gss++;
145
+ }
146
+ // eg, if gs is 2 and gss is 4, that means we have 3 **
147
+ // parts, and can remove 2 of them.
148
+ if (gss > gs) {
149
+ parts.splice(gs + 1, gss - gs);
150
+ }
151
+ let next = parts[gs + 1];
152
+ const p = parts[gs + 2];
153
+ if (next !== '..')
154
+ continue;
155
+ if (!p || p === '.' || p === '..')
156
+ continue;
157
+ didSomething = true;
158
+ // edit parts in place, and push the new one
159
+ parts.splice(gs, 1);
160
+ const other = parts.slice(0);
161
+ other[gs] = '**';
162
+ globParts.push(other);
163
+ gs--;
164
+ }
165
+ // <pre>/<e>/<rest> -> <pre>/<rest>
166
+ for (let i = 1; i < parts.length - 1; i++) {
167
+ const p = parts[i];
168
+ // don't squeeze out UNC patterns
169
+ if (i === 1 && p === '' && parts[0] === '')
170
+ continue;
171
+ if (p === '.' || p === '') {
172
+ didSomething = true;
173
+ parts.splice(i, 1);
174
+ i--;
175
+ }
176
+ }
177
+ if (parts[0] === '.') {
178
+ didSomething = true;
179
+ parts.shift();
180
+ }
181
+ // <pre>/<p>/../<rest> -> <pre>/<rest>
182
+ let dd = 0;
183
+ while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
184
+ const p = parts[dd - 1];
185
+ if (p && p !== '.' && p !== '..' && p !== '**') {
186
+ didSomething = true;
187
+ parts.splice(dd - 1, 2);
188
+ if (parts.length === 0)
189
+ parts.push('');
190
+ dd -= 2;
191
+ }
192
+ }
193
+ }
194
+ } while (didSomething);
195
+ return globParts;
196
+ }
197
+ // second phase: multi-pattern dedupes
198
+ // {<pre>/*/<rest>,<pre>/<p>/<rest>} -> <pre>/*/<rest>
199
+ // {<pre>/<rest>,<pre>/<rest>} -> <pre>/<rest>
200
+ // {<pre>/**/<rest>,<pre>/<rest>} -> <pre>/**/<rest>
201
+ //
202
+ // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
203
+ // ^-- not valid because ** doens't follow symlinks
204
+ secondPhasePreProcess(globParts) {
205
+ for (let i = 0; i < globParts.length - 1; i++) {
206
+ for (let j = i + 1; j < globParts.length; j++) {
207
+ const matched = this.partsMatch(globParts[i], globParts[j]);
208
+ if (!matched)
209
+ continue;
210
+ globParts[i] = matched;
211
+ globParts[j] = [];
212
+ }
213
+ }
214
+ return globParts.filter(gs => gs.length);
215
+ }
216
+ partsMatch(a, b) {
217
+ let ai = 0;
218
+ let bi = 0;
219
+ let result = [];
220
+ let which = '';
221
+ while (ai < a.length && bi < b.length) {
222
+ if (a[ai] === b[bi]) {
223
+ result.push(which === 'b' ? b[bi] : a[ai]);
224
+ ai++;
225
+ bi++;
226
+ }
227
+ else if (a[ai] === '**' && b[bi] === a[ai + 1]) {
228
+ result.push(a[ai]);
229
+ ai++;
230
+ }
231
+ else if (b[bi] === '**' && a[ai] === b[bi + 1]) {
232
+ result.push(b[bi]);
233
+ bi++;
234
+ }
235
+ else if (a[ai] === '*' &&
236
+ b[bi] &&
237
+ !b[bi].startsWith('.') &&
238
+ b[bi] !== '**') {
239
+ if (which === 'b')
240
+ return false;
241
+ which = 'a';
242
+ result.push(a[ai]);
243
+ ai++;
244
+ bi++;
245
+ }
246
+ else if (b[bi] === '*' &&
247
+ a[ai] &&
248
+ (this.dot || !a[ai].startsWith('.')) &&
249
+ a[ai] !== '**') {
250
+ if (which === 'a')
251
+ return false;
252
+ which = 'b';
253
+ result.push(b[bi]);
254
+ ai++;
255
+ bi++;
256
+ }
257
+ else {
258
+ return false;
259
+ }
260
+ }
261
+ // if we fall out of the loop, it means they two are identical
262
+ // as long as their lengths match
263
+ return a.length === b.length && result;
264
+ }
265
+ }
266
+ export const braceExpand = (pattern, options = {}) => {
267
+ assertValidPattern(pattern);
268
+ // Thanks to Yeting Li <https://github.com/yetingli> for
269
+ // improving this regexp to avoid a ReDOS vulnerability.
270
+ if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
271
+ // shortcut. no need to expand.
272
+ return [pattern];
273
+ }
274
+ return expand(pattern);
275
+ };
276
+ const MAX_PATTERN_LENGTH = 1024 * 64;
277
+ const assertValidPattern = (pattern) => {
278
+ if (typeof pattern !== 'string') {
279
+ throw new TypeError('invalid pattern');
280
+ }
281
+ if (pattern.length > MAX_PATTERN_LENGTH) {
282
+ throw new TypeError('pattern is too long');
283
+ }
284
+ };
285
+ const isGlobList = (gl) => gl.length >= 1;
286
+ export class Pattern {
287
+ #options;
288
+ #globList;
289
+ length;
290
+ #index;
291
+ #parsed;
292
+ // memoizing
293
+ #pattern;
294
+ #rest;
295
+ constructor(globList, index, parsed, options) {
296
+ if (!isGlobList(globList)) {
297
+ throw new TypeError('empty glob list');
298
+ }
299
+ this.#globList = globList;
300
+ if (index >= globList.length) {
301
+ throw new TypeError('index out of range');
302
+ }
303
+ this.length = globList.length;
304
+ this.#options = options;
305
+ this.#index = index;
306
+ this.#parsed = parsed;
307
+ }
308
+ glob() {
309
+ return this.#globList[this.#index];
310
+ }
311
+ pattern() {
312
+ if (this.#pattern !== undefined) {
313
+ return this.#pattern;
314
+ }
315
+ const glob = this.glob();
316
+ if (glob.endsWith('/')) {
317
+ return (this.#pattern = glob);
318
+ }
319
+ const cached = this.#parsed.get(glob);
320
+ if (cached !== undefined) {
321
+ return (this.#pattern = cached);
322
+ }
323
+ const pattern = parse(glob, this.#options);
324
+ this.#parsed.set(glob, pattern);
325
+ return (this.#pattern = pattern);
326
+ }
327
+ isAbsolute() {
328
+ return this.#globList[0].endsWith('/');
329
+ }
330
+ root() {
331
+ return this.#index === 0 && this.isAbsolute() ? this.#globList[0] : '';
332
+ }
333
+ rest() {
334
+ if (this.#rest !== undefined)
335
+ return this.#rest;
336
+ if (this.#index >= this.length - 1)
337
+ return (this.#rest = null);
338
+ const rest = new Pattern(this.#globList, this.#index + 1, this.#parsed, this.#options);
339
+ this.#rest = rest;
340
+ return rest;
341
+ }
342
+ hasMore() {
343
+ return this.#index < this.length - 1;
344
+ }
345
+ }
346
+ export const parsePattern = (pattern, options) => {
347
+ const ast = tokenize(pattern, options);
348
+ if (ast.length === 1 && ast[0][TokenField.TYPE] === TokenType.STRING) {
349
+ return ast[0][TokenField.VALUE];
350
+ }
351
+ const re = compile(ast, options, true);
352
+ try {
353
+ return new RegExp(re, options.nocase ? 'i' : '');
354
+ }
355
+ catch (er) {
356
+ return /$./;
357
+ }
358
+ };
359
+ export const compile = (ast, options, isTop = false, isStart = true) => {
360
+ const negativeExts = [];
361
+ const re = [];
362
+ let stillStart = isStart;
363
+ let maybeEmpty = true;
364
+ for (let i = 0; i < ast.length; i++) {
365
+ const token = ast[i];
366
+ if (isStringToken(token)) {
367
+ if (token[TokenField.VALUE] !== '') {
368
+ maybeEmpty = false;
369
+ }
370
+ re.push(compileNonMagic(token, options));
371
+ }
372
+ else if (isClassToken(token)) {
373
+ maybeEmpty = false;
374
+ re.push(compileClass(token, options));
375
+ }
376
+ else if (isExtToken(token)) {
377
+ if (token[TokenField.VALUE] === '!') {
378
+ negativeExts.push(i);
379
+ }
380
+ re.push(compileExt(token, options, stillStart));
381
+ }
382
+ else if (isQmarkToken(token)) {
383
+ maybeEmpty = false;
384
+ re.push(compileQmark(token, options));
385
+ }
386
+ else if (isStarToken(token)) {
387
+ re.push(compileStar(token, options));
388
+ /* c8 ignore start */
389
+ }
390
+ else {
391
+ throw new TypeError('unknown token type: ' + token);
392
+ }
393
+ /* c8 ignore stop */
394
+ stillStart = false;
395
+ }
396
+ if (isTop) {
397
+ re.push('$');
398
+ }
399
+ // a negative extglob is:
400
+ // ((?!(sub|patterns)<rest of the pattern>).*?)
401
+ // so we need to do it in two passes.
402
+ for (let i = negativeExts.length - 1; i >= 0; i--) {
403
+ const n = negativeExts[i];
404
+ re[n] += compileNegativeExtClose(re, n);
405
+ }
406
+ if (isTop) {
407
+ if (!options.dot && needDotProtection(ast)) {
408
+ re.unshift('(?!^\\.)');
409
+ }
410
+ if (maybeEmpty) {
411
+ re.unshift('(?=.)');
412
+ }
413
+ re.unshift('^');
414
+ }
415
+ else if (isStart) {
416
+ if (!options.dot && needDotProtection(ast)) {
417
+ re.unshift('(?!^\\.)');
418
+ }
419
+ }
420
+ return re.join('');
421
+ };
422
+ const needDotProtection = (ast) => {
423
+ const first = ast[0];
424
+ return isClassToken(first) || isStarToken(first) || isQmarkToken(first);
425
+ };
426
+ const compileQmark = (_, __) => '.';
427
+ const compileStar = (_, __) => '.*?';
428
+ const compileNonMagic = (token, _) => regExpEscape(token[TokenField.VALUE]);
429
+ const compileClass = (token, _) => {
430
+ // TODO: posix classes
431
+ const cls = braceEscape(charUnescape(token[TokenField.VALUE]));
432
+ const re = `[${cls}]`;
433
+ // handle out of order classes, like `[z-a]`, which throw
434
+ // in javascript, but just match nothing in glob syntax.
435
+ try {
436
+ RegExp(re);
437
+ return re;
438
+ }
439
+ catch (_) {
440
+ return '$.';
441
+ }
442
+ };
443
+ const charUnescape = (s) => s.replace(/\\([^-\]])/g, '$1');
444
+ const braceEscape = (s) => s.replace(/[[\]\\]/g, '\\$&');
445
+ const globSpecialChars = new Set(['?', '*', '+', '@', '!', '[', '(']);
446
+ const escapeInClass = new Set(['-', ']']);
447
+ const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
448
+ const isExtToken = (t) => t[TokenField.TYPE] === TokenType.EXT;
449
+ const isStringToken = (t) => t[TokenField.TYPE] === TokenType.STRING;
450
+ const isStarToken = (t) => t[TokenField.TYPE] === TokenType.STAR;
451
+ const isQmarkToken = (t) => t[TokenField.TYPE] === TokenType.QMARK;
452
+ const isClassToken = (t) => t[TokenField.TYPE] === TokenType.CLASS;
453
+ var TokenField;
454
+ (function (TokenField) {
455
+ TokenField[TokenField["TYPE"] = 0] = "TYPE";
456
+ TokenField[TokenField["VALUE"] = 1] = "VALUE";
457
+ TokenField[TokenField["CHILDREN"] = 2] = "CHILDREN";
458
+ })(TokenField || (TokenField = {}));
459
+ var TokenType;
460
+ (function (TokenType) {
461
+ TokenType["STRING"] = "string";
462
+ TokenType["STAR"] = "star";
463
+ TokenType["EXT"] = "ext";
464
+ TokenType["QMARK"] = "qmark";
465
+ TokenType["CLASS"] = "class";
466
+ })(TokenType || (TokenType = {}));
467
+ const extGlobTypes = new Set(['?', '*', '+', '@', '!']);
468
+ const isExtGlobType = (s) => extGlobTypes.has(s);
469
+ const extTypes = {
470
+ '!': { open: '(?:(?!(?:', close: ').*?))' },
471
+ '?': { open: '(?:', close: ')?' },
472
+ '+': { open: '(?:', close: ')+' },
473
+ '*': { open: '(?:', close: ')*' },
474
+ '@': { open: '(?:', close: ')' },
475
+ };
476
+ const compileExt = (token, options, isStart) => {
477
+ const t = token[TokenField.VALUE];
478
+ const open = extTypes[t].open;
479
+ const close = t === '!' ? '' : extTypes[t].close;
480
+ const subs = token[TokenField.CHILDREN];
481
+ const body = subs.map(ast => compile(ast, options, false, isStart)).join('|');
482
+ return open + body + close;
483
+ };
484
+ const compileNegativeExtClose = (re, n) => {
485
+ // walk the AST from i onwards, collecting the regexp
486
+ // then add the end bit:
487
+ // ((?!(sub|patterns)<rest of the pattern>).*?)
488
+ // ^-- from here on
489
+ let s = ')';
490
+ for (let i = n + 1; i < re.length; i++) {
491
+ s += re[i];
492
+ }
493
+ s += ').*?)';
494
+ return s;
495
+ };
496
+ export const tokenize = (pattern, options, ast = []) => {
497
+ // tokenize the string up into chunks first
498
+ // sort of like an AST of the pattern
499
+ // each node is [type, value, [...children]]
500
+ // root, or children of extglobs, are an array of nodes
501
+ // so 'i\?jk?*.@(xy[a-c]|!(foo|ba*r))baz*bo' becomes:
502
+ // [
503
+ // [STRING, 'i?jk'],
504
+ // [QMARK, '?'],
505
+ // [STAR, '*'],
506
+ // [STRING, '.'],
507
+ // [EXT, '@', [
508
+ // [[STRING, 'xy'], [CLASS, 'a-c']],
509
+ // [[EXT, '!', [
510
+ // [[STRING, 'foo']],
511
+ // [[STRING, 'ba'], [STAR, '*'], [STRING, 'r']]
512
+ // ]]],
513
+ // ]],
514
+ // [STRING, 'baz'],
515
+ // [STAR, '*'],
516
+ // [STRING, 'bo'],
517
+ // ]
518
+ //
519
+ // which turns into the regexp:
520
+ // ^i\?jk..*?\.(?:xy[a-c]|(?:(?!(?:foo|ba.*?r).*$)))baz.*?bo$
521
+ // Place the "no dot allowed" if the AST starts at position 0,
522
+ // and is a *, ?, or class at the start
523
+ let i = 0;
524
+ const length = pattern.length;
525
+ while (i < length) {
526
+ let c = pattern.charAt(i);
527
+ // take our best guess as to what it is
528
+ // the other tokenizers will append to the AST and return
529
+ // the amount of string that was consumed.
530
+ if (!options.noext && isExtGlobType(c) && pattern.charAt(i + 1) === '(') {
531
+ const consumed = tokenizeExt(pattern, options, i, ast);
532
+ if (consumed) {
533
+ i += consumed;
534
+ c = pattern.charAt(i);
535
+ continue;
536
+ }
537
+ }
538
+ if (c === '[') {
539
+ const consumed = tokenizeClass(pattern, options, i, ast);
540
+ if (consumed) {
541
+ i += consumed;
542
+ continue;
543
+ }
544
+ }
545
+ if (c === '*') {
546
+ ast.push([TokenType.STAR, '*']);
547
+ }
548
+ else if (c === '?') {
549
+ ast.push([TokenType.QMARK, '?']);
550
+ }
551
+ else {
552
+ const consumed = tokenizeNonMagic(pattern, options, i, ast);
553
+ if (consumed) {
554
+ i += consumed;
555
+ c = pattern.charAt(i);
556
+ continue;
557
+ }
558
+ }
559
+ i++;
560
+ }
561
+ return ast;
562
+ };
563
+ const tokenizeExt = (pattern, options, i, ast) => {
564
+ const extType = pattern.charAt(i);
565
+ if (!isExtGlobType(extType)) {
566
+ throw new Error('invalid extglob type: ' + extType);
567
+ }
568
+ const matchStack = [];
569
+ const pipes = [];
570
+ let p;
571
+ const length = pattern.length;
572
+ let end = -1;
573
+ let escaping = false;
574
+ // first split out the top-level set of strings
575
+ // if we can't do that, it's not a valid extglob
576
+ for (p = i + 2; p < length; p++) {
577
+ const c = pattern.charAt(p);
578
+ if (escaping) {
579
+ escaping = false;
580
+ continue;
581
+ }
582
+ if (c === '\\') {
583
+ escaping = true;
584
+ continue;
585
+ }
586
+ if (c === ']') {
587
+ if (matchStack[0] === '[' && pattern.charAt(p - 1) !== '[') {
588
+ matchStack.shift();
589
+ }
590
+ }
591
+ else if (c === ')') {
592
+ if (!matchStack.length) {
593
+ // finished!
594
+ end = p;
595
+ break;
596
+ }
597
+ else if (matchStack[0] === '(') {
598
+ matchStack.shift();
599
+ }
600
+ }
601
+ else if (c === '(') {
602
+ if (matchStack[0] !== '[' && isExtGlobType(pattern.charAt(p - 1))) {
603
+ matchStack.unshift(c);
604
+ }
605
+ }
606
+ else if (c === '|' && matchStack.length === 0) {
607
+ pipes.push(p);
608
+ }
609
+ }
610
+ if (!end || matchStack.length) {
611
+ return 0;
612
+ }
613
+ // i + 1, pipes, and end define the outside boundaries of the subs
614
+ const subPatterns = [];
615
+ let start = i + 2;
616
+ for (const pipe of pipes) {
617
+ subPatterns.push(pattern.substring(start, pipe));
618
+ start = pipe + 1;
619
+ }
620
+ subPatterns.push(pattern.substring(start, end));
621
+ const subTokenized = subPatterns.map(p => tokenize(p, options));
622
+ ast.push([TokenType.EXT, extType, subTokenized]);
623
+ return end - i + 1;
624
+ };
625
+ const tokenizeClass = (pattern, _, i, ast) => {
626
+ // walk until we find the closing ] that is not escaped or the first char
627
+ // return 0 if it's not a valid class (basically, just if it's left open)
628
+ let p;
629
+ let escaping = false;
630
+ const length = pattern.length;
631
+ let s = '';
632
+ for (p = i + 1; p < length; p++) {
633
+ const c = pattern.charAt(p);
634
+ if (c === '\\' && !escaping) {
635
+ escaping = true;
636
+ continue;
637
+ }
638
+ if (p === i + 1 && c === ']') {
639
+ s += c;
640
+ continue;
641
+ }
642
+ if (escaping) {
643
+ escaping = false;
644
+ if (escapeInClass.has(c)) {
645
+ s += '\\';
646
+ }
647
+ s += c;
648
+ continue;
649
+ }
650
+ if (c === ']') {
651
+ ast.push([TokenType.CLASS, s]);
652
+ return p - i + 1;
653
+ }
654
+ s += c;
655
+ }
656
+ return 0;
657
+ };
658
+ const tokenizeNonMagic = (pattern, _, i, ast) => {
659
+ let escaping = false;
660
+ let p = i;
661
+ let sawFirst = false;
662
+ const length = pattern.length;
663
+ let s = '';
664
+ for (p = i; p < length; p++) {
665
+ let c = pattern.charAt(p);
666
+ if (c === '\\' && !escaping) {
667
+ escaping = true;
668
+ continue;
669
+ }
670
+ if (escaping) {
671
+ escaping = false;
672
+ s += c;
673
+ continue;
674
+ }
675
+ // this is only called when we KNOW the first char is not magic,
676
+ // so no need to stop for that at the outset.
677
+ if (!sawFirst) {
678
+ sawFirst = true;
679
+ s += c;
680
+ continue;
681
+ }
682
+ if (globSpecialChars.has(c)) {
683
+ break;
684
+ }
685
+ s += c;
686
+ }
687
+ ast.push([TokenType.STRING, s]);
688
+ return p - i;
689
+ };
690
+ const parse = (pattern, options) => {
691
+ if (pattern === '**')
692
+ return GLOBSTAR;
693
+ let m;
694
+ let fastTest = null;
695
+ if ((m = pattern.match(starRE))) {
696
+ fastTest = options.dot ? starTestDot : starTest;
697
+ }
698
+ else if ((m = pattern.match(starDotExtRE))) {
699
+ fastTest = (options.nocase
700
+ ? options.dot
701
+ ? starDotExtTestNocaseDot
702
+ : starDotExtTestNocase
703
+ : options.dot
704
+ ? starDotExtTestDot
705
+ : starDotExtTest)(m[1]);
706
+ }
707
+ else if ((m = pattern.match(qmarksRE))) {
708
+ fastTest = (options.nocase
709
+ ? options.dot
710
+ ? qmarksTestNocaseDot
711
+ : qmarksTestNocase
712
+ : options.dot
713
+ ? qmarksTestDot
714
+ : qmarksTest)(m);
715
+ }
716
+ else if ((m = pattern.match(starDotStarRE))) {
717
+ fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
718
+ }
719
+ else if ((m = pattern.match(dotStarRE))) {
720
+ fastTest = dotStarTest;
721
+ }
722
+ if (fastTest) {
723
+ return Object.assign(/$./, {
724
+ _glob: pattern,
725
+ test: fastTest,
726
+ });
727
+ }
728
+ else {
729
+ // ok we have to actually parse it
730
+ const re = parsePattern(pattern, options);
731
+ return typeof re === 'string' ? re : Object.assign(re, {
732
+ _glob: pattern,
733
+ });
734
+ }
735
+ };
736
+ // Optimized checking for the most common glob patterns.
737
+ const starDotExtRE = /^\*+([^+@!?\*\[\(]*)$/;
738
+ const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
739
+ const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
740
+ const starDotExtTestNocase = (ext) => {
741
+ ext = ext.toLowerCase();
742
+ return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
743
+ };
744
+ const starDotExtTestNocaseDot = (ext) => {
745
+ ext = ext.toLowerCase();
746
+ return (f) => f.toLowerCase().endsWith(ext);
747
+ };
748
+ const starDotStarRE = /^\*+\.\*+$/;
749
+ const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
750
+ const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
751
+ const dotStarRE = /^\.\*+$/;
752
+ const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
753
+ const starRE = /^\*+$/;
754
+ const starTest = (f) => f.length !== 0 && !f.startsWith('.');
755
+ const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
756
+ const qmarksRE = /^\?+([^+@!?\*\[\(]*)?$/;
757
+ const qmarksTestNocase = ([$0, ext = '']) => {
758
+ const noext = qmarksTestNoExt([$0]);
759
+ if (!ext)
760
+ return noext;
761
+ ext = ext.toLowerCase();
762
+ return (f) => noext(f) && f.toLowerCase().endsWith(ext);
763
+ };
764
+ const qmarksTestNocaseDot = ([$0, ext = '']) => {
765
+ const noext = qmarksTestNoExtDot([$0]);
766
+ if (!ext)
767
+ return noext;
768
+ ext = ext.toLowerCase();
769
+ return (f) => noext(f) && f.toLowerCase().endsWith(ext);
770
+ };
771
+ const qmarksTestDot = ([$0, ext = '']) => {
772
+ const noext = qmarksTestNoExtDot([$0]);
773
+ return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
774
+ };
775
+ const qmarksTest = ([$0, ext = '']) => {
776
+ const noext = qmarksTestNoExt([$0]);
777
+ return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
778
+ };
779
+ const qmarksTestNoExt = ([$0]) => {
780
+ const len = $0.length;
781
+ return (f) => f.length === len && !f.startsWith('.');
782
+ };
783
+ const qmarksTestNoExtDot = ([$0]) => {
784
+ const len = $0.length;
785
+ return (f) => f.length === len && f !== '.' && f !== '..';
786
+ };
787
+ //# sourceMappingURL=mini.js.map