@shopware/api-gen 1.4.0 → 1.5.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.
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import yargs from 'yargs';
2
2
  import { hideBin } from 'yargs/helpers';
3
- import { a as getAugmentedNamespace, c as commonjsGlobal, g as generate, l as loadSchema, v as validateJson } from './shared/api-gen.BWZPGkRz.mjs';
3
+ import { g as getAugmentedNamespace, c as commonjsGlobal, a as generate, l as loadSchema, v as validateJson } from './shared/api-gen.a71ec925.mjs';
4
4
  import { mkdirSync, writeFileSync } from 'node:fs';
5
5
  import { resolve as resolve$4 } from 'node:path';
6
6
  import require$$0$5 from 'fs';
@@ -18,9 +18,9 @@ import require$$0$6 from 'buffer';
18
18
  import { format as format$5 } from 'prettier';
19
19
  import 'openapi-typescript';
20
20
  import 'typescript';
21
+ import 'crypto';
21
22
  import 'ofetch';
22
23
  import 'ts-morph';
23
- import 'crypto';
24
24
  import '@shopware/api-client';
25
25
 
26
26
 
@@ -33,7 +33,7 @@ const __filename = __cjs_url__.fileURLToPath(import.meta.url);
33
33
  const __dirname = __cjs_path__.dirname(__filename);
34
34
  const require = __cjs_mod__.createRequire(import.meta.url);
35
35
  const name$1 = "@shopware/api-gen";
36
- const version$2 = "1.4.0";
36
+ const version$2 = "1.5.0";
37
37
  const description$2 = "Shopware CLI for API client generation.";
38
38
  const author = "Shopware";
39
39
  const type$b = "module";
@@ -73,7 +73,7 @@ const scripts = {
73
73
  dev: "export NODE_ENV=development && unbuild --stub",
74
74
  lint: "biome check .",
75
75
  "lint:fix": "biome check --write . && pnpm run typecheck",
76
- typecheck: "tsc --noEmit",
76
+ typecheck: "tsgo --noEmit",
77
77
  test: "vitest run --typecheck",
78
78
  "test:bench": "vitest bench",
79
79
  "test:watch": "vitest --typecheck"
@@ -81,20 +81,21 @@ const scripts = {
81
81
  const devDependencies$1 = {
82
82
  "@biomejs/biome": "1.8.3",
83
83
  "@types/prettier": "3.0.0",
84
- "@types/yargs": "17.0.33",
85
- "@vitest/coverage-v8": "3.2.4",
84
+ "@types/yargs": "17.0.35",
85
+ "@typescript/native-preview": "7.0.0-dev.20260111.1",
86
+ "@vitest/coverage-v8": "4.0.18",
86
87
  json5: "2.2.3",
87
88
  picocolors: "1.1.1",
88
89
  tsconfig: "workspace:*",
89
90
  unbuild: "2.0.0",
90
- vitest: "3.2.4"
91
+ vitest: "4.0.18"
91
92
  };
92
93
  const dependencies$2 = {
93
94
  "@shopware/api-client": "workspace:*",
94
- ofetch: "1.4.1",
95
+ ofetch: "1.5.1",
95
96
  "openapi-typescript": "7.8.0",
96
97
  prettier: "3.7.4",
97
- "ts-morph": "26.0.0",
98
+ "ts-morph": "27.0.2",
98
99
  typescript: "5.9.3",
99
100
  yargs: "18.0.0"
100
101
  };
@@ -188,199 +189,207 @@ var commonjs = {};
188
189
 
189
190
  } (commonjs));
190
191
 
