minimatch 10.2.2 → 10.2.4

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.
package/README.md CHANGED
@@ -7,6 +7,43 @@ This is the matching library used internally by npm.
7
7
  It works by converting glob expressions into JavaScript `RegExp`
8
8
  objects.
9
9
 
10
+ ## Important Security Consideration!
11
+
12
+ > [!WARNING]
13
+ > This library uses JavaScript regular expressions. Please read
14
+ > the following warning carefully, and be thoughtful about what
15
+ > you provide to this library in production systems.
16
+
17
+ _Any_ library in JavaScript that deals with matching string
18
+ patterns using regular expressions will be subject to
19
+ [ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
20
+ if the pattern is generated using untrusted input.
21
+
22
+ Efforts have been made to mitigate risk as much as is feasible in
23
+ such a library, providing maximum recursion depths and so forth,
24
+ but these measures can only ultimately protect against accidents,
25
+ not malice. A dedicated attacker can _always_ find patterns that
26
+ cannot be defended against by a bash-compatible glob pattern
27
+ matching system that uses JavaScript regular expressions.
28
+
29
+ To be extremely clear:
30
+
31
+ > [!WARNING]
32
+ > **If you create a system where you take user input, and use
33
+ > that input as the source of a Regular Expression pattern, in
34
+ > this or any extant glob matcher in JavaScript, you will be
35
+ > pwned.**
36
+
37
+ A future version of this library _may_ use a different matching
38
+ algorithm which does not exhibit backtracking problems. If and
39
+ when that happens, it will likely be a sweeping change, and those
40
+ improvements will **not** be backported to legacy versions.
41
+
42
+ In the near term, it is not reasonable to continue to play
43
+ whack-a-mole with security advisories, and so any future ReDoS
44
+ reports will be considered "working as intended", and resolved
45
+ entirely by this warning.
46
+
10
47
  ## Usage
11
48
 
12
49
  ```js
@@ -396,6 +433,42 @@ separators in file paths for comparison.)
396
433
 
397
434
  Defaults to the value of `process.platform`.
398
435
 
436
+ ### maxGlobstarRecursion
437
+
438
+ Max number of non-adjacent `**` patterns to recursively walk
439
+ down.
440
+
441
+ The default of `200` is almost certainly high enough for most
442
+ purposes, and can handle absurdly excessive patterns.
443
+
444
+ If the limit is exceeded (which would require very excessively
445
+ long patterns and paths containing lots of `**` patterns!), then
446
+ it is treated as non-matching, even if the path would normally
447
+ match the pattern provided.
448
+
449
+ That is, this is an intentional false negative, deemed an
450
+ acceptable break in correctness for security and performance.
451
+
452
+ ### maxExtglobRecursion
453
+
454
+ Max depth to traverse for nested extglobs like `*(a|b|c)`
455
+
456
+ Default is 2, which is quite low, but any higher value swiftly
457
+ results in punishing performance impacts. Note that this is _not_
458
+ relevant when the globstar types can be safely coalesced into a
459
+ single set.
460
+
461
+ For example, `*(a|@(b|c)|d)` would be flattened into
462
+ `*(a|b|c|d)`. Thus, many common extglobs will retain good
463
+ performance and never hit this limit, even if they are
464
+ excessively deep and complicated.
465
+
466
+ If the limit is hit, then the extglob characters are simply not
467
+ parsed, and the pattern effectively switches into `noextglob:
468
+ true` mode for the contents of that nested sub-pattern. This will
469
+ typically _not_ result in a match, but is considered a valid
470
+ trade-off for security and performance.
471
+
399
472
  ## Comparisons to other fnmatch/glob implementations
400
473
 
401
474
  While strict compliance with the existing standards is a
@@ -1,2 +1,2 @@
1
- export declare const assertValidPattern: (pattern: any) => void;
1
+ export declare const assertValidPattern: (pattern: unknown) => void;
2
2
  //# sourceMappingURL=assert-valid-pattern.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assert-valid-pattern.d.ts","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,kBAAkB,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAUlD,CAAA"}
1
+ {"version":3,"file":"assert-valid-pattern.d.ts","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAUtD,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"assert-valid-pattern.js","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":";;;AAAA,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAA;AAC7B,MAAM,kBAAkB,GAA2B,CACxD,OAAY,EACe,EAAE;IAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QACxC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC,CAAA;AAVY,QAAA,kBAAkB,sBAU9B","sourcesContent":["const MAX_PATTERN_LENGTH = 1024 * 64\nexport const assertValidPattern: (pattern: any) => void = (\n pattern: any,\n): asserts pattern is string => {\n if (typeof pattern !== 'string') {\n throw new TypeError('invalid pattern')\n }\n\n if (pattern.length > MAX_PATTERN_LENGTH) {\n throw new TypeError('pattern is too long')\n }\n}\n"]}
1
+ {"version":3,"file":"assert-valid-pattern.js","sourceRoot":"","sources":["../../src/assert-valid-pattern.ts"],"names":[],"mappings":";;;AAAA,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAA;AAC7B,MAAM,kBAAkB,GAA+B,CAC5D,OAAgB,EACW,EAAE;IAC7B,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAA;IACxC,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QACxC,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAA;IAC5C,CAAC;AACH,CAAC,CAAA;AAVY,QAAA,kBAAkB,sBAU9B","sourcesContent":["const MAX_PATTERN_LENGTH = 1024 * 64\nexport const assertValidPattern: (pattern: unknown) => void = (\n pattern: unknown,\n): asserts pattern is string => {\n if (typeof pattern !== 'string') {\n throw new TypeError('invalid pattern')\n }\n\n if (pattern.length > MAX_PATTERN_LENGTH) {\n throw new TypeError('pattern is too long')\n }\n}\n"]}
@@ -3,6 +3,8 @@ export type ExtglobType = '!' | '?' | '+' | '*' | '@';
3
3
  export declare class AST {
4
4
  #private;
5
5
  type: ExtglobType | null;
6
+ id: number;
7
+ get depth(): number;
6
8
  constructor(type: ExtglobType | null, parent?: AST, options?: MinimatchOptions);
7
9
  get hasMagic(): boolean | undefined;
8
10
  toString(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/ast.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAwCvD,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAkCrD,qBAAa,GAAG;;IACd,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;gBAiBtB,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,CAAC,EAAE,GAAG,EACZ,OAAO,GAAE,gBAAqB;IAahC,IAAI,QAAQ,IAAI,OAAO,GAAG,SAAS,CAUlC;IAGD,QAAQ,IAAI,MAAM;IA+ClB,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE;IAe/B,MAAM;IAkBN,OAAO,IAAI,OAAO;IAgBlB,KAAK,IAAI,OAAO;IAYhB,MAAM,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM;IAKzB,KAAK,CAAC,MAAM,EAAE,GAAG;IAsIjB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAQ/D,WAAW,IAAI,QAAQ,GAAG,MAAM;IA2BhC,IAAI,OAAO,qBAEV;IAuED,cAAc,CACZ,QAAQ,CAAC,EAAE,OAAO,GACjB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;CAwMjE"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/ast.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAwCvD,MAAM,MAAM,WAAW,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AA6IrD,qBAAa,GAAG;;IACd,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;IAexB,EAAE,SAAO;IAET,IAAI,KAAK,IAAI,MAAM,CAElB;gBAgBC,IAAI,EAAE,WAAW,GAAG,IAAI,EACxB,MAAM,CAAC,EAAE,GAAG,EACZ,OAAO,GAAE,gBAAqB;IAahC,IAAI,QAAQ,IAAI,OAAO,GAAG,SAAS,CAUlC;IAGD,QAAQ,IAAI,MAAM;IA+ClB,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE;IAe/B,MAAM;IAkBN,OAAO,IAAI,OAAO;IAgBlB,KAAK,IAAI,OAAO;IAYhB,MAAM,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM;IAKzB,KAAK,CAAC,MAAM,EAAE,GAAG;IAwQjB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAQ/D,WAAW,IAAI,QAAQ,GAAG,MAAM;IA2BhC,IAAI,OAAO,qBAEV;IAuED,cAAc,CACZ,QAAQ,CAAC,EAAE,OAAO,GACjB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC;CA6OjE"}
@@ -1,11 +1,113 @@
1
1
  "use strict";
2
2
  // parse a single path portion
3
+ var _a;
3
4
  Object.defineProperty(exports, "__esModule", { value: true });
4
5
  exports.AST = void 0;
5
6
  const brace_expressions_js_1 = require("./brace-expressions.js");
6
7
  const unescape_js_1 = require("./unescape.js");
7
8
  const types = new Set(['!', '?', '+', '*', '@']);
8
9
  const isExtglobType = (c) => types.has(c);
10
+ const isExtglobAST = (c) => isExtglobType(c.type);
11
+ // Map of which extglob types can adopt the children of a nested extglob
12
+ //
13
+ // anything but ! can adopt a matching type:
14
+ // +(a|+(b|c)|d) => +(a|b|c|d)
15
+ // *(a|*(b|c)|d) => *(a|b|c|d)
16
+ // @(a|@(b|c)|d) => @(a|b|c|d)
17
+ // ?(a|?(b|c)|d) => ?(a|b|c|d)
18
+ //
19
+ // * can adopt anything, because 0 or repetition is allowed
20
+ // *(a|?(b|c)|d) => *(a|b|c|d)
21
+ // *(a|+(b|c)|d) => *(a|b|c|d)
22
+ // *(a|@(b|c)|d) => *(a|b|c|d)
23
+ //
24
+ // + can adopt @, because 1 or repetition is allowed
25
+ // +(a|@(b|c)|d) => +(a|b|c|d)
26
+ //
27
+ // + and @ CANNOT adopt *, because 0 would be allowed
28
+ // +(a|*(b|c)|d) => would match "", on *(b|c)
29
+ // @(a|*(b|c)|d) => would match "", on *(b|c)
30
+ //
31
+ // + and @ CANNOT adopt ?, because 0 would be allowed
32
+ // +(a|?(b|c)|d) => would match "", on ?(b|c)
33
+ // @(a|?(b|c)|d) => would match "", on ?(b|c)
34
+ //
35
+ // ? can adopt @, because 0 or 1 is allowed
36
+ // ?(a|@(b|c)|d) => ?(a|b|c|d)
37
+ //
38
+ // ? and @ CANNOT adopt * or +, because >1 would be allowed
39
+ // ?(a|*(b|c)|d) => would match bbb on *(b|c)
40
+ // @(a|*(b|c)|d) => would match bbb on *(b|c)
41
+ // ?(a|+(b|c)|d) => would match bbb on +(b|c)
42
+ // @(a|+(b|c)|d) => would match bbb on +(b|c)
43
+ //
44
+ // ! CANNOT adopt ! (nothing else can either)
45
+ // !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
46
+ //
47
+ // ! can adopt @
48
+ // !(a|@(b|c)|d) => !(a|b|c|d)
49
+ //
50
+ // ! CANNOT adopt *
51
+ // !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
52
+ //
53
+ // ! CANNOT adopt +
54
+ // !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
55
+ //
56
+ // ! CANNOT adopt ?
57
+ // x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
58
+ const adoptionMap = new Map([
59
+ ['!', ['@']],
60
+ ['?', ['?', '@']],
61
+ ['@', ['@']],
62
+ ['*', ['*', '+', '?', '@']],
63
+ ['+', ['+', '@']],
64
+ ]);
65
+ // nested extglobs that can be adopted in, but with the addition of
66
+ // a blank '' element.
67
+ const adoptionWithSpaceMap = new Map([
68
+ ['!', ['?']],
69
+ ['@', ['?']],
70
+ ['+', ['?', '*']],
71
+ ]);
72
+ // union of the previous two maps
73
+ const adoptionAnyMap = new Map([
74
+ ['!', ['?', '@']],
75
+ ['?', ['?', '@']],
76
+ ['@', ['?', '@']],
77
+ ['*', ['*', '+', '?', '@']],
78
+ ['+', ['+', '@', '?', '*']],
79
+ ]);
80
+ // Extglobs that can take over their parent if they are the only child
81
+ // the key is parent, value maps child to resulting extglob parent type
82
+ // '@' is omitted because it's a special case. An `@` extglob with a single
83
+ // member can always be usurped by that subpattern.
84
+ const usurpMap = new Map([
85
+ ['!', new Map([['!', '@']])],
86
+ [
87
+ '?',
88
+ new Map([
89
+ ['*', '*'],
90
+ ['+', '*'],
91
+ ]),
92
+ ],
93
+ [
94
+ '@',
95
+ new Map([
96
+ ['!', '!'],
97
+ ['?', '?'],
98
+ ['@', '@'],
99
+ ['*', '*'],
100
+ ['+', '+'],
101
+ ]),
102
+ ],
103
+ [
104
+ '+',
105
+ new Map([
106
+ ['?', '*'],
107
+ ['*', '*'],
108
+ ]),
109
+ ],
110
+ ]);
9
111
  // Patterns that get prepended to bind to the start of either the
10
112
  // entire string, or just a single path portion, to prevent dots
11
113
  // and/or traversal patterns, when needed.
@@ -29,6 +131,7 @@ const star = qmark + '*?';
29
131
  const starNoEmpty = qmark + '+?';
30
132
  // remove the \ chars that we added if we end up doing a nonmagic compare
31
133
  // const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
134
+ let ID = 0;
32
135
  class AST {
33
136
  type;
34
137
  #root;
@@ -44,6 +147,22 @@ class AST {
44
147
  // set to true if it's an extglob with no children
45
148
  // (which really means one child of '')
46
149
  #emptyExt = false;
150
+ id = ++ID;
151
+ get depth() {
152
+ return (this.#parent?.depth ?? -1) + 1;
153
+ }
154
+ [Symbol.for('nodejs.util.inspect.custom')]() {
155
+ return {
156
+ '@@type': 'AST',
157
+ id: this.id,
158
+ type: this.type,
159
+ root: this.#root.id,
160
+ parent: this.#parent?.id,
161
+ depth: this.depth,
162
+ partsLength: this.#parts.length,
163
+ parts: this.#parts,
164
+ };
165
+ }
47
166
  constructor(type, parent, options = {}) {
48
167
  this.type = type;
49
168
  // extglobs are inherently magical
@@ -123,7 +242,7 @@ class AST {
123
242
  continue;
124
243
  /* c8 ignore start */
125
244
  if (typeof p !== 'string' &&
126
- !(p instanceof AST && p.#parent === this)) {
245
+ !(p instanceof _a && p.#parent === this)) {
127
246
  throw new Error('invalid part: ' + p);
128
247
  }
129
248
  /* c8 ignore stop */
@@ -157,7 +276,7 @@ class AST {
157
276
  const p = this.#parent;
158
277
  for (let i = 0; i < this.#parentIndex; i++) {
159
278
  const pp = p.#parts[i];
160
- if (!(pp instanceof AST && pp.type === '!')) {
279
+ if (!(pp instanceof _a && pp.type === '!')) {
161
280
  return false;
162
281
  }
163
282
  }
@@ -185,13 +304,14 @@ class AST {
185
304
  this.push(part.clone(this));
186
305
  }
187
306
  clone(parent) {
188
- const c = new AST(this.type, parent);
307
+ const c = new _a(this.type, parent);
189
308
  for (const p of this.#parts) {
190
309
  c.copyIn(p);
191
310
  }
192
311
  return c;
193
312
  }
194
- static #parseAST(str, ast, pos, opt) {
313
+ static #parseAST(str, ast, pos, opt, extDepth) {
314
+ const maxDepth = opt.maxExtglobRecursion ?? 2;
195
315
  let escaping = false;
196
316
  let inBrace = false;
197
317
  let braceStart = -1;
@@ -228,11 +348,17 @@ class AST {
228
348
  acc += c;
229
349
  continue;
230
350
  }
231
- if (!opt.noext && isExtglobType(c) && str.charAt(i) === '(') {
351
+ // we don't have to check for adoption here, because that's
352
+ // done at the other recursion point.
353
+ const doRecurse = !opt.noext &&
354
+ isExtglobType(c) &&
355
+ str.charAt(i) === '(' &&
356
+ extDepth <= maxDepth;
357
+ if (doRecurse) {
232
358
  ast.push(acc);
233
359
  acc = '';
234
- const ext = new AST(c, ast);
235
- i = AST.#parseAST(str, ext, i, opt);
360
+ const ext = new _a(c, ast);
361
+ i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
236
362
  ast.push(ext);
237
363
  continue;
238
364
  }
@@ -244,7 +370,7 @@ class AST {
244
370
  // some kind of extglob, pos is at the (
245
371
  // find the next | or )
246
372
  let i = pos + 1;
247
- let part = new AST(null, ast);
373
+ let part = new _a(null, ast);
248
374
  const parts = [];
249
375
  let acc = '';
250
376
  while (i < str.length) {
@@ -275,19 +401,26 @@ class AST {
275
401
  acc += c;
276
402
  continue;
277
403
  }
278
- if (isExtglobType(c) && str.charAt(i) === '(') {
404
+ const doRecurse = !opt.noext &&
405
+ isExtglobType(c) &&
406
+ str.charAt(i) === '(' &&
407
+ /* c8 ignore start - the maxDepth is sufficient here */
408
+ (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
409
+ /* c8 ignore stop */
410
+ if (doRecurse) {
411
+ const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
279
412
  part.push(acc);
280
413
  acc = '';
281
- const ext = new AST(c, part);
414
+ const ext = new _a(c, part);
282
415
  part.push(ext);
283
- i = AST.#parseAST(str, ext, i, opt);
416
+ i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
284
417
  continue;
285
418
  }
286
419
  if (c === '|') {
287
420
  part.push(acc);
288
421
  acc = '';
289
422
  parts.push(part);
290
- part = new AST(null, ast);
423
+ part = new _a(null, ast);
291
424
  continue;
292
425
  }
293
426
  if (c === ')') {
@@ -309,9 +442,82 @@ class AST {
309
442
  ast.#parts = [str.substring(pos - 1)];
310
443
  return i;
311
444
  }
445
+ #canAdoptWithSpace(child) {
446
+ return this.#canAdopt(child, adoptionWithSpaceMap);
447
+ }
448
+ #canAdopt(child, map = adoptionMap) {
449
+ if (!child ||
450
+ typeof child !== 'object' ||
451
+ child.type !== null ||
452
+ child.#parts.length !== 1 ||
453
+ this.type === null) {
454
+ return false;
455
+ }
456
+ const gc = child.#parts[0];
457
+ if (!gc || typeof gc !== 'object' || gc.type === null) {
458
+ return false;
459
+ }
460
+ return this.#canAdoptType(gc.type, map);
461
+ }
462
+ #canAdoptType(c, map = adoptionAnyMap) {
463
+ return !!map.get(this.type)?.includes(c);
464
+ }
465
+ #adoptWithSpace(child, index) {
466
+ const gc = child.#parts[0];
467
+ const blank = new _a(null, gc, this.options);
468
+ blank.#parts.push('');
469
+ gc.push(blank);
470
+ this.#adopt(child, index);
471
+ }
472
+ #adopt(child, index) {
473
+ const gc = child.#parts[0];
474
+ this.#parts.splice(index, 1, ...gc.#parts);
475
+ for (const p of gc.#parts) {
476
+ if (typeof p === 'object')
477
+ p.#parent = this;
478
+ }
479
+ this.#toString = undefined;
480
+ }
481
+ #canUsurpType(c) {
482
+ const m = usurpMap.get(this.type);
483
+ return !!(m?.has(c));
484
+ }
485
+ #canUsurp(child) {
486
+ if (!child ||
487
+ typeof child !== 'object' ||
488
+ child.type !== null ||
489
+ child.#parts.length !== 1 ||
490
+ this.type === null ||
491
+ this.#parts.length !== 1) {
492
+ return false;
493
+ }
494
+ const gc = child.#parts[0];
495
+ if (!gc || typeof gc !== 'object' || gc.type === null) {
496
+ return false;
497
+ }
498
+ return this.#canUsurpType(gc.type);
499
+ }
500
+ #usurp(child) {
501
+ const m = usurpMap.get(this.type);
502
+ const gc = child.#parts[0];
503
+ const nt = m?.get(gc.type);
504
+ /* c8 ignore start - impossible */
505
+ if (!nt)
506
+ return false;
507
+ /* c8 ignore stop */
508
+ this.#parts = gc.#parts;
509
+ for (const p of this.#parts) {
510
+ if (typeof p === 'object') {
511
+ p.#parent = this;
512
+ }
513
+ }
514
+ this.type = nt;
515
+ this.#toString = undefined;
516
+ this.#emptyExt = false;
517
+ }
312
518
  static fromGlob(pattern, options = {}) {
313
- const ast = new AST(null, undefined, options);
314
- AST.#parseAST(pattern, ast, 0, options);
519
+ const ast = new _a(null, undefined, options);
520
+ _a.#parseAST(pattern, ast, 0, options, 0);
315
521
  return ast;
316
522
  }
317
523
  // returns the regular expression if there's magic, or the unescaped
@@ -415,16 +621,18 @@ class AST {
415
621
  // or start or whatever) and prepend ^ or / at the Regexp construction.
416
622
  toRegExpSource(allowDot) {
417
623
  const dot = allowDot ?? !!this.#options.dot;
418
- if (this.#root === this)
624
+ if (this.#root === this) {
625
+ this.#flatten();
419
626
  this.#fillNegs();
420
- if (!this.type) {
627
+ }
628
+ if (!isExtglobAST(this)) {
421
629
  const noEmpty = this.isStart() &&
422
630
  this.isEnd() &&
423
631
  !this.#parts.some(s => typeof s !== 'string');
424
632
  const src = this.#parts
425
633
  .map(p => {
426
634
  const [re, _, hasMagic, uflag] = typeof p === 'string' ?
427
- AST.#parseGlob(p, this.#hasMagic, noEmpty)
635
+ _a.#parseGlob(p, this.#hasMagic, noEmpty)
428
636
  : p.toRegExpSource(allowDot);
429
637
  this.#hasMagic = this.#hasMagic || hasMagic;
430
638
  this.#uflag = this.#uflag || uflag;
@@ -486,12 +694,12 @@ class AST {
486
694
  // invalid extglob, has to at least be *something* present, if it's
487
695
  // the entire path portion.
488
696
  const s = this.toString();
489
- this.#parts = [s];
490
- this.type = null;
491
- this.#hasMagic = undefined;
697
+ const me = this;
698
+ me.#parts = [s];
699
+ me.type = null;
700
+ me.#hasMagic = undefined;
492
701
  return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];
493
702
  }
494
- // XXX abstract out this map method
495
703
  let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
496
704
  ''
497
705
  : this.#partsToRegExp(true);
@@ -527,6 +735,42 @@ class AST {
527
735
  this.#uflag,
528
736
  ];
529
737
  }
738
+ #flatten() {
739
+ if (!isExtglobAST(this)) {
740
+ for (const p of this.#parts) {
741
+ if (typeof p === 'object') {
742
+ p.#flatten();
743
+ }
744
+ }
745
+ }
746
+ else {
747
+ // do up to 10 passes to flatten as much as possible
748
+ let iterations = 0;
749
+ let done = false;
750
+ do {
751
+ done = true;
752
+ for (let i = 0; i < this.#parts.length; i++) {
753
+ const c = this.#parts[i];
754
+ if (typeof c === 'object') {
755
+ c.#flatten();
756
+ if (this.#canAdopt(c)) {
757
+ done = false;
758
+ this.#adopt(c, i);
759
+ }
760
+ else if (this.#canAdoptWithSpace(c)) {
761
+ done = false;
762
+ this.#adoptWithSpace(c, i);
763
+ }
764
+ else if (this.#canUsurp(c)) {
765
+ done = false;
766
+ this.#usurp(c);
767
+ }
768
+ }
769
+ }
770
+ } while (!done && ++iterations < 10);
771
+ }
772
+ this.#toString = undefined;
773
+ }
530
774
  #partsToRegExp(dot) {
531
775
  return this.#parts
532
776
  .map(p => {
@@ -598,4 +842,5 @@ class AST {
598
842
  }
599
843
  }
600
844
  exports.AST = AST;
845
+ _a = AST;
601
846
  //# sourceMappingURL=ast.js.map