191
- Object.defineProperty(commonjs$1, "__esModule", { value: true });
192
- commonjs$1.expand = expand;
193
- const balanced_match_1 = commonjs;
194
- const escSlash = '\0SLASH' + Math.random() + '\0';
195
- const escOpen = '\0OPEN' + Math.random() + '\0';
196
- const escClose = '\0CLOSE' + Math.random() + '\0';
197
- const escComma = '\0COMMA' + Math.random() + '\0';
198
- const escPeriod = '\0PERIOD' + Math.random() + '\0';
199
- const escSlashPattern = new RegExp(escSlash, 'g');
200
- const escOpenPattern = new RegExp(escOpen, 'g');
201
- const escClosePattern = new RegExp(escClose, 'g');
202
- const escCommaPattern = new RegExp(escComma, 'g');
203
- const escPeriodPattern = new RegExp(escPeriod, 'g');
204
- const slashPattern = /\\\\/g;
205
- const openPattern = /\\{/g;
206
- const closePattern = /\\}/g;
207
- const commaPattern = /\\,/g;
208
- const periodPattern = /\\./g;
209
- function numeric(str) {
210
- return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
211
- }
212
- function escapeBraces(str) {
213
- return str
214
- .replace(slashPattern, escSlash)
215
- .replace(openPattern, escOpen)
216
- .replace(closePattern, escClose)
217
- .replace(commaPattern, escComma)
218
- .replace(periodPattern, escPeriod);
219
- }
220
- function unescapeBraces(str) {
221
- return str
222
- .replace(escSlashPattern, '\\')
223
- .replace(escOpenPattern, '{')
224
- .replace(escClosePattern, '}')
225
- .replace(escCommaPattern, ',')
226
- .replace(escPeriodPattern, '.');
227
- }
228
- /**
229
- * Basically just str.split(","), but handling cases
230
- * where we have nested braced sections, which should be
231
- * treated as individual members, like {a,{b,c},d}
232
- */
233
- function parseCommaParts(str) {
234
- if (!str) {
235
- return [''];
236
- }
237
- const parts = [];
238
- const m = (0, balanced_match_1.balanced)('{', '}', str);
239
- if (!m) {
240
- return str.split(',');
241
- }
242
- const { pre, body, post } = m;
243
- const p = pre.split(',');
244
- p[p.length - 1] += '{' + body + '}';
245
- const postParts = parseCommaParts(post);
246
- if (post.length) {
247
- p[p.length - 1] += postParts.shift();
248
- p.push.apply(p, postParts);
249
- }
250
- parts.push.apply(parts, p);
251
- return parts;
252
- }
253
- function expand(str) {
254
- if (!str) {
255
- return [];
256
- }
257
- // I don't know why Bash 4.3 does this, but it does.
258
- // Anything starting with {} will have the first two bytes preserved
259
- // but *only* at the top level, so {},a}b will not expand to anything,
260
- // but a{},b}c will be expanded to [a}c,abc].
261
- // One could argue that this is a bug in Bash, but since the goal of
262
- // this module is to match Bash's rules, we escape a leading {}
263
- if (str.slice(0, 2) === '{}') {
264
- str = '\\{\\}' + str.slice(2);
265
- }
266
- return expand_(escapeBraces(str), true).map(unescapeBraces);
267
- }
268
- function embrace(str) {
269
- return '{' + str + '}';
270
- }
271
- function isPadded(el) {
272
- return /^-?0\d/.test(el);
273
- }
274
- function lte(i, y) {
275
- return i <= y;
276
- }
277
- function gte(i, y) {
278
- return i >= y;
279
- }
280
- function expand_(str, isTop) {
281
- /** @type {string[]} */
282
- const expansions = [];
283
- const m = (0, balanced_match_1.balanced)('{', '}', str);
284
- if (!m)
285
- return [str];
286
- // no need to expand pre, since it is guaranteed to be free of brace-sets
287
- const pre = m.pre;
288
- const post = m.post.length ? expand_(m.post, false) : [''];
289
- if (/\$$/.test(m.pre)) {
290
- for (let k = 0; k < post.length; k++) {
291
- const expansion = pre + '{' + m.body + '}' + post[k];
292
- expansions.push(expansion);
293
- }
294
- }
295
- else {
296
- const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
297
- const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
298
- const isSequence = isNumericSequence || isAlphaSequence;
299
- const isOptions = m.body.indexOf(',') >= 0;
300
- if (!isSequence && !isOptions) {
301
- // {a},b}
302
- if (m.post.match(/,(?!,).*\}/)) {
303
- str = m.pre + '{' + m.body + escClose + m.post;
304
- return expand_(str);
305
- }
306
- return [str];
307
- }
308
- let n;
309
- if (isSequence) {
310
- n = m.body.split(/\.\./);
311
- }
312
- else {
313
- n = parseCommaParts(m.body);
314
- if (n.length === 1 && n[0] !== undefined) {
315
- // x{{a,b}}y ==> x{a}y x{b}y
316
- n = expand_(n[0], false).map(embrace);
317
- //XXX is this necessary? Can't seem to hit it in tests.
318
- /* c8 ignore start */
319
- if (n.length === 1) {
320
- return post.map(p => m.pre + n[0] + p);
321
- }
322
- /* c8 ignore stop */
323
- }
324
- }
325
- // at this point, n is the parts, and we know it's not a comma set
326
- // with a single entry.
327
- let N;
328
- if (isSequence && n[0] !== undefined && n[1] !== undefined) {
329
- const x = numeric(n[0]);
330
- const y = numeric(n[1]);
331
- const width = Math.max(n[0].length, n[1].length);
332
- let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
333
- let test = lte;
334
- const reverse = y < x;
335
- if (reverse) {
336
- incr *= -1;
337
- test = gte;
338
- }
339
- const pad = n.some(isPadded);
340
- N = [];
341
- for (let i = x; test(i, y); i += incr) {
342
- let c;
343
- if (isAlphaSequence) {
344
- c = String.fromCharCode(i);
345
- if (c === '\\') {
346
- c = '';
347
- }
348
- }
349
- else {
350
- c = String(i);
351
- if (pad) {
352
- const need = width - c.length;
353
- if (need > 0) {
354
- const z = new Array(need + 1).join('0');
355
- if (i < 0) {
356
- c = '-' + z + c.slice(1);
357
- }
358
- else {
359
- c = z + c;
360
- }
361
- }
362
- }
363
- }
364
- N.push(c);
365
- }
366
- }
367
- else {
368
- N = [];
369
- for (let j = 0; j < n.length; j++) {
370
- N.push.apply(N, expand_(n[j], false));
371
- }
372
- }
373
- for (let j = 0; j < N.length; j++) {
374
- for (let k = 0; k < post.length; k++) {
375
- const expansion = pre + N[j] + post[k];
376
- if (!isTop || isSequence || expansion) {
377
- expansions.push(expansion);
378
- }
379
- }
380
- }
381
- }
382
- return expansions;
383
- }
192
+ (function (exports) {
193
+ Object.defineProperty(exports, "__esModule", { value: true });
194
+ exports.EXPANSION_MAX = void 0;
195
+ exports.expand = expand;
196
+ const balanced_match_1 = commonjs;
197
+ const escSlash = '\0SLASH' + Math.random() + '\0';
198
+ const escOpen = '\0OPEN' + Math.random() + '\0';
199
+ const escClose = '\0CLOSE' + Math.random() + '\0';
200
+ const escComma = '\0COMMA' + Math.random() + '\0';
201
+ const escPeriod = '\0PERIOD' + Math.random() + '\0';
202
+ const escSlashPattern = new RegExp(escSlash, 'g');
203
+ const escOpenPattern = new RegExp(escOpen, 'g');
204
+ const escClosePattern = new RegExp(escClose, 'g');
205
+ const escCommaPattern = new RegExp(escComma, 'g');
206
+ const escPeriodPattern = new RegExp(escPeriod, 'g');
207
+ const slashPattern = /\\\\/g;
208
+ const openPattern = /\\{/g;
209
+ const closePattern = /\\}/g;
210
+ const commaPattern = /\\,/g;
211
+ const periodPattern = /\\\./g;
212
+ exports.EXPANSION_MAX = 100_000;
213
+ function numeric(str) {
214
+ return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
215
+ }
216
+ function escapeBraces(str) {
217
+ return str
218
+ .replace(slashPattern, escSlash)
219
+ .replace(openPattern, escOpen)
220
+ .replace(closePattern, escClose)
221
+ .replace(commaPattern, escComma)
222
+ .replace(periodPattern, escPeriod);
223
+ }
224
+ function unescapeBraces(str) {
225
+ return str
226
+ .replace(escSlashPattern, '\\')
227
+ .replace(escOpenPattern, '{')
228
+ .replace(escClosePattern, '}')
229
+ .replace(escCommaPattern, ',')
230
+ .replace(escPeriodPattern, '.');
231
+ }
232
+ /**
233
+ * Basically just str.split(","), but handling cases
234
+ * where we have nested braced sections, which should be
235
+ * treated as individual members, like {a,{b,c},d}
236
+ */
237
+ function parseCommaParts(str) {
238
+ if (!str) {
239
+ return [''];
240
+ }
241
+ const parts = [];
242
+ const m = (0, balanced_match_1.balanced)('{', '}', str);
243
+ if (!m) {
244
+ return str.split(',');
245
+ }
246
+ const { pre, body, post } = m;
247
+ const p = pre.split(',');
248
+ p[p.length - 1] += '{' + body + '}';
249
+ const postParts = parseCommaParts(post);
250
+ if (post.length) {
251
+ p[p.length - 1] += postParts.shift();
252
+ p.push.apply(p, postParts);
253
+ }
254
+ parts.push.apply(parts, p);
255
+ return parts;
256
+ }
257
+ function expand(str, options = {}) {
258
+ if (!str) {
259
+ return [];
260
+ }
261
+ const { max = exports.EXPANSION_MAX } = options;
262
+ // I don't know why Bash 4.3 does this, but it does.
263
+ // Anything starting with {} will have the first two bytes preserved
264
+ // but *only* at the top level, so {},a}b will not expand to anything,
265
+ // but a{},b}c will be expanded to [a}c,abc].
266
+ // One could argue that this is a bug in Bash, but since the goal of
267
+ // this module is to match Bash's rules, we escape a leading {}
268
+ if (str.slice(0, 2) === '{}') {
269
+ str = '\\{\\}' + str.slice(2);
270
+ }
271
+ return expand_(escapeBraces(str), max, true).map(unescapeBraces);
272
+ }
273
+ function embrace(str) {
274
+ return '{' + str + '}';
275
+ }
276
+ function isPadded(el) {
277
+ return /^-?0\d/.test(el);
278
+ }
279
+ function lte(i, y) {
280
+ return i <= y;
281
+ }
282
+ function gte(i, y) {
283
+ return i >= y;
284
+ }
285
+ function expand_(str, max, isTop) {
286
+ /** @type {string[]} */
287
+ const expansions = [];
288
+ const m = (0, balanced_match_1.balanced)('{', '}', str);
289
+ if (!m)
290
+ return [str];
291
+ // no need to expand pre, since it is guaranteed to be free of brace-sets
292
+ const pre = m.pre;
293
+ const post = m.post.length ? expand_(m.post, max, false) : [''];
294
+ if (/\$$/.test(m.pre)) {
295
+ for (let k = 0; k < post.length && k < max; k++) {
296
+ const expansion = pre + '{' + m.body + '}' + post[k];
297
+ expansions.push(expansion);
298
+ }
299
+ }
300
+ else {
301
+ const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
302
+ const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
303
+ const isSequence = isNumericSequence || isAlphaSequence;
304
+ const isOptions = m.body.indexOf(',') >= 0;
305
+ if (!isSequence && !isOptions) {
306
+ // {a},b}
307
+ if (m.post.match(/,(?!,).*\}/)) {
308
+ str = m.pre + '{' + m.body + escClose + m.post;
309
+ return expand_(str, max, true);
310
+ }
311
+ return [str];
312
+ }
313
+ let n;
314
+ if (isSequence) {
315
+ n = m.body.split(/\.\./);
316
+ }
317
+ else {
318
+ n = parseCommaParts(m.body);
319
+ if (n.length === 1 && n[0] !== undefined) {
320
+ // x{{a,b}}y ==> x{a}y x{b}y
321
+ n = expand_(n[0], max, false).map(embrace);
322
+ //XXX is this necessary? Can't seem to hit it in tests.
323
+ /* c8 ignore start */
324
+ if (n.length === 1) {
325
+ return post.map(p => m.pre + n[0] + p);
326
+ }
327
+ /* c8 ignore stop */
328
+ }
329
+ }
330
+ // at this point, n is the parts, and we know it's not a comma set
331
+ // with a single entry.
332
+ let N;
333
+ if (isSequence && n[0] !== undefined && n[1] !== undefined) {
334
+ const x = numeric(n[0]);
335
+ const y = numeric(n[1]);
336
+ const width = Math.max(n[0].length, n[1].length);
337
+ let incr = n.length === 3 && n[2] !== undefined ?
338
+ Math.max(Math.abs(numeric(n[2])), 1)
339
+ : 1;
340
+ let test = lte;
341
+ const reverse = y < x;
342
+ if (reverse) {
343
+ incr *= -1;
344
+ test = gte;
345
+ }
346
+ const pad = n.some(isPadded);
347
+ N = [];
348
+ for (let i = x; test(i, y); i += incr) {
349
+ let c;
350
+ if (isAlphaSequence) {
351
+ c = String.fromCharCode(i);
352
+ if (c === '\\') {
353
+ c = '';
354
+ }
355
+ }
356
+ else {
357
+ c = String(i);
358
+ if (pad) {
359
+ const need = width - c.length;
360
+ if (need > 0) {
361
+ const z = new Array(need + 1).join('0');
362
+ if (i < 0) {
363
+ c = '-' + z + c.slice(1);
364
+ }
365
+ else {
366
+ c = z + c;
367
+ }
368
+ }
369
+ }
370
+ }
371
+ N.push(c);
372
+ }
373
+ }
374
+ else {
375
+ N = [];
376
+ for (let j = 0; j < n.length; j++) {
377
+ N.push.apply(N, expand_(n[j], max, false));
378
+ }
379
+ }
380
+ for (let j = 0; j < N.length; j++) {
381
+ for (let k = 0; k < post.length && expansions.length < max; k++) {
382
+ const expansion = pre + N[j] + post[k];
383
+ if (!isTop || isSequence || expansion) {
384
+ expansions.push(expansion);
385
+ }
386
+ }
387
+ }
388
+ }
389
+ return expansions;
390
+ }
391
+
392
+ } (commonjs$1));
384
393
 
385
394
  var assertValidPattern$1 = {};
386
395
 
@@ -543,10 +552,8 @@ const parseClass = (glob, position) => {
543
552
  }
544
553
  const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']';
545
554
  const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']';
546
- const comb = ranges.length && negs.length
547
- ? '(' + sranges + '|' + snegs + ')'
548
- : ranges.length
549
- ? sranges
555
+ const comb = ranges.length && negs.length ? '(' + sranges + '|' + snegs + ')'
556
+ : ranges.length ? sranges
550
557
  : snegs;
551
558
  return [comb, uflag, endPos - pos, true];
552
559
  };
@@ -559,31 +566,147 @@ _unescape.unescape = void 0;
559
566
  /**
560
567
  * Un-escape a string that has been escaped with {@link escape}.
561
568
  *
562
- * If the {@link windowsPathsNoEscape} option is used, then square-brace
563
- * escapes are removed, but not backslash escapes. For example, it will turn
564
- * the string `'[*]'` into `*`, but it will not turn `'\\*'` into `'*'`,
565
- * becuase `\` is a path separator in `windowsPathsNoEscape` mode.
569
+ * If the {@link MinimatchOptions.windowsPathsNoEscape} option is used, then
570
+ * square-bracket escapes are removed, but not backslash escapes.
571
+ *
572
+ * For example, it will turn the string `'[*]'` into `*`, but it will not
573
+ * turn `'\\*'` into `'*'`, because `\` is a path separator in
574
+ * `windowsPathsNoEscape` mode.
566
575
  *
567
- * When `windowsPathsNoEscape` is not set, then both brace escapes and
576
+ * When `windowsPathsNoEscape` is not set, then both square-bracket escapes and
568
577
  * backslash escapes are removed.
569
578
  *
570
579
  * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
571
580
  * or unescaped.
581
+ *
582
+ * When `magicalBraces` is not set, escapes of braces (`{` and `}`) will not be
583
+ * unescaped.
572
584
  */
573
- const unescape = (s, { windowsPathsNoEscape = false, } = {}) => {
574
- return windowsPathsNoEscape
575
- ? s.replace(/\[([^\/\\])\]/g, '$1')
576
- : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, '$1$2').replace(/\\([^\/])/g, '$1');
585
+ const unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true, } = {}) => {
586
+ if (magicalBraces) {
587
+ return windowsPathsNoEscape ?
588
+ s.replace(/\[([^\/\\])\]/g, '$1')
589
+ : s
590
+ .replace(/((?!\\).|^)\[([^\/\\])\]/g, '$1$2')
591
+ .replace(/\\([^\/])/g, '$1');
592
+ }
593
+ return windowsPathsNoEscape ?
594
+ s.replace(/\[([^\/\\{}])\]/g, '$1')
595
+ : s
596
+ .replace(/((?!\\).|^)\[([^\/\\{}])\]/g, '$1$2')
597
+ .replace(/\\([^\/{}])/g, '$1');
577
598
  };
578
599
  _unescape.unescape = unescape;
579
600
 
580
601
  // parse a single path portion
602
+ var _a;
581
603
  Object.defineProperty(ast$3, "__esModule", { value: true });
582
604
  ast$3.AST = void 0;
583
605
  const brace_expressions_js_1 = braceExpressions;
584
606
  const unescape_js_1 = _unescape;
585
607
  const types$3 = new Set(['!', '?', '+', '*', '@']);
586
608
  const isExtglobType = (c) => types$3.has(c);
609
+ const isExtglobAST = (c) => isExtglobType(c.type);
610
+ // Map of which extglob types can adopt the children of a nested extglob
611
+ //
612
+ // anything but ! can adopt a matching type:
613
+ // +(a|+(b|c)|d) => +(a|b|c|d)
614
+ // *(a|*(b|c)|d) => *(a|b|c|d)
615
+ // @(a|@(b|c)|d) => @(a|b|c|d)
616
+ // ?(a|?(b|c)|d) => ?(a|b|c|d)
617
+ //
618
+ // * can adopt anything, because 0 or repetition is allowed
619
+ // *(a|?(b|c)|d) => *(a|b|c|d)
620
+ // *(a|+(b|c)|d) => *(a|b|c|d)
621
+ // *(a|@(b|c)|d) => *(a|b|c|d)
622
+ //
623
+ // + can adopt @, because 1 or repetition is allowed
624
+ // +(a|@(b|c)|d) => +(a|b|c|d)
625
+ //
626
+ // + and @ CANNOT adopt *, because 0 would be allowed
627
+ // +(a|*(b|c)|d) => would match "", on *(b|c)
628
+ // @(a|*(b|c)|d) => would match "", on *(b|c)
629
+ //
630
+ // + and @ CANNOT adopt ?, because 0 would be allowed
631
+ // +(a|?(b|c)|d) => would match "", on ?(b|c)
632
+ // @(a|?(b|c)|d) => would match "", on ?(b|c)
633
+ //
634
+ // ? can adopt @, because 0 or 1 is allowed
635
+ // ?(a|@(b|c)|d) => ?(a|b|c|d)
636
+ //
637
+ // ? and @ CANNOT adopt * or +, because >1 would be allowed
638
+ // ?(a|*(b|c)|d) => would match bbb on *(b|c)
639
+ // @(a|*(b|c)|d) => would match bbb on *(b|c)
640
+ // ?(a|+(b|c)|d) => would match bbb on +(b|c)
641
+ // @(a|+(b|c)|d) => would match bbb on +(b|c)
642
+ //
643
+ // ! CANNOT adopt ! (nothing else can either)
644
+ // !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
645
+ //
646
+ // ! can adopt @
647
+ // !(a|@(b|c)|d) => !(a|b|c|d)
648
+ //
649
+ // ! CANNOT adopt *
650
+ // !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
651
+ //
652
+ // ! CANNOT adopt +
653
+ // !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
654
+ //
655
+ // ! CANNOT adopt ?
656
+ // x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
657
+ const adoptionMap = new Map([
658
+ ['!', ['@']],
659
+ ['?', ['?', '@']],
660
+ ['@', ['@']],
661
+ ['*', ['*', '+', '?', '@']],
662
+ ['+', ['+', '@']],
663
+ ]);
664
+ // nested extglobs that can be adopted in, but with the addition of
665
+ // a blank '' element.
666
+ const adoptionWithSpaceMap = new Map([
667
+ ['!', ['?']],
668
+ ['@', ['?']],
669
+ ['+', ['?', '*']],
670
+ ]);
671
+ // union of the previous two maps
672
+ const adoptionAnyMap = new Map([
673
+ ['!', ['?', '@']],
674
+ ['?', ['?', '@']],
675
+ ['@', ['?', '@']],
676
+ ['*', ['*', '+', '?', '@']],
677
+ ['+', ['+', '@', '?', '*']],
678
+ ]);
679
+ // Extglobs that can take over their parent if they are the only child
680
+ // the key is parent, value maps child to resulting extglob parent type
681
+ // '@' is omitted because it's a special case. An `@` extglob with a single
682
+ // member can always be usurped by that subpattern.
683
+ const usurpMap = new Map([
684
+ ['!', new Map([['!', '@']])],
685
+ [
686
+ '?',
687
+ new Map([
688
+ ['*', '*'],
689
+ ['+', '*'],
690
+ ]),
691
+ ],
692
+ [
693
+ '@',
694
+ new Map([
695
+ ['!', '!'],
696
+ ['?', '?'],
697
+ ['@', '@'],
698
+ ['*', '*'],
699
+ ['+', '+'],
700
+ ]),
701
+ ],
702
+ [
703
+ '+',
704
+ new Map([
705
+ ['?', '*'],
706
+ ['*', '*'],
707
+ ]),
708
+ ],
709
+ ]);
587
710
  // Patterns that get prepended to bind to the start of either the
588
711
  // entire string, or just a single path portion, to prevent dots
589
712
  // and/or traversal patterns, when needed.
@@ -607,6 +730,7 @@ const star = qmark + '*?';
607
730
  const starNoEmpty = qmark + '+?';
608
731
  // remove the \ chars that we added if we end up doing a nonmagic compare
609
732
  // const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
733
+ let ID = 0;
610
734
  class AST {
611
735
  type;
612
736
  #root;
@@ -622,6 +746,22 @@ class AST {
622
746
  // set to true if it's an extglob with no children
623
747
  // (which really means one child of '')
624
748
  #emptyExt = false;
749
+ id = ++ID;
750
+ get depth() {
751
+ return (this.#parent?.depth ?? -1) + 1;
752
+ }
753
+ [Symbol.for('nodejs.util.inspect.custom')]() {
754
+ return {
755
+ '@@type': 'AST',
756
+ id: this.id,
757
+ type: this.type,
758
+ root: this.#root.id,
759
+ parent: this.#parent?.id,
760
+ depth: this.depth,
761
+ partsLength: this.#parts.length,
762
+ parts: this.#parts,
763
+ };
764
+ }
625
765
  constructor(type, parent, options = {}) {
626
766
  this.type = type;
627
767
  // extglobs are inherently magical
@@ -700,7 +840,8 @@ class AST {
700
840
  if (p === '')
701
841
  continue;
702
842
  /* c8 ignore start */
703
- if (typeof p !== 'string' && !(p instanceof AST && p.#parent === this)) {
843
+ if (typeof p !== 'string' &&
844
+ !(p instanceof _a && p.#parent === this)) {
704
845
  throw new Error('invalid part: ' + p);
705
846
  }
706
847
  /* c8 ignore stop */
@@ -708,8 +849,10 @@ class AST {
708
849
  }
709
850
  }
710
851
  toJSON() {
711
- const ret = this.type === null
712
- ? this.#parts.slice().map(p => (typeof p === 'string' ? p : p.toJSON()))
852
+ const ret = this.type === null ?
853
+ this.#parts
854
+ .slice()
855
+ .map(p => (typeof p === 'string' ? p : p.toJSON()))
713
856
  : [this.type, ...this.#parts.map(p => p.toJSON())];
714
857
  if (this.isStart() && !this.type)
715
858
  ret.unshift([]);
@@ -732,7 +875,7 @@ class AST {
732
875
  const p = this.#parent;
733
876
  for (let i = 0; i < this.#parentIndex; i++) {
734
877
  const pp = p.#parts[i];
735
- if (!(pp instanceof AST && pp.type === '!')) {
878
+ if (!(pp instanceof _a && pp.type === '!')) {
736
879
  return false;
737
880
  }
738
881
  }
@@ -760,13 +903,14 @@ class AST {
760
903
  this.push(part.clone(this));
761
904
  }
762
905
  clone(parent) {
763
- const c = new AST(this.type, parent);
906
+ const c = new _a(this.type, parent);
764
907
  for (const p of this.#parts) {
765
908
  c.copyIn(p);
766
909
  }
767
910
  return c;
768
911
  }
769
- static #parseAST(str, ast, pos, opt) {
912
+ static #parseAST(str, ast, pos, opt, extDepth) {
913
+ const maxDepth = opt.maxExtglobRecursion ?? 2;
770
914
  let escaping = false;
771
915
  let inBrace = false;
772
916
  let braceStart = -1;
@@ -803,11 +947,17 @@ class AST {
803
947
  acc += c;
804
948
  continue;
805
949
  }
806
- if (!opt.noext && isExtglobType(c) && str.charAt(i) === '(') {
950
+ // we don't have to check for adoption here, because that's
951
+ // done at the other recursion point.
952
+ const doRecurse = !opt.noext &&
953
+ isExtglobType(c) &&
954
+ str.charAt(i) === '(' &&
955
+ extDepth <= maxDepth;
956
+ if (doRecurse) {
807
957
  ast.push(acc);
808
958
  acc = '';
809
- const ext = new AST(c, ast);
810
- i = AST.#parseAST(str, ext, i, opt);
959
+ const ext = new _a(c, ast);
960
+ i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
811
961
  ast.push(ext);
812
962
  continue;
813
963
  }
@@ -819,7 +969,7 @@ class AST {
819
969
  // some kind of extglob, pos is at the (
820
970
  // find the next | or )
821
971
  let i = pos + 1;
822
- let part = new AST(null, ast);
972
+ let part = new _a(null, ast);
823
973
  const parts = [];
824
974
  let acc = '';
825
975
  while (i < str.length) {
@@ -850,19 +1000,26 @@ class AST {
850
1000
  acc += c;
851
1001
  continue;
852
1002
  }
853
- if (isExtglobType(c) && str.charAt(i) === '(') {
1003
+ const doRecurse = !opt.noext &&
1004
+ isExtglobType(c) &&
1005
+ str.charAt(i) === '(' &&
1006
+ /* c8 ignore start - the maxDepth is sufficient here */
1007
+ (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
1008
+ /* c8 ignore stop */
1009
+ if (doRecurse) {
1010
+ const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
854
1011
  part.push(acc);
855
1012
  acc = '';
856
- const ext = new AST(c, part);
1013
+ const ext = new _a(c, part);
857
1014
  part.push(ext);
858
- i = AST.#parseAST(str, ext, i, opt);
1015
+ i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
859
1016
  continue;
860
1017
  }
861
1018
  if (c === '|') {
862
1019
  part.push(acc);
863
1020
  acc = '';
864
1021
  parts.push(part);
865
- part = new AST(null, ast);
1022
+ part = new _a(null, ast);
866
1023
  continue;
867
1024
  }
868
1025
  if (c === ')') {
@@ -884,9 +1041,82 @@ class AST {
884
1041
  ast.#parts = [str.substring(pos - 1)];
885
1042
  return i;
886
1043
  }
1044
+ #canAdoptWithSpace(child) {
1045
+ return this.#canAdopt(child, adoptionWithSpaceMap);
1046
+ }
1047
+ #canAdopt(child, map = adoptionMap) {
1048
+ if (!child ||
1049
+ typeof child !== 'object' ||
1050
+ child.type !== null ||
1051
+ child.#parts.length !== 1 ||
1052
+ this.type === null) {
1053
+ return false;
1054
+ }
1055
+ const gc = child.#parts[0];
1056
+ if (!gc || typeof gc !== 'object' || gc.type === null) {
1057
+ return false;
1058
+ }
1059
+ return this.#canAdoptType(gc.type, map);
1060
+ }
1061
+ #canAdoptType(c, map = adoptionAnyMap) {
1062
+ return !!map.get(this.type)?.includes(c);
1063
+ }
1064
+ #adoptWithSpace(child, index) {
1065
+ const gc = child.#parts[0];
1066
+ const blank = new _a(null, gc, this.options);
1067
+ blank.#parts.push('');
1068
+ gc.push(blank);
1069
+ this.#adopt(child, index);
1070
+ }
1071
+ #adopt(child, index) {
1072
+ const gc = child.#parts[0];
1073
+ this.#parts.splice(index, 1, ...gc.#parts);
1074
+ for (const p of gc.#parts) {
1075
+ if (typeof p === 'object')
1076
+ p.#parent = this;
1077
+ }
1078
+ this.#toString = undefined;
1079
+ }
1080
+ #canUsurpType(c) {
1081
+ const m = usurpMap.get(this.type);
1082
+ return !!(m?.has(c));
1083
+ }
1084
+ #canUsurp(child) {
1085
+ if (!child ||
1086
+ typeof child !== 'object' ||
1087
+ child.type !== null ||
1088
+ child.#parts.length !== 1 ||
1089
+ this.type === null ||
1090
+ this.#parts.length !== 1) {
1091
+ return false;
1092
+ }
1093
+ const gc = child.#parts[0];
1094
+ if (!gc || typeof gc !== 'object' || gc.type === null) {
1095
+ return false;
1096
+ }
1097
+ return this.#canUsurpType(gc.type);
1098
+ }
1099
+ #usurp(child) {
1100
+ const m = usurpMap.get(this.type);
1101
+ const gc = child.#parts[0];
1102
+ const nt = m?.get(gc.type);
1103
+ /* c8 ignore start - impossible */
1104
+ if (!nt)
1105
+ return false;
1106
+ /* c8 ignore stop */
1107
+ this.#parts = gc.#parts;
1108
+ for (const p of this.#parts) {
1109
+ if (typeof p === 'object') {
1110
+ p.#parent = this;
1111
+ }
1112
+ }
1113
+ this.type = nt;
1114
+ this.#toString = undefined;
1115
+ this.#emptyExt = false;
1116
+ }
887
1117
  static fromGlob(pattern, options = {}) {
888
- const ast = new AST(null, undefined, options);
889
- AST.#parseAST(pattern, ast, 0, options);
1118
+ const ast = new _a(null, undefined, options);
1119
+ _a.#parseAST(pattern, ast, 0, options, 0);
890
1120
  return ast;
891
1121
  }
892
1122
  // returns the regular expression if there's magic, or the unescaped
@@ -990,14 +1220,18 @@ class AST {
990
1220
  // or start or whatever) and prepend ^ or / at the Regexp construction.
991
1221
  toRegExpSource(allowDot) {
992
1222
  const dot = allowDot ?? !!this.#options.dot;
993
- if (this.#root === this)
1223
+ if (this.#root === this) {
1224
+ this.#flatten();
994
1225
  this.#fillNegs();
995
- if (!this.type) {
996
- const noEmpty = this.isStart() && this.isEnd();
1226
+ }
1227
+ if (!isExtglobAST(this)) {
1228
+ const noEmpty = this.isStart() &&
1229
+ this.isEnd() &&
1230
+ !this.#parts.some(s => typeof s !== 'string');
997
1231
  const src = this.#parts
998
1232
  .map(p => {
999
- const [re, _, hasMagic, uflag] = typeof p === 'string'
1000
- ? AST.#parseGlob(p, this.#hasMagic, noEmpty)
1233
+ const [re, _, hasMagic, uflag] = typeof p === 'string' ?
1234
+ _a.#parseGlob(p, this.#hasMagic, noEmpty)
1001
1235
  : p.toRegExpSource(allowDot);
1002
1236
  this.#hasMagic = this.#hasMagic || hasMagic;
1003
1237
  this.#uflag = this.#uflag || uflag;
@@ -1026,7 +1260,10 @@ class AST {
1026
1260
  // no need to prevent dots if it can't match a dot, or if a
1027
1261
  // sub-pattern will be preventing it anyway.
1028
1262
  const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
1029
- start = needNoTrav ? startNoTraversal : needNoDot ? startNoDot : '';
1263
+ start =
1264
+ needNoTrav ? startNoTraversal
1265
+ : needNoDot ? startNoDot
1266
+ : '';
1030
1267
  }
1031
1268
  }
1032
1269
  }
@@ -1056,14 +1293,14 @@ class AST {
1056
1293
  // invalid extglob, has to at least be *something* present, if it's
1057
1294
  // the entire path portion.
1058
1295
  const s = this.toString();
1059
- this.#parts = [s];
1060
- this.type = null;
1061
- this.#hasMagic = undefined;
1296
+ const me = this;
1297
+ me.#parts = [s];
1298
+ me.type = null;
1299
+ me.#hasMagic = undefined;
1062
1300
  return [s, (0, unescape_js_1.unescape)(this.toString()), false, false];
1063
1301
  }
1064
- // XXX abstract out this map method
1065
- let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot
1066
- ? ''
1302
+ let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
1303
+ ''
1067
1304
  : this.#partsToRegExp(true);
1068
1305
  if (bodyDotAllowed === body) {
1069
1306
  bodyDotAllowed = '';
@@ -1077,20 +1314,16 @@ class AST {
1077
1314
  final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
1078
1315
  }
1079
1316
  else {
1080
- const close = this.type === '!'
1081
- ? // !() must match something,but !(x) can match ''
1082
- '))' +
1083
- (this.isStart() && !dot && !allowDot ? startNoDot : '') +
1084
- star +
1085
- ')'
1086
- : this.type === '@'
1087
- ? ')'
1088
- : this.type === '?'
1089
- ? ')?'
1090
- : this.type === '+' && bodyDotAllowed
1091
- ? ')'
1092
- : this.type === '*' && bodyDotAllowed
1093
- ? `)?`
1317
+ const close = this.type === '!' ?
1318
+ // !() must match something,but !(x) can match ''
1319
+ '))' +
1320
+ (this.isStart() && !dot && !allowDot ? startNoDot : '') +
1321
+ star +
1322
+ ')'
1323
+ : this.type === '@' ? ')'
1324
+ : this.type === '?' ? ')?'
1325
+ : this.type === '+' && bodyDotAllowed ? ')'
1326
+ : this.type === '*' && bodyDotAllowed ? `)?`
1094
1327
  : `)${this.type}`;
1095
1328
  final = start + body + close;
1096
1329
  }
@@ -1101,6 +1334,42 @@ class AST {
1101
1334
  this.#uflag,
1102
1335
  ];
1103
1336
  }
1337
+ #flatten() {
1338
+ if (!isExtglobAST(this)) {
1339
+ for (const p of this.#parts) {
1340
+ if (typeof p === 'object') {
1341
+ p.#flatten();
1342
+ }
1343
+ }
1344
+ }
1345
+ else {
1346
+ // do up to 10 passes to flatten as much as possible
1347
+ let iterations = 0;
1348
+ let done = false;
1349
+ do {
1350
+ done = true;
1351
+ for (let i = 0; i < this.#parts.length; i++) {
1352
+ const c = this.#parts[i];
1353
+ if (typeof c === 'object') {
1354
+ c.#flatten();
1355
+ if (this.#canAdopt(c)) {
1356
+ done = false;
1357
+ this.#adopt(c, i);
1358
+ }
1359
+ else if (this.#canAdoptWithSpace(c)) {
1360
+ done = false;
1361
+ this.#adoptWithSpace(c, i);
1362
+ }
1363
+ else if (this.#canUsurp(c)) {
1364
+ done = false;
1365
+ this.#usurp(c);
1366
+ }
1367
+ }
1368
+ }
1369
+ } while (!done && ++iterations < 10);
1370
+ }
1371
+ this.#toString = undefined;
1372
+ }
1104
1373
  #partsToRegExp(dot) {
1105
1374
  return this.#parts
1106
1375
  .map(p => {
@@ -1122,6 +1391,8 @@ class AST {
1122
1391
  let escaping = false;
1123
1392
  let re = '';
1124
1393
  let uflag = false;
1394
+ // multiple stars that aren't globstars coalesce into one *
1395
+ let inStar = false;
1125
1396
  for (let i = 0; i < glob.length; i++) {
1126
1397
  const c = glob.charAt(i);
1127
1398
  if (escaping) {
@@ -1129,6 +1400,17 @@ class AST {
1129
1400
  re += (reSpecials.has(c) ? '\\' : '') + c;
1130
1401
  continue;
1131
1402
  }
1403
+ if (c === '*') {
1404
+ if (inStar)
1405
+ continue;
1406
+ inStar = true;
1407
+ re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star;
1408
+ hasMagic = true;
1409
+ continue;
1410
+ }
1411
+ else {
1412
+ inStar = false;
1413
+ }
1132
1414
  if (c === '\\') {
1133
1415
  if (i === glob.length - 1) {
1134
1416
  re += '\\\\';
@@ -1148,14 +1430,6 @@ class AST {
1148
1430
  continue;
1149
1431
  }
1150
1432
  }
1151
- if (c === '*') {
1152
- if (noEmpty && glob === '*')
1153
- re += starNoEmpty;
1154
- else
1155
- re += star;
1156
- hasMagic = true;
1157
- continue;
1158
- }
1159
1433
  if (c === '?') {
1160
1434
  re += qmark;
1161
1435
  hasMagic = true;
@@ -1167,6 +1441,7 @@ class AST {
1167
1441
  }
1168
1442
  }
1169
1443
  ast$3.AST = AST;
1444
+ _a = AST;
1170
1445
 
1171
1446
  var _escape = {};
1172
1447
 
@@ -1175,18 +1450,26 @@ _escape.escape = void 0;
1175
1450
  /**
1176
1451
  * Escape all magic characters in a glob pattern.
1177
1452
  *
1178
- * If the {@link windowsPathsNoEscape | GlobOptions.windowsPathsNoEscape}
1453
+ * If the {@link MinimatchOptions.windowsPathsNoEscape}
1179
1454
  * option is used, then characters are escaped by wrapping in `[]`, because
1180
1455
  * a magic character wrapped in a character class can only be satisfied by
1181
1456
  * that exact character. In this mode, `\` is _not_ escaped, because it is
1182
1457
  * not interpreted as a magic character, but instead as a path separator.
1458
+ *
1459
+ * If the {@link MinimatchOptions.magicalBraces} option is used,
1460
+ * then braces (`{` and `}`) will be escaped.
1183
1461
  */
1184
- const escape = (s, { windowsPathsNoEscape = false, } = {}) => {
1462
+ const escape = (s, { windowsPathsNoEscape = false, magicalBraces = false, } = {}) => {
1185
1463
  // don't need to escape +@! because we escape the parens
1186
1464
  // that make those magic, and escaping ! as [!] isn't valid,
1187
1465
  // because [!]] is a valid glob class meaning not ']'.
1188
- return windowsPathsNoEscape
1189
- ? s.replace(/[?*()[\]]/g, '[$&]')
1466
+ if (magicalBraces) {
1467
+ return windowsPathsNoEscape ?
1468
+ s.replace(/[?*()[\]{}]/g, '[$&]')
1469
+ : s.replace(/[?*()[\]\\{}]/g, '\\$&');
1470
+ }
1471
+ return windowsPathsNoEscape ?
1472
+ s.replace(/[?*()[\]]/g, '[$&]')
1190
1473
  : s.replace(/[?*()[\]\\]/g, '\\$&');
1191
1474
  };
1192
1475
  _escape.escape = escape;
@@ -1260,8 +1543,8 @@ _escape.escape = escape;
1260
1543
  return (f) => f.length === len && f !== '.' && f !== '..';
1261
1544
  };
1262
1545
  /* c8 ignore start */
1263
- const defaultPlatform = (typeof process === 'object' && process
1264
- ? (typeof process.env === 'object' &&
1546
+ const defaultPlatform = (typeof process === 'object' && process ?
1547
+ (typeof process.env === 'object' &&
1265
1548
  process.env &&
1266
1549
  process.env.__MINIMATCH_TESTING_PLATFORM__) ||
1267
1550
  process.platform
@@ -1347,7 +1630,7 @@ _escape.escape = escape;
1347
1630
  // shortcut. no need to expand.
1348
1631
  return [pattern];
1349
1632
  }
1350
- return (0, brace_expansion_1.expand)(pattern);
1633
+ return (0, brace_expansion_1.expand)(pattern, { max: options.braceExpandMax });
1351
1634
  };
1352
1635
  exports.braceExpand = braceExpand;
1353
1636
  exports.minimatch.braceExpand = exports.braceExpand;
@@ -1395,16 +1678,20 @@ _escape.escape = escape;
1395
1678
  isWindows;
1396
1679
  platform;
1397
1680
  windowsNoMagicRoot;
1681
+ maxGlobstarRecursion;
1398
1682
  regexp;
1399
1683
  constructor(pattern, options = {}) {
1400
1684
  (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
1401
1685
  options = options || {};
1402
1686
  this.options = options;
1687
+ this.maxGlobstarRecursion = options.maxGlobstarRecursion ?? 200;
1403
1688
  this.pattern = pattern;
1404
1689
  this.platform = options.platform || defaultPlatform;
1405
1690
  this.isWindows = this.platform === 'win32';
1691
+ // avoid the annoying deprecation flag lol
1692
+ const awe = ('allowWindow' + 'sEscape');
1406
1693
  this.windowsPathsNoEscape =
1407
- !!options.windowsPathsNoEscape || options.allowWindowsEscape === false;
1694
+ !!options.windowsPathsNoEscape || options[awe] === false;
1408
1695
  if (this.windowsPathsNoEscape) {
1409
1696
  this.pattern = this.pattern.replace(/\\/g, '/');
1410
1697
  }
@@ -1417,8 +1704,8 @@ _escape.escape = escape;
1417
1704
  this.partial = !!options.partial;
1418
1705
  this.nocase = !!this.options.nocase;
1419
1706
  this.windowsNoMagicRoot =
1420
- options.windowsNoMagicRoot !== undefined
1421
- ? options.windowsNoMagicRoot
1707
+ options.windowsNoMagicRoot !== undefined ?
1708
+ options.windowsNoMagicRoot
1422
1709
  : !!(this.isWindows && this.nocase);
1423
1710
  this.globSet = [];
1424
1711
  this.globParts = [];
@@ -1481,7 +1768,10 @@ _escape.escape = escape;
1481
1768
  !globMagic.test(s[3]);
1482
1769
  const isDrive = /^[a-z]:/i.test(s[0]);
1483
1770
  if (isUNC) {
1484
- return [...s.slice(0, 4), ...s.slice(4).map(ss => this.parse(ss))];
1771
+ return [
1772
+ ...s.slice(0, 4),
1773
+ ...s.slice(4).map(ss => this.parse(ss)),
1774
+ ];
1485
1775
  }
1486
1776
  else if (isDrive) {
1487
1777
  return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
@@ -1513,7 +1803,7 @@ _escape.escape = escape;
1513
1803
  // to the right as possible, even if it increases the number
1514
1804
  // of patterns that we have to process.
1515
1805
  preprocess(globParts) {
1516
- // if we're not in globstar mode, then turn all ** into *
1806
+ // if we're not in globstar mode, then turn ** into *
1517
1807
  if (this.options.noglobstar) {
1518
1808
  for (let i = 0; i < globParts.length; i++) {
1519
1809
  for (let j = 0; j < globParts[i].length; j++) {
@@ -1799,7 +2089,8 @@ _escape.escape = escape;
1799
2089
  // out of pattern, then that's fine, as long as all
1800
2090
  // the parts match.
1801
2091
  matchOne(file, pattern, partial = false) {
1802
- const options = this.options;
2092
+ let fileStartIndex = 0;
2093
+ let patternStartIndex = 0;
1803
2094
  // UNC paths like //?/X:/... can match X:/... and vice versa
1804
2095
  // Drive letters in absolute drive or unc paths are always compared
1805
2096
  // case-insensitively.
@@ -1817,120 +2108,210 @@ _escape.escape = escape;
1817
2108
  pattern[2] === '?' &&
1818
2109
  typeof pattern[3] === 'string' &&
1819
2110
  /^[a-z]:$/i.test(pattern[3]);
1820
- const fdi = fileUNC ? 3 : fileDrive ? 0 : undefined;
1821
- const pdi = patternUNC ? 3 : patternDrive ? 0 : undefined;
2111
+ const fdi = fileUNC ? 3
2112
+ : fileDrive ? 0
2113
+ : undefined;
2114
+ const pdi = patternUNC ? 3
2115
+ : patternDrive ? 0
2116
+ : undefined;
1822
2117
  if (typeof fdi === 'number' && typeof pdi === 'number') {
1823
- const [fd, pd] = [file[fdi], pattern[pdi]];
2118
+ const [fd, pd] = [
2119
+ file[fdi],
2120
+ pattern[pdi],
2121
+ ];
2122
+ // start matching at the drive letter index of each
1824
2123
  if (fd.toLowerCase() === pd.toLowerCase()) {
1825
2124
  pattern[pdi] = fd;
1826
- if (pdi > fdi) {
1827
- pattern = pattern.slice(pdi);
1828
- }
1829
- else if (fdi > pdi) {
1830
- file = file.slice(fdi);
1831
- }
2125
+ patternStartIndex = pdi;
2126
+ fileStartIndex = fdi;
1832
2127
  }
1833
2128
  }
1834
2129
  }
1835
2130
  // resolve and reduce . and .. portions in the file as well.
1836
- // dont' need to do the second phase, because it's only one string[]
2131
+ // don't need to do the second phase, because it's only one string[]
1837
2132
  const { optimizationLevel = 1 } = this.options;
1838
2133
  if (optimizationLevel >= 2) {
1839
2134
  file = this.levelTwoFileOptimize(file);
1840
2135
  }
1841
- this.debug('matchOne', this, { file, pattern });
1842
- this.debug('matchOne', file.length, pattern.length);
1843
- for (var fi = 0, pi = 0, fl = file.length, pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
2136
+ if (pattern.includes(exports.GLOBSTAR)) {
2137
+ return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
2138
+ }
2139
+ return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
2140
+ }
2141
+ #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
2142
+ // split the pattern into head, tail, and middle of ** delimited parts
2143
+ const firstgs = pattern.indexOf(exports.GLOBSTAR, patternIndex);
2144
+ const lastgs = pattern.lastIndexOf(exports.GLOBSTAR);
2145
+ // split the pattern up into globstar-delimited sections
2146
+ // the tail has to be at the end, and the others just have
2147
+ // to be found in order from the head.
2148
+ const [head, body, tail] = partial ? [
2149
+ pattern.slice(patternIndex, firstgs),
2150
+ pattern.slice(firstgs + 1),
2151
+ [],
2152
+ ] : [
2153
+ pattern.slice(patternIndex, firstgs),
2154
+ pattern.slice(firstgs + 1, lastgs),
2155
+ pattern.slice(lastgs + 1),
2156
+ ];
2157
+ // check the head, from the current file/pattern index.
2158
+ if (head.length) {
2159
+ const fileHead = file.slice(fileIndex, fileIndex + head.length);
2160
+ if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
2161
+ return false;
2162
+ }
2163
+ fileIndex += head.length;
2164
+ patternIndex += head.length;
2165
+ }
2166
+ // now we know the head matches!
2167
+ // if the last portion is not empty, it MUST match the end
2168
+ // check the tail
2169
+ let fileTailMatch = 0;
2170
+ if (tail.length) {
2171
+ // if head + tail > file, then we cannot possibly match
2172
+ if (tail.length + fileIndex > file.length)
2173
+ return false;
2174
+ // try to match the tail
2175
+ let tailStart = file.length - tail.length;
2176
+ if (this.#matchOne(file, tail, partial, tailStart, 0)) {
2177
+ fileTailMatch = tail.length;
2178
+ }
2179
+ else {
2180
+ // affordance for stuff like a/**/* matching a/b/
2181
+ // if the last file portion is '', and there's more to the pattern
2182
+ // then try without the '' bit.
2183
+ if (file[file.length - 1] !== '' ||
2184
+ fileIndex + tail.length === file.length) {
2185
+ return false;
2186
+ }
2187
+ tailStart--;
2188
+ if (!this.#matchOne(file, tail, partial, tailStart, 0)) {
2189
+ return false;
2190
+ }
2191
+ fileTailMatch = tail.length + 1;
2192
+ }
2193
+ }
2194
+ // now we know the tail matches!
2195
+ // the middle is zero or more portions wrapped in **, possibly
2196
+ // containing more ** sections.
2197
+ // so a/**/b/**/c/**/d has become **/b/**/c/**
2198
+ // if it's empty, it means a/**/b, just verify we have no bad dots
2199
+ // if there's no tail, so it ends on /**, then we must have *something*
2200
+ // after the head, or it's not a matc
2201
+ if (!body.length) {
2202
+ let sawSome = !!fileTailMatch;
2203
+ for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
2204
+ const f = String(file[i]);
2205
+ sawSome = true;
2206
+ if (f === '.' ||
2207
+ f === '..' ||
2208
+ (!this.options.dot && f.startsWith('.'))) {
2209
+ return false;
2210
+ }
2211
+ }
2212
+ // in partial mode, we just need to get past all file parts
2213
+ return partial || sawSome;
2214
+ }
2215
+ // now we know that there's one or more body sections, which can
2216
+ // be matched anywhere from the 0 index (because the head was pruned)
2217
+ // through to the length-fileTailMatch index.
2218
+ // split the body up into sections, and note the minimum index it can
2219
+ // be found at (start with the length of all previous segments)
2220
+ // [section, before, after]
2221
+ const bodySegments = [[[], 0]];
2222
+ let currentBody = bodySegments[0];
2223
+ let nonGsParts = 0;
2224
+ const nonGsPartsSums = [0];
2225
+ for (const b of body) {
2226
+ if (b === exports.GLOBSTAR) {
2227
+ nonGsPartsSums.push(nonGsParts);
2228
+ currentBody = [[], 0];
2229
+ bodySegments.push(currentBody);
2230
+ }
2231
+ else {
2232
+ currentBody[0].push(b);
2233
+ nonGsParts++;
2234
+ }
2235
+ }
2236
+ let i = bodySegments.length - 1;
2237
+ const fileLength = file.length - fileTailMatch;
2238
+ for (const b of bodySegments) {
2239
+ b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
2240
+ }
2241
+ return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
2242
+ }
2243
+ // return false for "nope, not matching"
2244
+ // return null for "not matching, cannot keep trying"
2245
+ #matchGlobStarBodySections(file,
2246
+ // pattern section, last possible position for it
2247
+ bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
2248
+ // take the first body segment, and walk from fileIndex to its "after"
2249
+ // value at the end
2250
+ // If it doesn't match at that position, we increment, until we hit
2251
+ // that final possible position, and give up.
2252
+ // If it does match, then advance and try to rest.
2253
+ // If any of them fail we keep walking forward.
2254
+ // this is still a bit recursively painful, but it's more constrained
2255
+ // than previous implementations, because we never test something that
2256
+ // can't possibly be a valid matching condition.
2257
+ const bs = bodySegments[bodyIndex];
2258
+ if (!bs) {
2259
+ // just make sure that there's no bad dots
2260
+ for (let i = fileIndex; i < file.length; i++) {
2261
+ sawTail = true;
2262
+ const f = file[i];
2263
+ if (f === '.' ||
2264
+ f === '..' ||
2265
+ (!this.options.dot && f.startsWith('.'))) {
2266
+ return false;
2267
+ }
2268
+ }
2269
+ return sawTail;
2270
+ }
2271
+ // have a non-globstar body section to test
2272
+ const [body, after] = bs;
2273
+ while (fileIndex <= after) {
2274
+ const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
2275
+ // if limit exceeded, no match. intentional false negative,
2276
+ // acceptable break in correctness for security.
2277
+ if (m && globStarDepth < this.maxGlobstarRecursion) {
2278
+ // match! see if the rest match. if so, we're done!
2279
+ const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
2280
+ if (sub !== false) {
2281
+ return sub;
2282
+ }
2283
+ }
2284
+ const f = file[fileIndex];
2285
+ if (f === '.' ||
2286
+ f === '..' ||
2287
+ (!this.options.dot && f.startsWith('.'))) {
2288
+ return false;
2289
+ }
2290
+ fileIndex++;
2291
+ }
2292
+ // walked off. no point continuing
2293
+ return partial || null;
2294
+ }
2295
+ #matchOne(file, pattern, partial, fileIndex, patternIndex) {
2296
+ let fi;
2297
+ let pi;
2298
+ let pl;
2299
+ let fl;
2300
+ for (fi = fileIndex,
2301
+ pi = patternIndex,
2302
+ fl = file.length,
2303
+ pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
1844
2304
  this.debug('matchOne loop');
1845
- var p = pattern[pi];
1846
- var f = file[fi];
2305
+ let p = pattern[pi];
2306
+ let f = file[fi];
1847
2307
  this.debug(pattern, p, f);
1848
2308
  // should be impossible.
1849
2309
  // some invalid regexp stuff in the set.
1850
2310
  /* c8 ignore start */
1851
- if (p === false) {
2311
+ if (p === false || p === exports.GLOBSTAR) {
1852
2312
  return false;
1853
2313
  }
1854
2314
  /* c8 ignore stop */
1855
- if (p === exports.GLOBSTAR) {
1856
- this.debug('GLOBSTAR', [pattern, p, f]);
1857
- // "**"
1858
- // a/**/b/**/c would match the following:
1859
- // a/b/x/y/z/c
1860
- // a/x/y/z/b/c
1861
- // a/b/x/b/x/c
1862
- // a/b/c
1863
- // To do this, take the rest of the pattern after
1864
- // the **, and see if it would match the file remainder.
1865
- // If so, return success.
1866
- // If not, the ** "swallows" a segment, and try again.
1867
- // This is recursively awful.
1868
- //
1869
- // a/**/b/**/c matching a/b/x/y/z/c
1870
- // - a matches a
1871
- // - doublestar
1872
- // - matchOne(b/x/y/z/c, b/**/c)
1873
- // - b matches b
1874
- // - doublestar
1875
- // - matchOne(x/y/z/c, c) -> no
1876
- // - matchOne(y/z/c, c) -> no
1877
- // - matchOne(z/c, c) -> no
1878
- // - matchOne(c, c) yes, hit
1879
- var fr = fi;
1880
- var pr = pi + 1;
1881
- if (pr === pl) {
1882
- this.debug('** at the end');
1883
- // a ** at the end will just swallow the rest.
1884
- // We have found a match.
1885
- // however, it will not swallow /.x, unless
1886
- // options.dot is set.
1887
- // . and .. are *never* matched by **, for explosively
1888
- // exponential reasons.
1889
- for (; fi < fl; fi++) {
1890
- if (file[fi] === '.' ||
1891
- file[fi] === '..' ||
1892
- (!options.dot && file[fi].charAt(0) === '.'))
1893
- return false;
1894
- }
1895
- return true;
1896
- }
1897
- // ok, let's see if we can swallow whatever we can.
1898
- while (fr < fl) {
1899
- var swallowee = file[fr];
1900
- this.debug('\nglobstar while', file, fr, pattern, pr, swallowee);
1901
- // XXX remove this slice. Just pass the start index.
1902
- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
1903
- this.debug('globstar found match!', fr, fl, swallowee);
1904
- // found a match.
1905
- return true;
1906
- }
1907
- else {
1908
- // can't swallow "." or ".." ever.
1909
- // can only swallow ".foo" when explicitly asked.
1910
- if (swallowee === '.' ||
1911
- swallowee === '..' ||
1912
- (!options.dot && swallowee.charAt(0) === '.')) {
1913
- this.debug('dot detected!', file, fr, pattern, pr);
1914
- break;
1915
- }
1916
- // ** swallows a segment, and continue.
1917
- this.debug('globstar swallow a segment, and continue');
1918
- fr++;
1919
- }
1920
- }
1921
- // no match was found.
1922
- // However, in partial mode, we can't say this is necessarily over.
1923
- /* c8 ignore start */
1924
- if (partial) {
1925
- // ran out of file
1926
- this.debug('\n>>> no match, partial?', file, fr, pattern, pr);
1927
- if (fr === fl) {
1928
- return true;
1929
- }
1930
- }
1931
- /* c8 ignore stop */
1932
- return false;
1933
- }
1934
2315
  // something other than **
1935
2316
  // non-magic patterns just have to match exactly
1936
2317
  // patterns with magic have been turned into regexps.
@@ -2001,21 +2382,19 @@ _escape.escape = escape;
2001
2382
  fastTest = options.dot ? starTestDot : starTest;
2002
2383
  }
2003
2384
  else if ((m = pattern.match(starDotExtRE))) {
2004
- fastTest = (options.nocase
2005
- ? options.dot
2006
- ? starDotExtTestNocaseDot
2385
+ fastTest = (options.nocase ?
2386
+ options.dot ?
2387
+ starDotExtTestNocaseDot
2007
2388
  : starDotExtTestNocase
2008
- : options.dot
2009
- ? starDotExtTestDot
2389
+ : options.dot ? starDotExtTestDot
2010
2390
  : starDotExtTest)(m[1]);
2011
2391
  }
2012
2392
  else if ((m = pattern.match(qmarksRE))) {
2013
- fastTest = (options.nocase
2014
- ? options.dot
2015
- ? qmarksTestNocaseDot
2393
+ fastTest = (options.nocase ?
2394
+ options.dot ?
2395
+ qmarksTestNocaseDot
2016
2396
  : qmarksTestNocase
2017
- : options.dot
2018
- ? qmarksTestDot
2397
+ : options.dot ? qmarksTestDot
2019
2398
  : qmarksTest)(m);
2020
2399
  }
2021
2400
  else if ((m = pattern.match(starDotStarRE))) {
@@ -2046,10 +2425,8 @@ _escape.escape = escape;
2046
2425
  return this.regexp;
2047
2426
  }
2048
2427
  const options = this.options;
2049
- const twoStar = options.noglobstar
2050
- ? star
2051
- : options.dot
2052
- ? twoStarDot
2428
+ const twoStar = options.noglobstar ? star
2429
+ : options.dot ? twoStarDot
2053
2430
  : twoStarNoDot;
2054
2431
  const flags = new Set(options.nocase ? ['i'] : []);
2055
2432
  // regexpify non-globstar patterns
@@ -2065,11 +2442,9 @@ _escape.escape = escape;
2065
2442
  for (const f of p.flags.split(''))
2066
2443
  flags.add(f);
2067
2444
  }
2068
- return typeof p === 'string'
2069
- ? regExpEscape(p)
2070
- : p === exports.GLOBSTAR
2071
- ? exports.GLOBSTAR
2072
- : p._src;
2445
+ return (typeof p === 'string' ? regExpEscape(p)
2446
+ : p === exports.GLOBSTAR ? exports.GLOBSTAR
2447
+ : p._src);
2073
2448
  });
2074
2449
  pp.forEach((p, i) => {
2075
2450
  const next = pp[i + 1];
@@ -2086,14 +2461,25 @@ _escape.escape = escape;
2086
2461
  }
2087
2462
  }
2088
2463
  else if (next === undefined) {
2089
- pp[i - 1] = prev + '(?:\\/|' + twoStar + ')?';
2464
+ pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + ')?';
2090
2465
  }
2091
2466
  else if (next !== exports.GLOBSTAR) {
2092
2467
  pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
2093
2468
  pp[i + 1] = exports.GLOBSTAR;
2094
2469
  }
2095
2470
  });
2096
- return pp.filter(p => p !== exports.GLOBSTAR).join('/');
2471
+ const filtered = pp.filter(p => p !== exports.GLOBSTAR);
2472
+ // For partial matches, we need to make the pattern match
2473
+ // any prefix of the full path. We do this by generating
2474
+ // alternative patterns that match progressively longer prefixes.
2475
+ if (this.partial && filtered.length >= 1) {
2476
+ const prefixes = [];
2477
+ for (let i = 1; i <= filtered.length; i++) {
2478
+ prefixes.push(filtered.slice(0, i).join('/'));
2479
+ }
2480
+ return '(?:' + prefixes.join('|') + ')';
2481
+ }
2482
+ return filtered.join('/');
2097
2483
  })
2098
2484
  .join('|');
2099
2485
  // need to wrap in parens if we had more than one thing with |,
@@ -2102,6 +2488,10 @@ _escape.escape = escape;
2102
2488
  // must match entire pattern
2103
2489
  // ending in a * or ** will make it less strict.
2104
2490
  re = '^' + open + re + close + '$';
2491
+ // In partial mode, '/' should always match as it's a valid prefix for any pattern
2492
+ if (this.partial) {
2493
+ re = '^(?:\\/|' + open + re.slice(1, -1) + close + ')$';
2494
+ }
2105
2495
  // can match anything, as long as it's not this.
2106
2496
  if (this.negate)
2107
2497
  re = '^(?!' + re + ').+$';