@vue/compiler-sfc 3.5.44 → 3.5.45

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @vue/compiler-sfc v3.5.44
2
+ * @vue/compiler-sfc v3.5.45
3
3
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
4
4
  * @license MIT
5
5
  **/
@@ -4542,13 +4542,11 @@ function requireUnesc$1 () {
4542
4542
  function gobbleHex(str) {
4543
4543
  var lower = str.toLowerCase();
4544
4544
  var hex = "";
4545
- var spaceTerminated = false;
4546
4545
  for (var i = 0; i < 6 && lower[i] !== undefined; i++) {
4547
4546
  var code = lower.charCodeAt(i);
4548
4547
  // check to see if we are dealing with a valid hex char [a-f|0-9]
4549
4548
  var valid = (code >= 97 && code <= 102) || (code >= 48 && code <= 57);
4550
4549
  // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
4551
- spaceTerminated = code === 32;
4552
4550
  if (!valid) {
4553
4551
  break;
4554
4552
  }
@@ -4557,15 +4555,21 @@ function requireUnesc$1 () {
4557
4555
  if (hex.length === 0) {
4558
4556
  return undefined;
4559
4557
  }
4558
+ var consumed = hex.length;
4559
+ var next = str[consumed];
4560
+ if (next === " " || next === "\t" || next === "\n" || next === "\r" || next === "\f") {
4561
+ // CSS preprocessing treats CRLF as a single newline.
4562
+ consumed += next === "\r" && str[consumed + 1] === "\n" ? 2 : 1;
4563
+ }
4560
4564
  var codePoint = parseInt(hex, 16);
4561
4565
  var isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
4562
4566
  // Add special case for
4563
4567
  // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
4564
4568
  // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
4565
4569
  if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
4566
- return ["\uFFFD", hex.length + (spaceTerminated ? 1 : 0)];
4570
+ return ["\uFFFD", consumed];
4567
4571
  }
4568
- return [String.fromCodePoint(codePoint), hex.length + (spaceTerminated ? 1 : 0)];
4572
+ return [String.fromCodePoint(codePoint), consumed];
4569
4573
  }
4570
4574
  var CONTAINS_ESCAPE = /\\/;
4571
4575
  function unesc(str) {
@@ -4576,7 +4580,7 @@ function requireUnesc$1 () {
4576
4580
  var ret = "";
4577
4581
  for (var i = 0; i < str.length; i++) {
4578
4582
  if (str[i] === "\\") {
4579
- var gobbled = gobbleHex(str.slice(i + 1, i + 7));
4583
+ var gobbled = gobbleHex(str.slice(i + 1, i + 9));
4580
4584
  if (gobbled !== undefined) {
4581
4585
  ret += gobbled[0];
4582
4586
  i += gobbled[1];
@@ -4953,29 +4957,29 @@ function requireNode$2 () {
4953
4957
  return node$2;
4954
4958
  }
4955
4959
 
4956
- var types$2 = {};
4960
+ var types$1 = {};
4957
4961
 
4958
4962
  var hasRequiredTypes$1;
4959
4963
 
4960
4964
  function requireTypes$1 () {
4961
- if (hasRequiredTypes$1) return types$2;
4965
+ if (hasRequiredTypes$1) return types$1;
4962
4966
  hasRequiredTypes$1 = 1;
4963
- Object.defineProperty(types$2, "__esModule", { value: true });
4964
- types$2.UNIVERSAL = types$2.ATTRIBUTE = types$2.CLASS = types$2.COMBINATOR = types$2.COMMENT = types$2.ID = types$2.NESTING = types$2.PSEUDO = types$2.ROOT = types$2.SELECTOR = types$2.STRING = types$2.TAG = void 0;
4965
- types$2.TAG = "tag";
4966
- types$2.STRING = "string";
4967
- types$2.SELECTOR = "selector";
4968
- types$2.ROOT = "root";
4969
- types$2.PSEUDO = "pseudo";
4970
- types$2.NESTING = "nesting";
4971
- types$2.ID = "id";
4972
- types$2.COMMENT = "comment";
4973
- types$2.COMBINATOR = "combinator";
4974
- types$2.CLASS = "class";
4975
- types$2.ATTRIBUTE = "attribute";
4976
- types$2.UNIVERSAL = "universal";
4967
+ Object.defineProperty(types$1, "__esModule", { value: true });
4968
+ types$1.UNIVERSAL = types$1.ATTRIBUTE = types$1.CLASS = types$1.COMBINATOR = types$1.COMMENT = types$1.ID = types$1.NESTING = types$1.PSEUDO = types$1.ROOT = types$1.SELECTOR = types$1.STRING = types$1.TAG = void 0;
4969
+ types$1.TAG = "tag";
4970
+ types$1.STRING = "string";
4971
+ types$1.SELECTOR = "selector";
4972
+ types$1.ROOT = "root";
4973
+ types$1.PSEUDO = "pseudo";
4974
+ types$1.NESTING = "nesting";
4975
+ types$1.ID = "id";
4976
+ types$1.COMMENT = "comment";
4977
+ types$1.COMBINATOR = "combinator";
4978
+ types$1.CLASS = "class";
4979
+ types$1.ATTRIBUTE = "attribute";
4980
+ types$1.UNIVERSAL = "universal";
4977
4981
 
4978
- return types$2;
4982
+ return types$1;
4979
4983
  }
4980
4984
 
4981
4985
  var hasRequiredContainer$1;
@@ -5166,6 +5170,7 @@ function requireContainer$1 () {
5166
5170
  var oldIndex = this.index(oldNode);
5167
5171
  var resetNode = [];
5168
5172
  for (var i = 2; i < arguments.length; i++) {
5173
+ arguments[i].parent = this;
5169
5174
  resetNode.push(arguments[i]);
5170
5175
  }
5171
5176
  (_a = this.nodes).splice.apply(_a, __spreadArray([oldIndex + 1, 0, newNode], __read(resetNode), false));
@@ -5185,6 +5190,7 @@ function requireContainer$1 () {
5185
5190
  var oldIndex = this.index(oldNode);
5186
5191
  var resetNode = [];
5187
5192
  for (var i = 2; i < arguments.length; i++) {
5193
+ arguments[i].parent = this;
5188
5194
  resetNode.push(arguments[i]);
5189
5195
  }
5190
5196
  (_a = this.nodes).splice.apply(_a, __spreadArray([oldIndex, 0, newNode], __read(resetNode), false));
@@ -6578,8 +6584,22 @@ function requireCombinator$1 () {
6578
6584
  function Combinator(opts) {
6579
6585
  var _this = _super.call(this, opts) || this;
6580
6586
  _this.type = types_1.COMBINATOR;
6587
+ _this._constructed = true;
6581
6588
  return _this;
6582
6589
  }
6590
+ Object.defineProperty(Combinator.prototype, "value", {
6591
+ get: function () {
6592
+ return this._value;
6593
+ },
6594
+ set: function (value) {
6595
+ if (this._constructed && this.raws) {
6596
+ delete this.raws.value;
6597
+ }
6598
+ this._value = value;
6599
+ },
6600
+ enumerable: false,
6601
+ configurable: true
6602
+ });
6583
6603
  return Combinator;
6584
6604
  }(node_1.default));
6585
6605
  combinator$1.default = Combinator;
@@ -6646,7 +6666,7 @@ function requireSortAscending$1 () {
6646
6666
  return sortAscending$1;
6647
6667
  }
6648
6668
 
6649
- var tokenize$1 = {};
6669
+ var tokenize$2 = {};
6650
6670
 
6651
6671
  var tokenTypes$1 = {};
6652
6672
 
@@ -6699,9 +6719,9 @@ function requireTokenTypes$1 () {
6699
6719
  var hasRequiredTokenize$1;
6700
6720
 
6701
6721
  function requireTokenize$1 () {
6702
- if (hasRequiredTokenize$1) return tokenize$1;
6722
+ if (hasRequiredTokenize$1) return tokenize$2;
6703
6723
  hasRequiredTokenize$1 = 1;
6704
- var __createBinding = (tokenize$1 && tokenize$1.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6724
+ var __createBinding = (tokenize$2 && tokenize$2.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6705
6725
  if (k2 === undefined) k2 = k;
6706
6726
  var desc = Object.getOwnPropertyDescriptor(m, k);
6707
6727
  if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
@@ -6712,12 +6732,12 @@ function requireTokenize$1 () {
6712
6732
  if (k2 === undefined) k2 = k;
6713
6733
  o[k2] = m[k];
6714
6734
  }));
6715
- var __setModuleDefault = (tokenize$1 && tokenize$1.__setModuleDefault) || (Object.create ? (function(o, v) {
6735
+ var __setModuleDefault = (tokenize$2 && tokenize$2.__setModuleDefault) || (Object.create ? (function(o, v) {
6716
6736
  Object.defineProperty(o, "default", { enumerable: true, value: v });
6717
6737
  }) : function(o, v) {
6718
6738
  o["default"] = v;
6719
6739
  });
6720
- var __importStar = (tokenize$1 && tokenize$1.__importStar) || (function () {
6740
+ var __importStar = (tokenize$2 && tokenize$2.__importStar) || (function () {
6721
6741
  var ownKeys = function(o) {
6722
6742
  ownKeys = Object.getOwnPropertyNames || function (o) {
6723
6743
  var ar = [];
@@ -6735,9 +6755,9 @@ function requireTokenize$1 () {
6735
6755
  };
6736
6756
  })();
6737
6757
  var _a, _b;
6738
- Object.defineProperty(tokenize$1, "__esModule", { value: true });
6739
- tokenize$1.FIELDS = void 0;
6740
- tokenize$1.default = tokenize;
6758
+ Object.defineProperty(tokenize$2, "__esModule", { value: true });
6759
+ tokenize$2.FIELDS = void 0;
6760
+ tokenize$2.default = tokenize;
6741
6761
  var t = __importStar(/*@__PURE__*/ requireTokenTypes$1());
6742
6762
  var unescapable = (_a = {},
6743
6763
  _a[t.tab] = true,
@@ -6828,7 +6848,7 @@ function requireTokenize$1 () {
6828
6848
  }
6829
6849
  return next;
6830
6850
  }
6831
- tokenize$1.FIELDS = {
6851
+ tokenize$2.FIELDS = {
6832
6852
  TYPE: 0,
6833
6853
  START_LINE: 1,
6834
6854
  START_COL: 2,
@@ -6999,7 +7019,7 @@ function requireTokenize$1 () {
6999
7019
  return tokens;
7000
7020
  }
7001
7021
 
7002
- return tokenize$1;
7022
+ return tokenize$2;
7003
7023
  }
7004
7024
 
7005
7025
  var hasRequiredParser$2;
@@ -7200,6 +7220,23 @@ function requireParser$2 () {
7200
7220
  return _this.rule.error(message, errorOptions);
7201
7221
  };
7202
7222
  };
7223
+ Parser.prototype.namespaceSeparatorFollows = function (attr, pos) {
7224
+ var next = pos;
7225
+ while (attr[next] && attr[next][tokenize_1.FIELDS.TYPE] === tokens.space) {
7226
+ next++;
7227
+ }
7228
+ if (!attr[next] || this.content(attr[next]) !== "|") {
7229
+ return false;
7230
+ }
7231
+ next++;
7232
+ if (attr[next] && attr[next][tokenize_1.FIELDS.TYPE] === tokens.equals) {
7233
+ return false;
7234
+ }
7235
+ while (attr[next] && attr[next][tokenize_1.FIELDS.TYPE] === tokens.space) {
7236
+ next++;
7237
+ }
7238
+ return Boolean(attr[next]);
7239
+ };
7203
7240
  Parser.prototype.attribute = function () {
7204
7241
  var attr = [];
7205
7242
  var startingToken = this.currToken;
@@ -7230,6 +7267,7 @@ function requireParser$2 () {
7230
7267
  var commentBefore = "";
7231
7268
  var lastAdded = null;
7232
7269
  var spaceAfterMeaningfulToken = false;
7270
+ var namespaceSpaces = null;
7233
7271
  while (pos < len) {
7234
7272
  var token = attr[pos];
7235
7273
  var content = this.content(token);
@@ -7246,6 +7284,15 @@ function requireParser$2 () {
7246
7284
  if (this.options.lossy) {
7247
7285
  break;
7248
7286
  }
7287
+ if (namespaceSpaces !== null) {
7288
+ namespaceSpaces += content;
7289
+ break;
7290
+ }
7291
+ if (lastAdded === "namespace") {
7292
+ (0, util_1.ensureObject)(node, "raws");
7293
+ node.raws.namespace = (node.raws.namespace || node.namespace) + content;
7294
+ break;
7295
+ }
7249
7296
  if (lastAdded) {
7250
7297
  (0, util_1.ensureObject)(node, "spaces", lastAdded);
7251
7298
  var prevContent = node.spaces[lastAdded].after || "";
@@ -7267,6 +7314,11 @@ function requireParser$2 () {
7267
7314
  }
7268
7315
  else if ((!node.namespace || (lastAdded === "namespace" && !spaceAfterMeaningfulToken)) &&
7269
7316
  next) {
7317
+ node.namespace = (node.namespace || "") + content;
7318
+ var rawValue = (0, util_1.getProp)(node, "raws", "namespace") || null;
7319
+ if (rawValue) {
7320
+ node.raws.namespace += content;
7321
+ }
7270
7322
  if (spaceBefore) {
7271
7323
  (0, util_1.ensureObject)(node, "spaces", "attribute");
7272
7324
  node.spaces.attribute.before = spaceBefore;
@@ -7277,11 +7329,6 @@ function requireParser$2 () {
7277
7329
  node.raws.spaces.attribute.before = commentBefore;
7278
7330
  commentBefore = "";
7279
7331
  }
7280
- node.namespace = (node.namespace || "") + content;
7281
- var rawValue = (0, util_1.getProp)(node, "raws", "namespace") || null;
7282
- if (rawValue) {
7283
- node.raws.namespace += content;
7284
- }
7285
7332
  lastAdded = "namespace";
7286
7333
  }
7287
7334
  spaceAfterMeaningfulToken = false;
@@ -7316,18 +7363,16 @@ function requireParser$2 () {
7316
7363
  node.operator = content;
7317
7364
  lastAdded = "operator";
7318
7365
  }
7319
- else if (!node.namespace && !node.attribute) {
7320
- node.namespace = true;
7366
+ else if (!node.operator) {
7367
+ if (!node.namespace && !node.attribute) {
7368
+ node.namespace = true;
7369
+ }
7370
+ namespaceSpaces = "";
7321
7371
  }
7322
7372
  spaceAfterMeaningfulToken = false;
7323
7373
  break;
7324
7374
  case tokens.word:
7325
- if (next &&
7326
- this.content(next) === "|" &&
7327
- attr[pos + 2] &&
7328
- attr[pos + 2][tokenize_1.FIELDS.TYPE] !== tokens.equals && // this look-ahead probably fails with comment nodes involved.
7329
- !node.operator &&
7330
- !node.namespace) {
7375
+ if (this.namespaceSeparatorFollows(attr, pos + 1) && !node.operator && !node.namespace) {
7331
7376
  node.namespace = content;
7332
7377
  lastAdded = "namespace";
7333
7378
  }
@@ -7342,6 +7387,11 @@ function requireParser$2 () {
7342
7387
  node.raws.spaces.attribute.before = commentBefore;
7343
7388
  commentBefore = "";
7344
7389
  }
7390
+ if (namespaceSpaces) {
7391
+ (0, util_1.ensureObject)(node, "raws");
7392
+ node.raws.attribute = namespaceSpaces + (node.raws.attribute || node.attribute || "");
7393
+ }
7394
+ namespaceSpaces = null;
7345
7395
  node.attribute = (node.attribute || "") + content;
7346
7396
  var rawValue = (0, util_1.getProp)(node, "raws", "attribute") || null;
7347
7397
  if (rawValue) {
@@ -7651,7 +7701,34 @@ function requireParser$2 () {
7651
7701
  }
7652
7702
  return this.newNode(node);
7653
7703
  };
7704
+ // Whitespace is only ever stored on a node, as `spaces.before` or
7705
+ // `spaces.after`. `space()` parks leading whitespace on `this.spaces` for the
7706
+ // next node to claim, so a selector that ends before any node is created --
7707
+ // `:not(a, )`, `:not( )`, a trailing `a, ` -- drops it. Park it on an empty
7708
+ // string node instead, which is what `parseWhitespaceEquivalentTokens`
7709
+ // already does for the comment-adjacent case.
7710
+ Parser.prototype.flushPendingSpaces = function () {
7711
+ if (!this.spaces) {
7712
+ return;
7713
+ }
7714
+ var last = this.current.last;
7715
+ if (last) {
7716
+ // `space()` routes trailing whitespace to `this.spaces` whenever the
7717
+ // selector so far holds nothing but comments, so `:not( /*c*/ )` lands
7718
+ // here rather than on the `spaces.after` path.
7719
+ last.spaces.after += this.spaces;
7720
+ this.spaces = "";
7721
+ return;
7722
+ }
7723
+ var token = this.currToken || this.prevToken;
7724
+ this.newNode(new string_1.default({
7725
+ value: "",
7726
+ source: token ? getTokenSource(token) : undefined,
7727
+ sourceIndex: token ? token[tokenize_1.FIELDS.START_POS] : 0,
7728
+ }));
7729
+ };
7654
7730
  Parser.prototype.comma = function () {
7731
+ this.flushPendingSpaces();
7655
7732
  if (this.position === this.tokens.length - 1) {
7656
7733
  this.root.trailingComma = true;
7657
7734
  this.position++;
@@ -7772,6 +7849,7 @@ function requireParser$2 () {
7772
7849
  this.parse();
7773
7850
  }
7774
7851
  else {
7852
+ this.flushPendingSpaces();
7775
7853
  this.current.source.end = tokenEnd(this.currToken);
7776
7854
  this.current.parent.source.end = tokenEnd(this.currToken);
7777
7855
  this.position++;
@@ -7981,6 +8059,7 @@ function requireParser$2 () {
7981
8059
  while (this.position < this.tokens.length) {
7982
8060
  this.parse(true);
7983
8061
  }
8062
+ this.flushPendingSpaces();
7984
8063
  this.current._inferEndPosition();
7985
8064
  return this.root;
7986
8065
  };
@@ -12232,7 +12311,7 @@ const processors = {
12232
12311
  stylus: styl
12233
12312
  };
12234
12313
 
12235
- var build = {exports: {}};
12314
+ var build$1 = {exports: {}};
12236
12315
 
12237
12316
  var fs = {};
12238
12317
 
@@ -14965,41 +15044,41 @@ function requireNode () {
14965
15044
  return node.exports;
14966
15045
  }
14967
15046
 
14968
- var types$1 = {};
15047
+ var types = {};
14969
15048
 
14970
15049
  var hasRequiredTypes;
14971
15050
 
14972
15051
  function requireTypes () {
14973
- if (hasRequiredTypes) return types$1;
15052
+ if (hasRequiredTypes) return types;
14974
15053
  hasRequiredTypes = 1;
14975
15054
 
14976
- types$1.__esModule = true;
14977
- types$1.UNIVERSAL = types$1.TAG = types$1.STRING = types$1.SELECTOR = types$1.ROOT = types$1.PSEUDO = types$1.NESTING = types$1.ID = types$1.COMMENT = types$1.COMBINATOR = types$1.CLASS = types$1.ATTRIBUTE = void 0;
15055
+ types.__esModule = true;
15056
+ types.UNIVERSAL = types.TAG = types.STRING = types.SELECTOR = types.ROOT = types.PSEUDO = types.NESTING = types.ID = types.COMMENT = types.COMBINATOR = types.CLASS = types.ATTRIBUTE = void 0;
14978
15057
  var TAG = 'tag';
14979
- types$1.TAG = TAG;
15058
+ types.TAG = TAG;
14980
15059
  var STRING = 'string';
14981
- types$1.STRING = STRING;
15060
+ types.STRING = STRING;
14982
15061
  var SELECTOR = 'selector';
14983
- types$1.SELECTOR = SELECTOR;
15062
+ types.SELECTOR = SELECTOR;
14984
15063
  var ROOT = 'root';
14985
- types$1.ROOT = ROOT;
15064
+ types.ROOT = ROOT;
14986
15065
  var PSEUDO = 'pseudo';
14987
- types$1.PSEUDO = PSEUDO;
15066
+ types.PSEUDO = PSEUDO;
14988
15067
  var NESTING = 'nesting';
14989
- types$1.NESTING = NESTING;
15068
+ types.NESTING = NESTING;
14990
15069
  var ID = 'id';
14991
- types$1.ID = ID;
15070
+ types.ID = ID;
14992
15071
  var COMMENT = 'comment';
14993
- types$1.COMMENT = COMMENT;
15072
+ types.COMMENT = COMMENT;
14994
15073
  var COMBINATOR = 'combinator';
14995
- types$1.COMBINATOR = COMBINATOR;
15074
+ types.COMBINATOR = COMBINATOR;
14996
15075
  var CLASS = 'class';
14997
- types$1.CLASS = CLASS;
15076
+ types.CLASS = CLASS;
14998
15077
  var ATTRIBUTE = 'attribute';
14999
- types$1.ATTRIBUTE = ATTRIBUTE;
15078
+ types.ATTRIBUTE = ATTRIBUTE;
15000
15079
  var UNIVERSAL = 'universal';
15001
- types$1.UNIVERSAL = UNIVERSAL;
15002
- return types$1;
15080
+ types.UNIVERSAL = UNIVERSAL;
15081
+ return types;
15003
15082
  }
15004
15083
 
15005
15084
  var hasRequiredContainer;
@@ -16302,7 +16381,7 @@ function requireSortAscending () {
16302
16381
  return sortAscending.exports;
16303
16382
  }
16304
16383
 
16305
- var tokenize = {};
16384
+ var tokenize$1 = {};
16306
16385
 
16307
16386
  var tokenTypes = {};
16308
16387
 
@@ -16386,7 +16465,7 @@ function requireTokenTypes () {
16386
16465
  var hasRequiredTokenize;
16387
16466
 
16388
16467
  function requireTokenize () {
16389
- if (hasRequiredTokenize) return tokenize;
16468
+ if (hasRequiredTokenize) return tokenize$1;
16390
16469
  hasRequiredTokenize = 1;
16391
16470
  (function (exports) {
16392
16471
 
@@ -16625,8 +16704,8 @@ function requireTokenize () {
16625
16704
  }
16626
16705
  return tokens;
16627
16706
  }
16628
- } (tokenize));
16629
- return tokenize;
16707
+ } (tokenize$1));
16708
+ return tokenize$1;
16630
16709
  }
16631
16710
 
16632
16711
  var hasRequiredParser;
@@ -20012,7 +20091,7 @@ function requirePluginFactory () {
20012
20091
  var hasRequiredBuild;
20013
20092
 
20014
20093
  function requireBuild () {
20015
- if (hasRequiredBuild) return build.exports;
20094
+ if (hasRequiredBuild) return build$1.exports;
20016
20095
  hasRequiredBuild = 1;
20017
20096
 
20018
20097
  var _fs = require$$0;
@@ -20026,10 +20105,10 @@ function requireBuild () {
20026
20105
  writeFile: _fs.writeFile
20027
20106
  });
20028
20107
 
20029
- build.exports = (opts = {}) => (0, _pluginFactory.makePlugin)(opts);
20108
+ build$1.exports = (opts = {}) => (0, _pluginFactory.makePlugin)(opts);
20030
20109
 
20031
- build.exports.postcss = true;
20032
- return build.exports;
20110
+ build$1.exports.postcss = true;
20111
+ return build$1.exports;
20033
20112
  }
20034
20113
 
20035
20114
  var buildExports = /*@__PURE__*/ requireBuild();
@@ -20209,10 +20288,10 @@ function toFileNameLowerCase(x) {
20209
20288
  function createGetCanonicalFileName(useCaseSensitiveFileNames) {
20210
20289
  return useCaseSensitiveFileNames ? identity : toFileNameLowerCase;
20211
20290
  }
20212
- const normalize = (path$1.posix || path$1).normalize;
20291
+ const normalize$1 = (path$1.posix || path$1).normalize;
20213
20292
  const windowsSlashRE = /\\/g;
20214
20293
  function normalizePath(p) {
20215
- return normalize(p.replace(windowsSlashRE, "/"));
20294
+ return normalize$1(p.replace(windowsSlashRE, "/"));
20216
20295
  }
20217
20296
  const joinPaths = (path$1.posix || path$1).join;
20218
20297
  const propNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~\-]/;
@@ -20291,7 +20370,7 @@ function getObjectOrArrayExpressionKeys(value) {
20291
20370
  return [];
20292
20371
  }
20293
20372
 
20294
- var _a$1, _b;
20373
+ var _a, _b;
20295
20374
  class ScriptCompileContext {
20296
20375
  constructor(descriptor, options) {
20297
20376
  this.descriptor = descriptor;
@@ -20300,7 +20379,7 @@ class ScriptCompileContext {
20300
20379
  this.source = this.descriptor.source;
20301
20380
  this.filename = this.descriptor.filename;
20302
20381
  this.s = new MagicString(this.source);
20303
- this.startOffset = (_a$1 = this.descriptor.scriptSetup) == null ? void 0 : _a$1.loc.start.offset;
20382
+ this.startOffset = (_a = this.descriptor.scriptSetup) == null ? void 0 : _a.loc.start.offset;
20304
20383
  this.endOffset = (_b = this.descriptor.scriptSetup) == null ? void 0 : _b.loc.end.offset;
20305
20384
  this.userImports = /* @__PURE__ */ Object.create(null);
20306
20385
  // macros presence check
@@ -20620,6 +20699,24 @@ const EXPANSION_MAX = 100_000;
20620
20699
  // realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
20621
20700
  // characters) so legitimate input is unaffected.
20622
20701
  const EXPANSION_MAX_LENGTH = 4_000_000;
20702
+ // `expand_` recurses once per level of brace *nesting* - both when expanding a
20703
+ // set's comma members and when re-wrapping a set whose body is a single part.
20704
+ // The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
20705
+ // level per chained group), which left nesting depth unbounded: about 3,100
20706
+ // levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
20707
+ // and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
20708
+ // will follow nesting. It sits far above any realistic pattern and well below
20709
+ // the depth at which the stack runs out.
20710
+ const EXPANSION_MAX_DEPTH = 1_000;
20711
+ // Bash keeps a quirk where a brace group followed by a comma set still expands
20712
+ // (`{a},b}`). The parser implements it by rewriting the string and restarting
20713
+ // the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
20714
+ // full passes over a string that itself grows by one `escClose` sentinel each
20715
+ // time - quadratic in `n`, with a ~26x constant from the sentinel's length.
20716
+ // 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
20717
+ // seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
20718
+ // times the scan may restart. Real `{a},b}` input needs a handful.
20719
+ const EXPANSION_MAX_REWRITES = 1_000;
20623
20720
  function numeric(str) {
20624
20721
  return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
20625
20722
  }
@@ -20639,36 +20736,52 @@ function unescapeBraces(str) {
20639
20736
  .replace(escCommaPattern, ',')
20640
20737
  .replace(escPeriodPattern, '.');
20641
20738
  }
20739
+ // Like `target.push(...items)` but doesn't overflow the stack
20740
+ function pushAll(target, items) {
20741
+ for (let i = 0; i < items.length; i++) {
20742
+ target.push(items[i]);
20743
+ }
20744
+ }
20642
20745
  /**
20643
20746
  * Basically just str.split(","), but handling cases
20644
20747
  * where we have nested braced sections, which should be
20645
20748
  * treated as individual members, like {a,{b,c},d}
20646
20749
  */
20647
20750
  function parseCommaParts(str) {
20648
- if (!str) {
20649
- return [''];
20650
- }
20651
20751
  const parts = [];
20652
- const m = balanced('{', '}', str);
20653
- if (!m) {
20654
- return str.split(',');
20655
- }
20656
- const { pre, body, post } = m;
20657
- const p = pre.split(',');
20658
- p[p.length - 1] += '{' + body + '}';
20659
- const postParts = parseCommaParts(post);
20660
- if (post.length) {
20661
- p[p.length - 1] += postParts.shift();
20662
- p.push.apply(p, postParts);
20752
+ // Walk the brace groups iteratively. Recursing on `post` once per group let a
20753
+ // chain of them exhaust the stack - the parsing-side counterpart to
20754
+ // the `expand_` overflow fixed for CVE-2026-14257, and not something `max` or
20755
+ // `maxLength` can bound, since it happens before expansion.
20756
+ //
20757
+ // The part the next chunk continues
20758
+ let carry = '';
20759
+ for (;;) {
20760
+ const m = balanced('{', '}', str);
20761
+ if (!m) {
20762
+ const tail = str.split(',');
20763
+ tail[0] = carry + tail[0];
20764
+ pushAll(parts, tail);
20765
+ return parts;
20766
+ }
20767
+ const { pre, body, post } = m;
20768
+ const p = pre.split(',');
20769
+ p[0] = carry + p[0];
20770
+ p[p.length - 1] += '{' + body + '}';
20771
+ if (!post.length) {
20772
+ pushAll(parts, p);
20773
+ return parts;
20774
+ }
20775
+ carry = p.pop();
20776
+ pushAll(parts, p);
20777
+ str = post;
20663
20778
  }
20664
- parts.push.apply(parts, p);
20665
- return parts;
20666
20779
  }
20667
20780
  function expand(str, options = {}) {
20668
20781
  if (!str) {
20669
20782
  return [];
20670
20783
  }
20671
- const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
20784
+ const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH, maxDepth = EXPANSION_MAX_DEPTH, maxRewrites = EXPANSION_MAX_REWRITES, } = options;
20672
20785
  // I don't know why Bash 4.3 does this, but it does.
20673
20786
  // Anything starting with {} will have the first two bytes preserved
20674
20787
  // but *only* at the top level, so {},a}b will not expand to anything,
@@ -20678,7 +20791,7 @@ function expand(str, options = {}) {
20678
20791
  if (str.slice(0, 2) === '{}') {
20679
20792
  str = '\\{\\}' + str.slice(2);
20680
20793
  }
20681
- return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
20794
+ return expand_(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
20682
20795
  }
20683
20796
  function embrace(str) {
20684
20797
  return '{' + str + '}';
@@ -20773,7 +20886,13 @@ function expandSequence(body, isAlphaSequence, max, maxLength) {
20773
20886
  }
20774
20887
  return N;
20775
20888
  }
20776
- function expand_(str, max, maxLength, isTop) {
20889
+ function expand_(str, max, maxLength, maxDepth, depth, maxRewrites, isTop) {
20890
+ // Too deeply nested to keep following: treat the rest as literal, the same
20891
+ // way a group that cannot expand is already handled. Truncating rather than
20892
+ // throwing keeps `expand` total, matching `max` and `maxLength`.
20893
+ if (depth > maxDepth) {
20894
+ return [str];
20895
+ }
20777
20896
  // Consume the string's top-level brace groups left to right, threading a
20778
20897
  // running set of combined prefixes (`acc`). Expanding the tail iteratively -
20779
20898
  // rather than recursing on `m.post` once per group - keeps the native stack
@@ -20785,6 +20904,9 @@ function expand_(str, max, maxLength, isTop) {
20785
20904
  // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
20786
20905
  // is on the final strings, so it is applied to whichever `combine` produces
20787
20906
  // them (the one with no brace set left in the tail).
20907
+ // How many times the `{a},b}` rewrite below has restarted the scan. Each pass
20908
+ // re-reads the whole string, so leaving this unbounded is quadratic.
20909
+ let rewrites = 0;
20788
20910
  let dropEmpties = false;
20789
20911
  let firstGroup = true;
20790
20912
  for (;;) {
@@ -20809,7 +20931,8 @@ function expand_(str, max, maxLength, isTop) {
20809
20931
  const isOptions = m.body.indexOf(',') >= 0;
20810
20932
  if (!isSequence && !isOptions) {
20811
20933
  // {a},b}
20812
- if (m.post.match(/,(?!,).*\}/)) {
20934
+ if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
20935
+ rewrites++;
20813
20936
  str = m.pre + '{' + m.body + escClose + m.post;
20814
20937
  isTop = true;
20815
20938
  continue;
@@ -20829,7 +20952,7 @@ function expand_(str, max, maxLength, isTop) {
20829
20952
  let n = parseCommaParts(m.body);
20830
20953
  if (n.length === 1 && n[0] !== undefined) {
20831
20954
  // x{{a,b}}y ==> x{a}y x{b}y
20832
- n = expand_(n[0], max, maxLength, false).map(embrace);
20955
+ n = expand_(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
20833
20956
  //XXX is this necessary? Can't seem to hit it in tests.
20834
20957
  /* c8 ignore start */
20835
20958
  if (n.length === 1) {
@@ -20855,12 +20978,13 @@ function expand_(str, max, maxLength, isTop) {
20855
20978
  values = [];
20856
20979
  let valuesLength = 0;
20857
20980
  outer: for (let j = 0; j < n.length; j++) {
20858
- const expanded = expand_(n[j], max, maxLength, false);
20981
+ const expanded = expand_(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false);
20859
20982
  for (let k = 0; k < expanded.length; k++) {
20860
20983
  const v = expanded[k];
20861
20984
  if (dropsEmpties && !v)
20862
20985
  continue;
20863
- if (values.length >= max || valuesLength + v.length > maxLength) {
20986
+ if (values.length >= max ||
20987
+ valuesLength + v.length > maxLength) {
20864
20988
  break outer;
20865
20989
  }
20866
20990
  values.push(v);
@@ -20876,49 +21000,42 @@ function expand_(str, max, maxLength, isTop) {
20876
21000
  return acc;
20877
21001
  }
20878
21002
 
20879
- const MAX_PATTERN_LENGTH = 1024 * 64;
20880
- const assertValidPattern = (pattern) => {
20881
- if (typeof pattern !== 'string') {
20882
- throw new TypeError('invalid pattern');
20883
- }
20884
- if (pattern.length > MAX_PATTERN_LENGTH) {
20885
- throw new TypeError('pattern is too long');
20886
- }
20887
- };
20888
-
20889
21003
  // translate the various posix character classes into unicode properties
20890
21004
  // this works across all unicode locales
20891
- // { <posix class>: [<translation>, /u flag required, negated]
21005
+ // { <posix class>: [<translation>, negated]
20892
21006
  const posixClasses = {
20893
- '[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}', true],
20894
- '[:alpha:]': ['\\p{L}\\p{Nl}', true],
20895
- '[:ascii:]': ['\\x' + '00-\\x' + '7f', false],
20896
- '[:blank:]': ['\\p{Zs}\\t', true],
20897
- '[:cntrl:]': ['\\p{Cc}', true],
20898
- '[:digit:]': ['\\p{Nd}', true],
20899
- '[:graph:]': ['\\p{Z}\\p{C}', true, true],
20900
- '[:lower:]': ['\\p{Ll}', true],
21007
+ '[:alnum:]': ['\\p{L}\\p{Nl}\\p{Nd}'],
21008
+ '[:alpha:]': ['\\p{L}\\p{Nl}'],
21009
+ '[:ascii:]': ['\\x' + '00-\\x' + '7f'],
21010
+ '[:blank:]': ['\\p{Zs}\\t'],
21011
+ '[:cntrl:]': ['\\p{Cc}'],
21012
+ '[:digit:]': ['\\p{Nd}'],
21013
+ '[:graph:]': ['\\p{Z}\\p{C}', true],
21014
+ '[:lower:]': ['\\p{Ll}'],
20901
21015
  '[:print:]': ['\\p{C}', true],
20902
- '[:punct:]': ['\\p{P}', true],
20903
- '[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f', true],
20904
- '[:upper:]': ['\\p{Lu}', true],
20905
- '[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}', true],
20906
- '[:xdigit:]': ['A-Fa-f0-9', false],
21016
+ '[:punct:]': ['\\p{P}\\p{S}'],
21017
+ '[:space:]': ['\\p{Z}\\t\\r\\n\\v\\f'],
21018
+ '[:upper:]': ['\\p{Lu}'],
21019
+ '[:word:]': ['\\p{L}\\p{Nl}\\p{Nd}\\p{Pc}'],
21020
+ '[:xdigit:]': ['A-Fa-f0-9'],
20907
21021
  };
20908
21022
  // only need to escape a few things inside of brace expressions
20909
21023
  // escapes: [ \ ] -
20910
21024
  const braceEscape = (s) => s.replace(/[[\]\\-]/g, '\\$&');
20911
- // escape all regexp magic characters
20912
- const regexpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
20913
- // everything has already been escaped, we just have to join
20914
- const rangesToString = (ranges) => ranges.join('');
20915
- // takes a glob string at a posix brace expression, and returns
20916
- // an equivalent regular expression source, and boolean indicating
20917
- // whether the /u flag needs to be applied, and the number of chars
20918
- // consumed to parse the character class.
20919
- // This also removes out of order ranges, and returns ($.) if the
20920
- // entire class just no good.
20921
- const parseClass = (glob, position) => {
21025
+ const noMatch = /$./;
21026
+ // Takes a glob string at a posix brace expression. Returns a RegExp
21027
+ // that tests one character, and the number of glob chars consumed.
21028
+ // If the class is not closed, consumed is 0, and the `[` must be
21029
+ // treated as a literal character.
21030
+ // Out of order ranges are removed. If nothing is left, the RegExp
21031
+ // is `/$./`, and the rest of the glob is consumed, so that the
21032
+ // whole glob cannot match.
21033
+ const parseClass = (glob, position, nocase = false, form) => {
21034
+ // normalize one class member, if it stays one code point
21035
+ const norm = (c) => {
21036
+ const n = form ? c.normalize(form) : c;
21037
+ return [...n].length === 1 ? n : c;
21038
+ };
20922
21039
  const pos = position;
20923
21040
  /* c8 ignore start */
20924
21041
  if (glob.charAt(pos) !== '[') {
@@ -20929,13 +21046,12 @@ const parseClass = (glob, position) => {
20929
21046
  const negs = [];
20930
21047
  let i = pos + 1;
20931
21048
  let sawStart = false;
20932
- let uflag = false;
20933
21049
  let escaping = false;
20934
21050
  let negate = false;
20935
21051
  let endPos = pos;
20936
21052
  let rangeStart = '';
20937
21053
  WHILE: while (i < glob.length) {
20938
- const c = glob.charAt(i);
21054
+ const c = String.fromCodePoint(glob.codePointAt(i));
20939
21055
  if ((c === '!' || c === '^') && i === pos + 1) {
20940
21056
  negate = true;
20941
21057
  i++;
@@ -20956,954 +21072,838 @@ const parseClass = (glob, position) => {
20956
21072
  }
20957
21073
  if (c === '[' && !escaping) {
20958
21074
  // either a posix class, a collation equivalent, or just a [
20959
- for (const [cls, [unip, u, neg]] of Object.entries(posixClasses)) {
21075
+ for (const [cls, [unip, neg]] of Object.entries(posixClasses)) {
20960
21076
  if (glob.startsWith(cls, i)) {
20961
21077
  // invalid, [a-[] is fine, but not [a-[:alpha]]
20962
21078
  if (rangeStart) {
20963
- return ['$.', false, glob.length - pos, true];
21079
+ return [noMatch, glob.length - pos];
20964
21080
  }
20965
21081
  i += cls.length;
20966
21082
  if (neg)
20967
21083
  negs.push(unip);
20968
21084
  else
20969
21085
  ranges.push(unip);
20970
- uflag = uflag || u;
20971
21086
  continue WHILE;
20972
21087
  }
20973
21088
  }
20974
21089
  }
20975
21090
  // now it's just a normal character, effectively
20976
21091
  escaping = false;
21092
+ const n = norm(c);
20977
21093
  if (rangeStart) {
20978
21094
  // throw this range away if it's not valid, but others
20979
21095
  // can still match.
20980
- if (c > rangeStart) {
20981
- ranges.push(braceEscape(rangeStart) + '-' + braceEscape(c));
21096
+ if (n.codePointAt(0) > rangeStart.codePointAt(0)) {
21097
+ ranges.push(braceEscape(rangeStart) + '-' + braceEscape(n));
20982
21098
  }
20983
- else if (c === rangeStart) {
20984
- ranges.push(braceEscape(c));
21099
+ else if (n === rangeStart) {
21100
+ ranges.push(braceEscape(n));
20985
21101
  }
20986
21102
  rangeStart = '';
20987
- i++;
21103
+ i += c.length;
20988
21104
  continue;
20989
21105
  }
20990
21106
  // now might be the start of a range.
20991
21107
  // can be either c-d or c-] or c<more...>] or c] at this point
20992
- if (glob.startsWith('-]', i + 1)) {
20993
- ranges.push(braceEscape(c + '-'));
20994
- i += 2;
21108
+ if (glob.startsWith('-]', i + c.length)) {
21109
+ ranges.push(braceEscape(n + '-'));
21110
+ i += c.length + 1;
20995
21111
  continue;
20996
21112
  }
20997
- if (glob.startsWith('-', i + 1)) {
20998
- rangeStart = c;
20999
- i += 2;
21113
+ if (glob.startsWith('-', i + c.length)) {
21114
+ rangeStart = n;
21115
+ i += c.length + 1;
21000
21116
  continue;
21001
21117
  }
21002
21118
  // not the start of a range, just a single character
21003
- ranges.push(braceEscape(c));
21004
- i++;
21119
+ ranges.push(braceEscape(n));
21120
+ i += c.length;
21005
21121
  }
21006
21122
  if (endPos < i) {
21007
21123
  // didn't see the end of the class, not a valid class,
21008
21124
  // but might still be valid as a literal match.
21009
- return ['', false, 0, false];
21125
+ return [noMatch, 0];
21010
21126
  }
21011
21127
  // if we got no ranges and no negates, then we have a range that
21012
21128
  // cannot possibly match anything, and that poisons the whole glob
21013
21129
  if (!ranges.length && !negs.length) {
21014
- return ['$.', false, glob.length - pos, true];
21015
- }
21016
- // if we got one positive range, and it's a single character, then that's
21017
- // not actually a magic pattern, it's just that one literal character.
21018
- // we should not treat that as "magic", we should just return the literal
21019
- // character. [_] is a perfectly valid way to escape glob magic chars.
21020
- if (negs.length === 0 &&
21021
- ranges.length === 1 &&
21022
- /^\\?.$/.test(ranges[0]) &&
21023
- !negate) {
21024
- const r = ranges[0].length === 2 ? ranges[0].slice(-1) : ranges[0];
21025
- return [regexpEscape(r), false, endPos - pos, false];
21026
- }
21027
- const sranges = '[' + (negate ? '^' : '') + rangesToString(ranges) + ']';
21028
- const snegs = '[' + (negate ? '' : '^') + rangesToString(negs) + ']';
21029
- const comb = ranges.length && negs.length ? '(' + sranges + '|' + snegs + ')'
21030
- : ranges.length ? sranges
21031
- : snegs;
21032
- return [comb, uflag, endPos - pos, true];
21130
+ return [noMatch, glob.length - pos];
21131
+ }
21132
+ const sranges = '[' + (negate ? '^' : '') + ranges.join('') + ']';
21133
+ const snegs = '[' + (negate ? '' : '^') + negs.join('') + ']';
21134
+ const comb = '^' +
21135
+ (ranges.length && negs.length ? '(' + sranges + '|' + snegs + ')'
21136
+ : ranges.length ? sranges
21137
+ : snegs);
21138
+ return [new RegExp(comb, nocase ? 'iu' : 'u'), endPos - pos];
21033
21139
  };
21034
21140
 
21035
- /**
21036
- * Un-escape a string that has been escaped with {@link escape}.
21037
- *
21038
- * If the {@link MinimatchOptions.windowsPathsNoEscape} option is used, then
21039
- * square-bracket escapes are removed, but not backslash escapes.
21040
- *
21041
- * For example, it will turn the string `'[*]'` into `*`, but it will not
21042
- * turn `'\\*'` into `'*'`, because `\` is a path separator in
21043
- * `windowsPathsNoEscape` mode.
21044
- *
21045
- * When `windowsPathsNoEscape` is not set, then both square-bracket escapes and
21046
- * backslash escapes are removed.
21047
- *
21048
- * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
21049
- * or unescaped.
21050
- *
21051
- * When `magicalBraces` is not set, escapes of braces (`{` and `}`) will not be
21052
- * unescaped.
21053
- */
21054
- const unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true, } = {}) => {
21055
- if (magicalBraces) {
21056
- return windowsPathsNoEscape ?
21057
- s.replace(/\[([^/\\])\]/g, '$1')
21058
- : s
21059
- .replace(/((?!\\).|^)\[([^/\\])\]/g, '$1$2')
21060
- .replace(/\\([^/])/g, '$1');
21061
- }
21062
- return windowsPathsNoEscape ?
21063
- s.replace(/\[([^/\\{}])\]/g, '$1')
21064
- : s
21065
- .replace(/((?!\\).|^)\[([^/\\{}])\]/g, '$1$2')
21066
- .replace(/\\([^/{}])/g, '$1');
21141
+ // the end of the pattern, or of a negated sub-pattern
21142
+ const MATCH = { t: 'match', id: 0 };
21143
+ const extTypes = '!?+*@';
21144
+ const lit = (ch) => ({ t: 'lit', ch });
21145
+ const reEscape = (s) => s.replace(/[\\^$.*+?()[\]{}|/]/g, '\\$&');
21146
+ // a RegExp that only matches `s`, with unicode case folding
21147
+ const newFoldRe = (s) => new RegExp(`^${reEscape(s)}$`, 'iu');
21148
+ // foldRe for one literal char. The same chars are in most patterns,
21149
+ // so keep them. A RegExp without the `g` or `y` flag has no state to
21150
+ // share.
21151
+ const foldCache = new Map();
21152
+ const foldRe = (ch) => {
21153
+ let re = foldCache.get(ch);
21154
+ if (!re) {
21155
+ re = newFoldRe(ch);
21156
+ if (foldCache.size >= 1000)
21157
+ foldCache.clear();
21158
+ foldCache.set(ch, re);
21159
+ }
21160
+ return re;
21067
21161
  };
21068
-
21069
- // parse a single path portion
21070
- var _a;
21071
- const types = new Set(['!', '?', '+', '*', '@']);
21072
- const isExtglobType = (c) => types.has(c);
21073
- const isExtglobAST = (c) => isExtglobType(c.type);
21074
- // Map of which extglob types can adopt the children of a nested extglob
21075
- //
21076
- // anything but ! can adopt a matching type:
21077
- // +(a|+(b|c)|d) => +(a|b|c|d)
21078
- // *(a|*(b|c)|d) => *(a|b|c|d)
21079
- // @(a|@(b|c)|d) => @(a|b|c|d)
21080
- // ?(a|?(b|c)|d) => ?(a|b|c|d)
21081
- //
21082
- // * can adopt anything, because 0 or repetition is allowed
21083
- // *(a|?(b|c)|d) => *(a|b|c|d)
21084
- // *(a|+(b|c)|d) => *(a|b|c|d)
21085
- // *(a|@(b|c)|d) => *(a|b|c|d)
21086
- //
21087
- // + can adopt @, because 1 or repetition is allowed
21088
- // +(a|@(b|c)|d) => +(a|b|c|d)
21089
- //
21090
- // + and @ CANNOT adopt *, because 0 would be allowed
21091
- // +(a|*(b|c)|d) => would match "", on *(b|c)
21092
- // @(a|*(b|c)|d) => would match "", on *(b|c)
21093
- //
21094
- // + and @ CANNOT adopt ?, because 0 would be allowed
21095
- // +(a|?(b|c)|d) => would match "", on ?(b|c)
21096
- // @(a|?(b|c)|d) => would match "", on ?(b|c)
21097
- //
21098
- // ? can adopt @, because 0 or 1 is allowed
21099
- // ?(a|@(b|c)|d) => ?(a|b|c|d)
21100
- //
21101
- // ? and @ CANNOT adopt * or +, because >1 would be allowed
21102
- // ?(a|*(b|c)|d) => would match bbb on *(b|c)
21103
- // @(a|*(b|c)|d) => would match bbb on *(b|c)
21104
- // ?(a|+(b|c)|d) => would match bbb on +(b|c)
21105
- // @(a|+(b|c)|d) => would match bbb on +(b|c)
21106
- //
21107
- // ! CANNOT adopt ! (nothing else can either)
21108
- // !(a|!(b|c)|d) => !(a|b|c|d) would fail to match on b (not not b|c)
21109
- //
21110
- // ! can adopt @
21111
- // !(a|@(b|c)|d) => !(a|b|c|d)
21112
- //
21113
- // ! CANNOT adopt *
21114
- // !(a|*(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
21115
- //
21116
- // ! CANNOT adopt +
21117
- // !(a|+(b|c)|d) => !(a|b|c|d) would match on bbb, not allowed
21118
- //
21119
- // ! CANNOT adopt ?
21120
- // x!(a|?(b|c)|d) => x!(a|b|c|d) would fail to match "x"
21121
- const adoptionMap = new Map([
21122
- ['!', ['@']],
21123
- ['?', ['?', '@']],
21124
- ['@', ['@']],
21125
- ['*', ['*', '+', '?', '@']],
21126
- ['+', ['+', '@']],
21127
- ]);
21128
- // nested extglobs that can be adopted in, but with the addition of
21129
- // a blank '' element.
21130
- const adoptionWithSpaceMap = new Map([
21131
- ['!', ['?']],
21132
- ['@', ['?']],
21133
- ['+', ['?', '*']],
21134
- ]);
21135
- // union of the previous two maps
21136
- const adoptionAnyMap = new Map([
21137
- ['!', ['?', '@']],
21138
- ['?', ['?', '@']],
21139
- ['@', ['?', '@']],
21140
- ['*', ['*', '+', '?', '@']],
21141
- ['+', ['+', '@', '?', '*']],
21142
- ]);
21143
- // Extglobs that can take over their parent if they are the only child
21144
- // the key is parent, value maps child to resulting extglob parent type
21145
- // '@' is omitted because it's a special case. An `@` extglob with a single
21146
- // member can always be usurped by that subpattern.
21147
- const usurpMap = new Map([
21148
- ['!', new Map([['!', '@']])],
21149
- [
21150
- '?',
21151
- new Map([
21152
- ['*', '*'],
21153
- ['+', '*'],
21154
- ]),
21155
- ],
21156
- [
21157
- '@',
21158
- new Map([
21159
- ['!', '!'],
21160
- ['?', '?'],
21161
- ['@', '@'],
21162
- ['*', '*'],
21163
- ['+', '+'],
21164
- ]),
21165
- ],
21166
- [
21167
- '+',
21168
- new Map([
21169
- ['?', '*'],
21170
- ['*', '*'],
21171
- ]),
21172
- ],
21173
- ]);
21174
- // Patterns that get prepended to bind to the start of either the
21175
- // entire string, or just a single path portion, to prevent dots
21176
- // and/or traversal patterns, when needed.
21177
- // Exts don't need the ^ or / bit, because the root binds that already.
21178
- const startNoTraversal = '(?!(?:^|/)\\.\\.?(?:$|/))';
21179
- const startNoDot = '(?!\\.)';
21180
- // characters that indicate a start of pattern needs the "no dots" bit,
21181
- // because a dot *might* be matched. ( is not in the list, because in
21182
- // the case of a child extglob, it will handle the prevention itself.
21183
- const addPatternStart = new Set(['[', '.']);
21184
- // cases where traversal is A-OK, no dot prevention needed
21185
- const justDots = new Set(['..', '.']);
21186
- const reSpecials = new Set('().*{}+?[]^$\\!');
21187
- const regExpEscape$1 = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
21188
- // any single thing other than /
21189
- const qmark$1 = '[^/]';
21190
- // * => any number of characters
21191
- const star$1 = qmark$1 + '*?';
21192
- // use + when we need to ensure that *something* matches, because the * is
21193
- // the only thing in the path portion.
21194
- const starNoEmpty = qmark$1 + '+?';
21195
- // remove the \ chars that we added if we end up doing a nonmagic compare
21196
- // const deslash = (s: string) => s.replace(/\\(.)/g, '$1')
21197
- let ID = 0;
21198
- class AST {
21199
- type;
21200
- #root;
21201
- #hasMagic;
21202
- #uflag = false;
21203
- #parts = [];
21204
- #parent;
21205
- #parentIndex;
21206
- #negs;
21207
- #filledNegs = false;
21208
- #options;
21209
- #toString;
21210
- // set to true if it's an extglob with no children
21211
- // (which really means one child of '')
21212
- #emptyExt = false;
21213
- id = ++ID;
21214
- get depth() {
21215
- return (this.#parent?.depth ?? -1) + 1;
21216
- }
21217
- [Symbol.for('nodejs.util.inspect.custom')]() {
21218
- return {
21219
- '@@type': 'AST',
21220
- id: this.id,
21221
- type: this.type,
21222
- root: this.#root.id,
21223
- parent: this.#parent?.id,
21224
- depth: this.depth,
21225
- partsLength: this.#parts.length,
21226
- parts: this.#parts,
21227
- };
21228
- }
21229
- constructor(type, parent, options = {}) {
21230
- this.type = type;
21231
- // extglobs are inherently magical
21232
- if (type)
21233
- this.#hasMagic = true;
21234
- this.#parent = parent;
21235
- this.#root = this.#parent ? this.#parent.#root : this;
21236
- this.#options = this.#root === this ? options : this.#root.#options;
21237
- this.#negs = this.#root === this ? [] : this.#root.#negs;
21238
- if (type === '!' && !this.#root.#filledNegs)
21239
- this.#negs.push(this);
21240
- this.#parentIndex = this.#parent ? this.#parent.#parts.length : 0;
21241
- }
21242
- get hasMagic() {
21243
- /* c8 ignore start */
21244
- if (this.#hasMagic !== undefined)
21245
- return this.#hasMagic;
21246
- /* c8 ignore stop */
21247
- for (const p of this.#parts) {
21248
- if (typeof p === 'string')
21249
- continue;
21250
- if (p.type || p.hasMagic)
21251
- return (this.#hasMagic = true);
21162
+ const litMatch = (l, c) => {
21163
+ if (c === l.ch)
21164
+ return true;
21165
+ if (!l.re)
21166
+ return false;
21167
+ const code = c.charCodeAt(0);
21168
+ if (code >= 128)
21169
+ return l.re.test(c);
21170
+ const ascii = (l.ascii ??= new Uint8Array(128));
21171
+ ascii[code] ||= l.re.test(c) ? 1 : 2;
21172
+ return ascii[code] === 1;
21173
+ };
21174
+ const tokenize = (p, nocase, noext, form) => {
21175
+ const toks = [];
21176
+ for (let i = 0; i < p.length;) {
21177
+ const c = String.fromCodePoint(p.codePointAt(i));
21178
+ if (c === '\\') {
21179
+ // a trailing \ is a literal \
21180
+ const e = p.codePointAt(i + 1);
21181
+ const ch = e === undefined ? c : String.fromCodePoint(e);
21182
+ toks.push(lit(ch));
21183
+ i += 1 + ch.length;
21184
+ continue;
21252
21185
  }
21253
- // note: will be undefined until we generate the regexp src and find out
21254
- return this.#hasMagic;
21255
- }
21256
- // reconstructs the pattern
21257
- toString() {
21258
- return (this.#toString !== undefined ? this.#toString
21259
- : !this.type ?
21260
- (this.#toString = this.#parts.map(p => String(p)).join(''))
21261
- : (this.#toString =
21262
- this.type +
21263
- '(' +
21264
- this.#parts.map(p => String(p)).join('|') +
21265
- ')'));
21266
- }
21267
- #fillNegs() {
21268
- /* c8 ignore start */
21269
- if (this !== this.#root)
21270
- throw new Error('should only call on root');
21271
- if (this.#filledNegs)
21272
- return this;
21273
- /* c8 ignore stop */
21274
- // call toString() once to fill this out
21275
- this.toString();
21276
- this.#filledNegs = true;
21277
- let n;
21278
- while ((n = this.#negs.pop())) {
21279
- if (n.type !== '!')
21186
+ if (c === '[') {
21187
+ const [re, consumed] = parseClass(p, i, nocase, form);
21188
+ if (consumed) {
21189
+ toks.push({ t: 'class', re });
21190
+ i += consumed;
21280
21191
  continue;
21281
- // walk up the tree, appending everthing that comes AFTER parentIndex
21282
- let p = n;
21283
- let pp = p.#parent;
21284
- while (pp) {
21285
- for (let i = p.#parentIndex + 1; !pp.type && i < pp.#parts.length; i++) {
21286
- for (const part of n.#parts) {
21287
- /* c8 ignore start */
21288
- if (typeof part === 'string') {
21289
- throw new Error('string part in extglob AST??');
21290
- }
21291
- /* c8 ignore stop */
21292
- part.copyIn(pp.#parts[i]);
21293
- }
21294
- }
21295
- p = pp;
21296
- pp = p.#parent;
21297
21192
  }
21298
21193
  }
21299
- return this;
21194
+ if (!noext && extTypes.includes(c) && p[i + 1] === '(') {
21195
+ toks.push({ t: 'open', type: c });
21196
+ i += 2;
21197
+ continue;
21198
+ }
21199
+ toks.push(!noext && c === '|' ? { t: 'bar' }
21200
+ : !noext && c === ')' ? { t: 'close' }
21201
+ : c === '*' ? { t: 'star' }
21202
+ : c === '?' ? { t: 'any' }
21203
+ : lit(c));
21204
+ i += c.length;
21205
+ }
21206
+ return toks;
21207
+ };
21208
+ // map each open token index to the index of its close token
21209
+ const pair = (toks) => {
21210
+ const pairs = new Map();
21211
+ const stack = [];
21212
+ toks.forEach((tok, i) => {
21213
+ if (tok.t === 'open')
21214
+ stack.push(i);
21215
+ else if (tok.t === 'close' && stack.length)
21216
+ pairs.set(stack.pop(), i);
21217
+ });
21218
+ return pairs;
21219
+ };
21220
+ // build the alternatives between start and end. An open token that
21221
+ // is not closed is never inside a closed extglob, so bar and close
21222
+ // tokens are only literal at the top level.
21223
+ const build = (toks, pairs, start, end, top) => {
21224
+ let seq = [];
21225
+ const alts = [seq];
21226
+ for (let i = start; i < end; i++) {
21227
+ const tok = toks[i];
21228
+ const close = pairs.get(i);
21229
+ if (tok.t === 'open' && close !== undefined) {
21230
+ const sub = build(toks, pairs, i + 1, close, false);
21231
+ seq.push({ t: 'ext', type: tok.type, alts: sub });
21232
+ i = close;
21233
+ }
21234
+ else if (tok.t === 'bar' && !top) {
21235
+ alts.push((seq = []));
21236
+ }
21237
+ else if (tok.t === 'open') {
21238
+ seq.push(lit(tok.type), lit('('));
21239
+ }
21240
+ else if (tok.t === 'bar') {
21241
+ seq.push(lit('|'));
21242
+ }
21243
+ else if (tok.t === 'close') {
21244
+ seq.push(lit(')'));
21245
+ }
21246
+ else {
21247
+ seq.push(tok);
21248
+ }
21300
21249
  }
21301
- push(...parts) {
21302
- for (const p of parts) {
21303
- if (p === '')
21304
- continue;
21305
- /* c8 ignore start */
21306
- if (typeof p !== 'string' &&
21307
- !(p instanceof _a && p.#parent === this)) {
21308
- throw new Error('invalid part: ' + p);
21309
- }
21310
- /* c8 ignore stop */
21311
- this.#parts.push(p);
21312
- }
21313
- }
21314
- toJSON() {
21315
- const ret = this.type === null ?
21316
- this.#parts
21317
- .slice()
21318
- .map(p => (typeof p === 'string' ? p : p.toJSON()))
21319
- : [this.type, ...this.#parts.map(p => p.toJSON())];
21320
- if (this.isStart() && !this.type)
21321
- ret.unshift([]);
21322
- if (this.isEnd() &&
21323
- (this === this.#root ||
21324
- (this.#root.#filledNegs && this.#parent?.type === '!'))) {
21325
- ret.push({});
21326
- }
21327
- return ret;
21328
- }
21329
- isStart() {
21330
- if (this.#root === this)
21331
- return true;
21332
- // if (this.type) return !!this.#parent?.isStart()
21333
- if (!this.#parent?.isStart())
21334
- return false;
21335
- if (this.#parentIndex === 0)
21336
- return true;
21337
- // if everything AHEAD of this is a negation, then it's still the "start"
21338
- const p = this.#parent;
21339
- for (let i = 0; i < this.#parentIndex; i++) {
21340
- const pp = p.#parts[i];
21341
- if (!(pp instanceof _a && pp.type === '!')) {
21342
- return false;
21343
- }
21250
+ return alts;
21251
+ };
21252
+ // normalize each run of literal chars as one string, so that a char
21253
+ // and the combining marks after it compose (or decompose) together.
21254
+ // Syntax is already parsed, so a char that normalizes to a syntax
21255
+ // char (such as full-width `*` in NFKC) stays a literal.
21256
+ const normalize = (items, form) => {
21257
+ const out = [];
21258
+ let run = '';
21259
+ const flush = () => {
21260
+ for (const ch of run.normalize(form))
21261
+ out.push(lit(ch));
21262
+ run = '';
21263
+ };
21264
+ for (const it of items) {
21265
+ if (it.t === 'lit') {
21266
+ run += it.ch;
21267
+ continue;
21268
+ }
21269
+ flush();
21270
+ out.push(it.t === 'ext' ?
21271
+ { ...it, alts: it.alts.map(a => normalize(a, form)) }
21272
+ : it);
21273
+ }
21274
+ flush();
21275
+ return out;
21276
+ };
21277
+ // the start node, and the number of node ids. Ids are dense, from 0
21278
+ // for the match node.
21279
+ const compile = (items, nocase) => {
21280
+ let id = 1;
21281
+ const split = (out) => ({ t: 'split', id: id++, out });
21282
+ const seq = (items, next) => items.reduceRight((next, it) => item(it, next), next);
21283
+ const item = (it, next) => {
21284
+ switch (it.t) {
21285
+ case 'lit':
21286
+ const re = nocase ? foldRe(it.ch) : undefined;
21287
+ return { t: 'lit', id: id++, ch: it.ch, re, next };
21288
+ case 'any':
21289
+ case 'star':
21290
+ return { t: it.t, id: id++, next };
21291
+ case 'class':
21292
+ return { t: 'class', id: id++, re: it.re, next };
21293
+ }
21294
+ const alts = (next) => it.alts.map(a => seq(a, next));
21295
+ switch (it.type) {
21296
+ case '@':
21297
+ return split(alts(next));
21298
+ case '?':
21299
+ return split([next, ...alts(next)]);
21300
+ case '!':
21301
+ return { t: 'neg', id: id++, sub: split(alts(MATCH)), next };
21302
+ }
21303
+ // * and +, loop back after each match
21304
+ const after = split([]);
21305
+ const entry = split(alts(after));
21306
+ after.out.push(next, entry);
21307
+ return it.type === '*' ? after : entry;
21308
+ };
21309
+ const start = seq(items, MATCH);
21310
+ return [start, id];
21311
+ };
21312
+ // one line per node, in the order that a walk from the start
21313
+ // reaches them. Edges point to node ids. The nodes of a negated
21314
+ // sub-pattern are indented under their negation.
21315
+ const show = (start) => {
21316
+ const lines = [];
21317
+ const seen = new Set();
21318
+ const ref = (n) => (n.t === 'match' ? 'match' : String(n.id));
21319
+ const walk = (n, indent) => {
21320
+ if (n.t === 'match' || seen.has(n))
21321
+ return;
21322
+ seen.add(n);
21323
+ const line = (desc) => lines.push(`${indent}${n.id}: ${desc}`);
21324
+ switch (n.t) {
21325
+ case 'split':
21326
+ line(`split -> ${n.out.map(ref).join(', ')}`);
21327
+ for (const o of n.out)
21328
+ walk(o, indent);
21329
+ return;
21330
+ case 'neg':
21331
+ line(`!(${ref(n.sub)}) -> ${ref(n.next)}`);
21332
+ walk(n.sub, indent + ' ');
21333
+ return walk(n.next, indent);
21334
+ case 'lit':
21335
+ line(`${JSON.stringify(n.ch)} -> ${ref(n.next)}`);
21336
+ break;
21337
+ case 'class':
21338
+ line(`${n.re} -> ${ref(n.next)}`);
21339
+ break;
21340
+ default:
21341
+ line(`${n.t === 'any' ? '?' : '*'} -> ${ref(n.next)}`);
21344
21342
  }
21343
+ walk(n.next, indent);
21344
+ };
21345
+ walk(start, '');
21346
+ return [`start -> ${ref(start)}`, ...lines].join('\n');
21347
+ };
21348
+ // The threads at one position in the string. Each node thread is
21349
+ // only kept once, and runs with the same state are only kept once,
21350
+ // so that identical threads merge. The match node is not in
21351
+ // `threads`, because it can not step. It sets `match` instead.
21352
+ class State {
21353
+ // set by the Sim, to mark the nodes that this state has
21354
+ gen;
21355
+ threads = [];
21356
+ match = false;
21357
+ runs = [];
21358
+ // the sorted ids of the threads, with 0 for a match, once the
21359
+ // state is done
21360
+ ids = [];
21361
+ constructor(gen) {
21362
+ this.gen = gen;
21363
+ }
21364
+ // no threads, so nothing more can match
21365
+ get dead() {
21366
+ return !this.threads.length && !this.match && !this.runs.length;
21367
+ }
21368
+ }
21369
+ // true if the two states hold the same threads
21370
+ const sameState = (a, b) => {
21371
+ if (a.ids.length !== b.ids.length)
21372
+ return false;
21373
+ if (a.runs.length !== b.runs.length)
21374
+ return false;
21375
+ if (a.ids.some((id, i) => id !== b.ids[i]))
21376
+ return false;
21377
+ return a.runs.every(r => b.runs.some(o => o.neg === r.neg && sameState(o.state, r.state)));
21378
+ };
21379
+ const marks = (size) => ({
21380
+ has: new Float64Array(size),
21381
+ seen: new Float64Array(size),
21382
+ gen: 0,
21383
+ });
21384
+ class Sim {
21385
+ // `.` and `..` only match literal dots, even with `dot:true`
21386
+ dots;
21387
+ // the string starts with `.`, and `dot:true` is not set
21388
+ dotStart;
21389
+ #marks;
21390
+ constructor(dots, dotStart, marks) {
21391
+ this.dots = dots;
21392
+ this.dotStart = dotStart;
21393
+ this.#marks = marks;
21394
+ }
21395
+ state() {
21396
+ return new State(++this.#marks.gen);
21397
+ }
21398
+ // sort the ids of a state, once it has all of its threads
21399
+ done(state) {
21400
+ state.ids = state.threads.map(n => n.id).sort((a, b) => a - b);
21401
+ if (state.match)
21402
+ state.ids.unshift(MATCH.id);
21403
+ return state;
21404
+ }
21405
+ // true the first time that the state walks through the node, so
21406
+ // that loops in the graph stop
21407
+ #visit(state, node) {
21408
+ const { seen } = this.#marks;
21409
+ if (seen[node.id] === state.gen)
21410
+ return false;
21411
+ seen[node.id] = state.gen;
21345
21412
  return true;
21346
21413
  }
21347
- isEnd() {
21348
- if (this.#root === this)
21349
- return true;
21350
- if (this.#parent?.type === '!')
21351
- return true;
21352
- if (!this.#parent?.isEnd())
21414
+ #addThread(state, node) {
21415
+ const { has } = this.#marks;
21416
+ if (has[node.id] === state.gen)
21353
21417
  return false;
21354
- if (!this.type)
21355
- return this.#parent?.isEnd();
21356
- // if not root, it'll always have a parent
21357
- /* c8 ignore start */
21358
- const pl = this.#parent ? this.#parent.#parts.length : 0;
21359
- /* c8 ignore stop */
21360
- return this.#parentIndex === pl - 1;
21361
- }
21362
- copyIn(part) {
21363
- if (typeof part === 'string')
21364
- this.push(part);
21365
- else
21366
- this.push(part.clone(this));
21367
- }
21368
- clone(parent) {
21369
- const c = new _a(this.type, parent);
21370
- for (const p of this.#parts) {
21371
- c.copyIn(p);
21372
- }
21373
- return c;
21374
- }
21375
- static #parseAST(str, ast, pos, opt, extDepth) {
21376
- const maxDepth = opt.maxExtglobRecursion ?? 2;
21377
- let escaping = false;
21378
- let inBrace = false;
21379
- let braceStart = -1;
21380
- let braceNeg = false;
21381
- if (ast.type === null) {
21382
- // outside of a extglob, append until we find a start
21383
- let i = pos;
21384
- let acc = '';
21385
- while (i < str.length) {
21386
- const c = str.charAt(i++);
21387
- // still accumulate escapes at this point, but we do ignore
21388
- // starts that are escaped
21389
- if (escaping || c === '\\') {
21390
- escaping = !escaping;
21391
- acc += c;
21392
- continue;
21393
- }
21394
- if (inBrace) {
21395
- if (i === braceStart + 1) {
21396
- if (c === '^' || c === '!') {
21397
- braceNeg = true;
21398
- }
21418
+ has[node.id] = state.gen;
21419
+ state.threads.push(node);
21420
+ return true;
21421
+ }
21422
+ // wildcards only match a leading dot if `dot:true`. In a negated
21423
+ // sub-pattern, this does not apply, because the negation itself
21424
+ // can not match a leading dot.
21425
+ restricted(i, top) {
21426
+ return top && (this.dots || (this.dotStart && i === 0));
21427
+ }
21428
+ add(state, node, i, top) {
21429
+ switch (node.t) {
21430
+ case 'split':
21431
+ if (!this.#visit(state, node))
21432
+ return;
21433
+ for (const o of node.out)
21434
+ this.add(state, o, i, top);
21435
+ return;
21436
+ case 'neg':
21437
+ if (!this.#visit(state, node))
21438
+ return;
21439
+ const sub = this.state();
21440
+ this.add(sub, node.sub, i, false);
21441
+ this.done(sub);
21442
+ return this.addRun(state, { t: 'run', neg: node, state: sub }, i, top);
21443
+ case 'star':
21444
+ // a * can not start a dotfile, even if it matches nothing
21445
+ if (this.restricted(i, top))
21446
+ return;
21447
+ if (this.#addThread(state, node))
21448
+ this.add(state, node.next, i, top);
21449
+ return;
21450
+ case 'match':
21451
+ state.match = true;
21452
+ return;
21453
+ default:
21454
+ this.#addThread(state, node);
21455
+ }
21456
+ }
21457
+ addRun(state, run, i, top) {
21458
+ for (const r of state.runs) {
21459
+ if (r.neg === run.neg && sameState(r.state, run.state))
21460
+ return;
21461
+ }
21462
+ state.runs.push(run);
21463
+ if (!run.state.match) {
21464
+ this.add(state, run.neg.next, i, top);
21465
+ }
21466
+ }
21467
+ // the threads after char `c`, at index `i`
21468
+ step(state, c, i, top) {
21469
+ const next = this.state();
21470
+ for (const th of state.threads) {
21471
+ switch (th.t) {
21472
+ case 'lit':
21473
+ if (litMatch(th, c)) {
21474
+ this.add(next, th.next, i + 1, top);
21399
21475
  }
21400
- else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
21401
- inBrace = false;
21476
+ break;
21477
+ case 'any':
21478
+ if (!this.restricted(i, top))
21479
+ this.add(next, th.next, i + 1, top);
21480
+ break;
21481
+ case 'class':
21482
+ if (!this.restricted(i, top) && th.re.test(c)) {
21483
+ this.add(next, th.next, i + 1, top);
21402
21484
  }
21403
- acc += c;
21404
- continue;
21405
- }
21406
- else if (c === '[') {
21407
- inBrace = true;
21408
- braceStart = i;
21409
- braceNeg = false;
21410
- acc += c;
21411
- continue;
21412
- }
21413
- // we don't have to check for adoption here, because that's
21414
- // done at the other recursion point.
21415
- const doRecurse = !opt.noext &&
21416
- isExtglobType(c) &&
21417
- str.charAt(i) === '(' &&
21418
- extDepth <= maxDepth;
21419
- if (doRecurse) {
21420
- ast.push(acc);
21421
- acc = '';
21422
- const ext = new _a(c, ast);
21423
- i = _a.#parseAST(str, ext, i, opt, extDepth + 1);
21424
- ast.push(ext);
21425
- continue;
21426
- }
21427
- acc += c;
21485
+ break;
21486
+ case 'star':
21487
+ if (!this.restricted(i, top))
21488
+ this.add(next, th, i + 1, top);
21428
21489
  }
21429
- ast.push(acc);
21430
- return i;
21431
21490
  }
21432
- // some kind of extglob, pos is at the (
21433
- // find the next | or )
21434
- let i = pos + 1;
21435
- let part = new _a(null, ast);
21436
- const parts = [];
21437
- let acc = '';
21438
- while (i < str.length) {
21439
- const c = str.charAt(i++);
21440
- // still accumulate escapes at this point, but we do ignore
21441
- // starts that are escaped
21442
- if (escaping || c === '\\') {
21443
- escaping = !escaping;
21444
- acc += c;
21445
- continue;
21446
- }
21447
- if (inBrace) {
21448
- if (i === braceStart + 1) {
21449
- if (c === '^' || c === '!') {
21450
- braceNeg = true;
21451
- }
21452
- }
21453
- else if (c === ']' && !(i === braceStart + 2 && braceNeg)) {
21454
- inBrace = false;
21455
- }
21456
- acc += c;
21457
- continue;
21458
- }
21459
- else if (c === '[') {
21460
- inBrace = true;
21461
- braceStart = i;
21462
- braceNeg = false;
21463
- acc += c;
21464
- continue;
21465
- }
21466
- const doRecurse = !opt.noext &&
21467
- isExtglobType(c) &&
21468
- str.charAt(i) === '(' &&
21469
- /* c8 ignore start - the maxDepth is sufficient here */
21470
- (extDepth <= maxDepth || (ast && ast.#canAdoptType(c)));
21471
- /* c8 ignore stop */
21472
- if (doRecurse) {
21473
- const depthAdd = ast && ast.#canAdoptType(c) ? 0 : 1;
21474
- part.push(acc);
21475
- acc = '';
21476
- const ext = new _a(c, part);
21477
- part.push(ext);
21478
- i = _a.#parseAST(str, ext, i, opt, extDepth + depthAdd);
21479
- continue;
21480
- }
21481
- if (c === '|') {
21482
- part.push(acc);
21483
- acc = '';
21484
- parts.push(part);
21485
- part = new _a(null, ast);
21486
- continue;
21487
- }
21488
- if (c === ')') {
21489
- if (acc === '' && ast.#parts.length === 0) {
21490
- ast.#emptyExt = true;
21491
+ if (!this.restricted(i, top)) {
21492
+ for (const run of state.runs) {
21493
+ // a sub-pattern with no threads can not match again, so the
21494
+ // run stays the same
21495
+ if (run.state.dead) {
21496
+ this.addRun(next, run, i + 1, top);
21497
+ continue;
21491
21498
  }
21492
- part.push(acc);
21493
- acc = '';
21494
- ast.push(...parts, part);
21495
- return i;
21499
+ const sub = this.step(run.state, c, i, false);
21500
+ this.addRun(next, { t: 'run', neg: run.neg, state: sub }, i + 1, top);
21496
21501
  }
21497
- acc += c;
21498
- }
21499
- // unfinished extglob
21500
- // if we got here, it was a malformed extglob! not an extglob, but
21501
- // maybe something else in there.
21502
- ast.type = null;
21503
- ast.#hasMagic = undefined;
21504
- ast.#parts = [str.substring(pos - 1)];
21505
- return i;
21506
- }
21507
- #canAdoptWithSpace(child) {
21508
- return this.#canAdopt(child, adoptionWithSpaceMap);
21509
- }
21510
- #canAdopt(child, map = adoptionMap) {
21511
- if (!child ||
21512
- typeof child !== 'object' ||
21513
- child.type !== null ||
21514
- child.#parts.length !== 1 ||
21515
- this.type === null) {
21516
- return false;
21517
- }
21518
- const gc = child.#parts[0];
21519
- if (!gc || typeof gc !== 'object' || gc.type === null) {
21520
- return false;
21521
- }
21522
- return this.#canAdoptType(gc.type, map);
21523
- }
21524
- #canAdoptType(c, map = adoptionAnyMap) {
21525
- return !!map.get(this.type)?.includes(c);
21526
- }
21527
- #adoptWithSpace(child, index) {
21528
- const gc = child.#parts[0];
21529
- const blank = new _a(null, gc, this.options);
21530
- blank.#parts.push('');
21531
- gc.push(blank);
21532
- this.#adopt(child, index);
21533
- }
21534
- #adopt(child, index) {
21535
- const gc = child.#parts[0];
21536
- this.#parts.splice(index, 1, ...gc.#parts);
21537
- for (const p of gc.#parts) {
21538
- if (typeof p === 'object')
21539
- p.#parent = this;
21540
- }
21541
- this.#toString = undefined;
21542
- }
21543
- #canUsurpType(c) {
21544
- const m = usurpMap.get(this.type);
21545
- return !!m?.has(c);
21546
- }
21547
- #canUsurp(child) {
21548
- if (!child ||
21549
- typeof child !== 'object' ||
21550
- child.type !== null ||
21551
- child.#parts.length !== 1 ||
21552
- this.type === null ||
21553
- this.#parts.length !== 1) {
21554
- return false;
21555
- }
21556
- const gc = child.#parts[0];
21557
- if (!gc || typeof gc !== 'object' || gc.type === null) {
21558
- return false;
21559
21502
  }
21560
- return this.#canUsurpType(gc.type);
21503
+ return this.done(next);
21561
21504
  }
21562
- #usurp(child) {
21563
- const m = usurpMap.get(this.type);
21564
- const gc = child.#parts[0];
21565
- const nt = m?.get(gc.type);
21566
- /* c8 ignore start - impossible */
21567
- if (!nt)
21568
- return false;
21569
- /* c8 ignore stop */
21570
- this.#parts = gc.#parts;
21571
- for (const p of this.#parts) {
21572
- if (typeof p === 'object') {
21573
- p.#parent = this;
21574
- }
21575
- }
21576
- this.type = nt;
21577
- this.#toString = undefined;
21578
- this.#emptyExt = false;
21579
- }
21580
- static fromGlob(pattern, options = {}) {
21581
- const ast = new _a(null, undefined, options);
21582
- _a.#parseAST(pattern, ast, 0, options, 0);
21583
- return ast;
21584
- }
21585
- // returns the regular expression if there's magic, or the unescaped
21586
- // string if not.
21587
- toMMPattern() {
21588
- // should only be called on root
21589
- /* c8 ignore start */
21590
- if (this !== this.#root)
21591
- return this.#root.toMMPattern();
21592
- /* c8 ignore stop */
21593
- const glob = this.toString();
21594
- const [re, body, hasMagic, uflag] = this.toRegExpSource();
21595
- // if we're in nocase mode, and not nocaseMagicOnly, then we do
21596
- // still need a regular expression if we have to case-insensitively
21597
- // match capital/lowercase characters.
21598
- const anyMagic = hasMagic ||
21599
- this.#hasMagic ||
21600
- (this.#options.nocase &&
21601
- !this.#options.nocaseMagicOnly &&
21602
- glob.toUpperCase() !== glob.toLowerCase());
21603
- if (!anyMagic) {
21604
- return body;
21605
- }
21606
- const flags = (this.#options.nocase ? 'i' : '') + (uflag ? 'u' : '');
21607
- return Object.assign(new RegExp(`^${re}$`, flags), {
21608
- _src: re,
21609
- _glob: glob,
21610
- });
21505
+ }
21506
+ // A canonical key for the threads in a state. States with the same
21507
+ // key hold the same threads, so they match the same strings.
21508
+ const stateKey = (state) => {
21509
+ const ids = state.ids.join();
21510
+ if (!state.runs.length)
21511
+ return ids;
21512
+ const runs = state.runs.map(r => `${r.neg.id}(${stateKey(r.state)})`);
21513
+ return `${ids}|${runs.sort().join('|')}`;
21514
+ };
21515
+ // The cache can hold about this many chars of state keys and
21516
+ // transitions. When it is full, it is cleared, so that a pattern
21517
+ // can not use an unbounded amount of memory.
21518
+ const DFA_BUDGET = 1 << 18;
21519
+ // A cache of the states that the simulation reaches, built as the
21520
+ // matcher is used. Each char of a string is then one Map lookup,
21521
+ // once the states that it leads to are known.
21522
+ //
21523
+ // The dot rules only restrict the first char of a dotfile (strings
21524
+ // that are only `.` or `..` do not use the cache). So a dotfile
21525
+ // starts at its own state, which takes its first step with the
21526
+ // restriction, and every other state takes steps without it.
21527
+ class Dfa {
21528
+ #start;
21529
+ #states = new Map();
21530
+ #size = 0;
21531
+ #plain;
21532
+ #dotStart;
21533
+ #sim;
21534
+ #dotSim;
21535
+ constructor(start, marks) {
21536
+ this.#start = start;
21537
+ this.#sim = new Sim(false, false, marks);
21538
+ this.#dotSim = new Sim(false, true, marks);
21539
+ }
21540
+ #make(state) {
21541
+ return {
21542
+ state,
21543
+ match: state.match,
21544
+ dead: state.dead,
21545
+ next: new Map(),
21546
+ };
21611
21547
  }
21612
- get options() {
21613
- return this.#options;
21614
- }
21615
- // returns the string match, the regexp source, whether there's magic
21616
- // in the regexp (so a regular expression is required) and whether or
21617
- // not the uflag is needed for the regular expression (for posix classes)
21618
- // TODO: instead of injecting the start/end at this point, just return
21619
- // the BODY of the regexp, along with the start/end portions suitable
21620
- // for binding the start/end in either a joined full-path makeRe context
21621
- // (where we bind to (^|/), or a standalone matchPart context (where
21622
- // we bind to ^, and not /). Otherwise slashes get duped!
21623
- //
21624
- // In part-matching mode, the start is:
21625
- // - if not isStart: nothing
21626
- // - if traversal possible, but not allowed: ^(?!\.\.?$)
21627
- // - if dots allowed or not possible: ^
21628
- // - if dots possible and not allowed: ^(?!\.)
21629
- // end is:
21630
- // - if not isEnd(): nothing
21631
- // - else: $
21632
- //
21633
- // In full-path matching mode, we put the slash at the START of the
21634
- // pattern, so start is:
21635
- // - if first pattern: same as part-matching mode
21636
- // - if not isStart(): nothing
21637
- // - if traversal possible, but not allowed: /(?!\.\.?(?:$|/))
21638
- // - if dots allowed or not possible: /
21639
- // - if dots possible and not allowed: /(?!\.)
21640
- // end is:
21641
- // - if last pattern, same as part-matching mode
21642
- // - else nothing
21643
- //
21644
- // Always put the (?:$|/) on negated tails, though, because that has to be
21645
- // there to bind the end of the negated pattern portion, and it's easier to
21646
- // just stick it in now rather than try to inject it later in the middle of
21647
- // the pattern.
21648
- //
21649
- // We can just always return the same end, and leave it up to the caller
21650
- // to know whether it's going to be used joined or in parts.
21651
- // And, if the start is adjusted slightly, can do the same there:
21652
- // - if not isStart: nothing
21653
- // - if traversal possible, but not allowed: (?:/|^)(?!\.\.?$)
21654
- // - if dots allowed or not possible: (?:/|^)
21655
- // - if dots possible and not allowed: (?:/|^)(?!\.)
21656
- //
21657
- // But it's better to have a simpler binding without a conditional, for
21658
- // performance, so probably better to return both start options.
21659
- //
21660
- // Then the caller just ignores the end if it's not the first pattern,
21661
- // and the start always gets applied.
21662
- //
21663
- // But that's always going to be $ if it's the ending pattern, or nothing,
21664
- // so the caller can just attach $ at the end of the pattern when building.
21665
- //
21666
- // So the todo is:
21667
- // - better detect what kind of start is needed
21668
- // - return both flavors of starting pattern
21669
- // - attach $ at the end of the pattern when creating the actual RegExp
21670
- //
21671
- // Ah, but wait, no, that all only applies to the root when the first pattern
21672
- // is not an extglob. If the first pattern IS an extglob, then we need all
21673
- // that dot prevention biz to live in the extglob portions, because eg
21674
- // +(*|.x*) can match .xy but not .yx.
21675
- //
21676
- // So, return the two flavors if it's #root and the first child is not an
21677
- // AST, otherwise leave it to the child AST to handle it, and there,
21678
- // use the (?:^|/) style of start binding.
21679
- //
21680
- // Even simplified further:
21681
- // - Since the start for a join is eg /(?!\.) and the start for a part
21682
- // is ^(?!\.), we can just prepend (?!\.) to the pattern (either root
21683
- // or start or whatever) and prepend ^ or / at the Regexp construction.
21684
- toRegExpSource(allowDot) {
21685
- const dot = allowDot ?? !!this.#options.dot;
21686
- if (this.#root === this) {
21687
- this.#flatten();
21688
- this.#fillNegs();
21689
- }
21690
- if (!isExtglobAST(this)) {
21691
- const noEmpty = this.isStart() &&
21692
- this.isEnd() &&
21693
- !this.#parts.some(s => typeof s !== 'string');
21694
- const src = this.#parts
21695
- .map(p => {
21696
- const [re, _, hasMagic, uflag] = typeof p === 'string' ?
21697
- _a.#parseGlob(p, this.#hasMagic, noEmpty)
21698
- : p.toRegExpSource(allowDot);
21699
- this.#hasMagic = this.#hasMagic || hasMagic;
21700
- this.#uflag = this.#uflag || uflag;
21701
- return re;
21702
- })
21703
- .join('');
21704
- let start = '';
21705
- if (this.isStart()) {
21706
- if (typeof this.#parts[0] === 'string') {
21707
- // this is the string that will match the start of the pattern,
21708
- // so we need to protect against dots and such.
21709
- // '.' and '..' cannot match unless the pattern is that exactly,
21710
- // even if it starts with . or dot:true is set.
21711
- const dotTravAllowed = this.#parts.length === 1 && justDots.has(this.#parts[0]);
21712
- if (!dotTravAllowed) {
21713
- const aps = addPatternStart;
21714
- // check if we have a possibility of matching . or ..,
21715
- // and prevent that.
21716
- const needNoTrav =
21717
- // dots are allowed, and the pattern starts with [ or .
21718
- (dot && aps.has(src.charAt(0))) ||
21719
- // the pattern starts with \., and then [ or .
21720
- (src.startsWith('\\.') && aps.has(src.charAt(2))) ||
21721
- // the pattern starts with \.\., and then [ or .
21722
- (src.startsWith('\\.\\.') && aps.has(src.charAt(4)));
21723
- // no need to prevent dots if it can't match a dot, or if a
21724
- // sub-pattern will be preventing it anyway.
21725
- const needNoDot = !dot && !allowDot && aps.has(src.charAt(0));
21726
- start =
21727
- needNoTrav ? startNoTraversal
21728
- : needNoDot ? startNoDot
21729
- : '';
21730
- }
21731
- }
21732
- }
21733
- // append the "end of path portion" pattern to negation tails
21734
- let end = '';
21735
- if (this.isEnd() &&
21736
- this.#root.#filledNegs &&
21737
- this.#parent?.type === '!') {
21738
- end = '(?:$|\\/)';
21739
- }
21740
- const final = start + src + end;
21741
- return [
21742
- final,
21743
- unescape(src),
21744
- (this.#hasMagic = !!this.#hasMagic),
21745
- this.#uflag,
21746
- ];
21747
- }
21748
- // We need to calculate the body *twice* if it's a repeat pattern
21749
- // at the start, once in nodot mode, then again in dot mode, so a
21750
- // pattern like *(?) can match 'x.y'
21751
- const repeated = this.type === '*' || this.type === '+';
21752
- // some kind of extglob
21753
- const start = this.type === '!' ? '(?:(?!(?:' : '(?:';
21754
- let body = this.#partsToRegExp(dot);
21755
- if (this.isStart() && this.isEnd() && !body && this.type !== '!') {
21756
- // invalid extglob, has to at least be *something* present, if it's
21757
- // the entire path portion.
21758
- const s = this.toString();
21759
- const me = this;
21760
- me.#parts = [s];
21761
- me.type = null;
21762
- me.#hasMagic = undefined;
21763
- return [s, unescape(this.toString()), false, false];
21764
- }
21765
- let bodyDotAllowed = !repeated || allowDot || dot || !startNoDot ?
21766
- ''
21767
- : this.#partsToRegExp(true);
21768
- if (bodyDotAllowed === body) {
21769
- bodyDotAllowed = '';
21770
- }
21771
- if (bodyDotAllowed) {
21772
- body = `(?:${body})(?:${bodyDotAllowed})*?`;
21773
- }
21774
- // an empty !() is exactly equivalent to a starNoEmpty
21775
- let final = '';
21776
- if (this.type === '!' && this.#emptyExt) {
21777
- final = (this.isStart() && !dot ? startNoDot : '') + starNoEmpty;
21548
+ #intern(state) {
21549
+ const key = stateKey(state);
21550
+ const found = this.#states.get(key);
21551
+ if (found)
21552
+ return found;
21553
+ if (this.#size > DFA_BUDGET)
21554
+ this.#clear();
21555
+ const d = this.#make(state);
21556
+ this.#states.set(key, d);
21557
+ this.#size += key.length + 1;
21558
+ return d;
21559
+ }
21560
+ #clear() {
21561
+ this.#states.clear();
21562
+ this.#size = 0;
21563
+ this.#plain = undefined;
21564
+ this.#dotStart = undefined;
21565
+ }
21566
+ start(dotStart) {
21567
+ if (dotStart) {
21568
+ if (this.#dotStart)
21569
+ return this.#dotStart;
21570
+ const state = this.#dotSim.state();
21571
+ this.#dotSim.add(state, this.#start, 0, true);
21572
+ return (this.#dotStart = this.#make(this.#dotSim.done(state)));
21778
21573
  }
21779
- else {
21780
- const close = this.type === '!' ?
21781
- // !() must match something,but !(x) can match ''
21782
- '))' +
21783
- (this.isStart() && !dot && !allowDot ? startNoDot : '') +
21784
- star$1 +
21785
- ')'
21786
- : this.type === '@' ? ')'
21787
- : this.type === '?' ? ')?'
21788
- : this.type === '+' && bodyDotAllowed ? ')'
21789
- : this.type === '*' && bodyDotAllowed ? `)?`
21790
- : `)${this.type}`;
21791
- final = start + body + close;
21792
- }
21793
- return [
21794
- final,
21795
- unescape(body),
21796
- (this.#hasMagic = !!this.#hasMagic),
21797
- this.#uflag,
21798
- ];
21799
- }
21800
- #flatten() {
21801
- if (!isExtglobAST(this)) {
21802
- for (const p of this.#parts) {
21803
- if (typeof p === 'object') {
21804
- p.#flatten();
21805
- }
21574
+ if (this.#plain)
21575
+ return this.#plain;
21576
+ const state = this.#sim.state();
21577
+ this.#sim.add(state, this.#start, 0, true);
21578
+ return (this.#plain = this.#intern(this.#sim.done(state)));
21579
+ }
21580
+ // the next state, after a miss in `d.ascii`
21581
+ next(d, c) {
21582
+ const code = c.charCodeAt(0);
21583
+ const found = code < 128 ? undefined : d.next.get(c);
21584
+ if (found)
21585
+ return found;
21586
+ // the dotfile start is not in the cache, so it is never shared
21587
+ const n = d === this.#dotStart ?
21588
+ this.#intern(this.#dotSim.step(d.state, c, 0, true))
21589
+ : this.#intern(this.#sim.step(d.state, c, 1, true));
21590
+ if (code < 128) {
21591
+ if (!d.ascii) {
21592
+ d.ascii = new Array(128);
21593
+ this.#size += 128;
21806
21594
  }
21595
+ d.ascii[code] = n;
21807
21596
  }
21808
21597
  else {
21809
- // do up to 10 passes to flatten as much as possible
21810
- let iterations = 0;
21811
- let done = false;
21812
- do {
21813
- done = true;
21814
- for (let i = 0; i < this.#parts.length; i++) {
21815
- const c = this.#parts[i];
21816
- if (typeof c === 'object') {
21817
- c.#flatten();
21818
- if (this.#canAdopt(c)) {
21819
- done = false;
21820
- this.#adopt(c, i);
21821
- }
21822
- else if (this.#canAdoptWithSpace(c)) {
21823
- done = false;
21824
- this.#adoptWithSpace(c, i);
21825
- }
21826
- else if (this.#canUsurp(c)) {
21827
- done = false;
21828
- this.#usurp(c);
21829
- }
21830
- }
21831
- }
21832
- } while (!done && ++iterations < 10);
21598
+ d.next.set(c, n);
21833
21599
  }
21834
- this.#toString = undefined;
21600
+ this.#size++;
21601
+ return n;
21835
21602
  }
21836
- #partsToRegExp(dot) {
21837
- return this.#parts
21838
- .map(p => {
21839
- // extglob ASTs should only contain parent ASTs
21840
- /* c8 ignore start */
21841
- if (typeof p === 'string') {
21842
- throw new Error('string type in extglob ast??');
21843
- }
21844
- /* c8 ignore stop */
21845
- // can ignore hasMagic, because extglobs are already always magic
21846
- const [re, _, _hasMagic, uflag] = p.toRegExpSource(dot);
21847
- this.#uflag = this.#uflag || uflag;
21848
- return re;
21849
- })
21850
- .filter(p => !(this.isStart() && this.isEnd()) || !!p)
21851
- .join('|');
21852
- }
21853
- static #parseGlob(glob, hasMagic, noEmpty = false) {
21854
- let escaping = false;
21855
- let re = '';
21856
- let uflag = false;
21857
- // multiple stars that aren't globstars coalesce into one *
21858
- let inStar = false;
21859
- for (let i = 0; i < glob.length; i++) {
21860
- const c = glob.charAt(i);
21861
- if (escaping) {
21862
- escaping = false;
21863
- re += (reSpecials.has(c) ? '\\' : '') + c;
21864
- continue;
21603
+ }
21604
+ // the nodes in order, if the graph is a single chain
21605
+ const linear = (start) => {
21606
+ const nodes = [];
21607
+ for (let n = start; n.t !== 'match'; n = n.next) {
21608
+ if (n.t === 'split' || n.t === 'neg')
21609
+ return undefined;
21610
+ nodes.push(n);
21611
+ }
21612
+ return nodes;
21613
+ };
21614
+ // Match a chain of nodes. Each node other than `*` matches exactly
21615
+ // one char, so on a failure it is enough to go back to the last `*`
21616
+ // and let it match one more char. This takes at most
21617
+ // nodes.length * chars.length steps.
21618
+ const testLinear = (nodes, chars, start, end) => {
21619
+ let ni = 0;
21620
+ let ci = start;
21621
+ let starNi = -1;
21622
+ let starCi = 0;
21623
+ while (ci < end) {
21624
+ const n = nodes[ni];
21625
+ if (n?.t === 'star') {
21626
+ starNi = ni++;
21627
+ starCi = ci;
21628
+ continue;
21629
+ }
21630
+ const c = chars[ci];
21631
+ if (n &&
21632
+ (n.t === 'any' ||
21633
+ (n.t === 'lit' && litMatch(n, c)) ||
21634
+ (n.t === 'class' && n.re.test(c)))) {
21635
+ ni++;
21636
+ ci++;
21637
+ continue;
21638
+ }
21639
+ if (starNi === -1)
21640
+ return false;
21641
+ ni = starNi + 1;
21642
+ ci = ++starCi;
21643
+ }
21644
+ while (nodes[ni]?.t === 'star')
21645
+ ni++;
21646
+ return ni === nodes.length;
21647
+ };
21648
+ // The least and most chars that the items can match. These are only
21649
+ // bounds: a negation can match a string of any length, so it counts
21650
+ // as 0 to Infinity.
21651
+ const lengths = (items) => {
21652
+ let min = 0;
21653
+ let max = 0;
21654
+ for (const it of items) {
21655
+ const [lo, hi] = itemLengths(it);
21656
+ min += lo;
21657
+ max += hi;
21658
+ }
21659
+ return [min, max];
21660
+ };
21661
+ const itemLengths = (it) => {
21662
+ if (it.t === 'star')
21663
+ return [0, Infinity];
21664
+ if (it.t !== 'ext')
21665
+ return [1, 1];
21666
+ if (it.type === '!')
21667
+ return [0, Infinity];
21668
+ const alts = it.alts.map(lengths);
21669
+ const min = Math.min(...alts.map(a => a[0]));
21670
+ const max = Math.max(...alts.map(a => a[1]));
21671
+ // a loop of alternatives that only match the empty string is empty
21672
+ const loop = max === 0 ? 0 : Infinity;
21673
+ return (it.type === '@' ? [min, max]
21674
+ : it.type === '?' ? [0, max]
21675
+ : it.type === '*' ? [0, loop]
21676
+ : [min, loop]);
21677
+ };
21678
+ // true if the chars from `start` match the literals
21679
+ const hasAffix = (lits, chars, start) => {
21680
+ for (let j = 0; j < lits.length; j++) {
21681
+ if (!litMatch(lits[j], chars[start + j]))
21682
+ return false;
21683
+ }
21684
+ return true;
21685
+ };
21686
+ // a `/`, or half of a surrogate pair
21687
+ const slashOrSurrogate = /[/\ud800-\udfff]/;
21688
+ const surrogate = /[\ud800-\udfff]/;
21689
+ // a string can be indexed by code point, unless it has surrogate
21690
+ // pairs
21691
+ const codePoints = (s) => surrogate.test(s) ? [...s] : s;
21692
+ const noSlash = (s) => {
21693
+ if (s.includes('/')) {
21694
+ throw new Error('can only match a single path portion, got: ' + s);
21695
+ }
21696
+ };
21697
+ class Matcher {
21698
+ pattern;
21699
+ options;
21700
+ #start;
21701
+ // the number of node ids
21702
+ #size;
21703
+ #marks;
21704
+ // the Sim for strings that are only `.` or `..`
21705
+ #dotsSim;
21706
+ // the string to compare, if the pattern has no magic. With
21707
+ // `nocase:true`, the RegExp that folds case is made when it is
21708
+ // first used.
21709
+ #literal;
21710
+ #literalFold = false;
21711
+ #literalRe;
21712
+ // the nodes between the prefix and suffix, if the graph is a
21713
+ // single chain
21714
+ #linear;
21715
+ // the linear nodes are only `*` and `?`, so the length bounds and
21716
+ // the prefix and suffix decide the match
21717
+ #wild = false;
21718
+ // the least and most chars that a match can have
21719
+ #min = 0;
21720
+ #max = Infinity;
21721
+ // the literals that a match must start and end with. Each one
21722
+ // matches exactly one char, and they are distinct items in the
21723
+ // pattern, so a string that does not have them can not match.
21724
+ #prefix = [];
21725
+ #suffix = [];
21726
+ // the prefix and suffix as strings, if they do not fold case
21727
+ #prefixStr;
21728
+ #suffixStr;
21729
+ // there is a prefix or suffix to check
21730
+ #affix = false;
21731
+ #dfa;
21732
+ #form;
21733
+ constructor(pattern, options = {}) {
21734
+ if (pattern.includes('/')) {
21735
+ throw new Error('can only match a single path portion, got pattern: ' + pattern);
21736
+ }
21737
+ this.pattern = pattern;
21738
+ this.options = options;
21739
+ const { nocase = false, noext = false, normalize: n } = options;
21740
+ this.#form = n === true ? 'NFC' : n || undefined;
21741
+ // a pattern that is only an empty extglob is a literal string,
21742
+ // except for `!()`, which matches any non-empty string
21743
+ const literal = /^[?*+@]\(\)$/.test(pattern);
21744
+ const toks = tokenize(pattern, nocase, noext || literal, this.#form);
21745
+ let [items] = build(toks, pair(toks), 0, toks.length, true);
21746
+ if (this.#form)
21747
+ items = normalize(items, this.#form);
21748
+ const [start, size] = compile(items, nocase);
21749
+ this.#start = start;
21750
+ this.#size = size;
21751
+ if (items.every(it => it.t === 'lit')) {
21752
+ this.#literal = items.map(it => it.ch).join('');
21753
+ this.#literalFold = nocase;
21754
+ }
21755
+ else {
21756
+ const lit = (it) => it.t !== 'lit' ? undefined
21757
+ : nocase ? { ch: it.ch, re: foldRe(it.ch) }
21758
+ : { ch: it.ch };
21759
+ for (const it of items) {
21760
+ const l = lit(it);
21761
+ if (!l)
21762
+ break;
21763
+ this.#prefix.push(l);
21865
21764
  }
21866
- if (c === '*') {
21867
- if (inStar)
21868
- continue;
21869
- inStar = true;
21870
- re += noEmpty && /^[*]+$/.test(glob) ? starNoEmpty : star$1;
21871
- hasMagic = true;
21872
- continue;
21765
+ for (let j = items.length - 1; j >= 0; j--) {
21766
+ const l = lit(items[j]);
21767
+ if (!l)
21768
+ break;
21769
+ this.#suffix.unshift(l);
21873
21770
  }
21874
- else {
21875
- inStar = false;
21771
+ this.#affix = !!(this.#prefix.length || this.#suffix.length);
21772
+ if (!nocase) {
21773
+ this.#prefixStr = this.#prefix.map(l => l.ch).join('');
21774
+ this.#suffixStr = this.#suffix.map(l => l.ch).join('');
21876
21775
  }
21877
- if (c === '\\') {
21878
- if (i === glob.length - 1) {
21879
- re += '\\\\';
21880
- }
21881
- else {
21882
- escaping = true;
21776
+ this.#linear = linear(this.#start)?.slice(this.#prefix.length, -this.#suffix.length || undefined);
21777
+ this.#wild = !!this.#linear?.every(n => n.t === 'star' || n.t === 'any');
21778
+ const [min, max] = lengths(items);
21779
+ this.#min = min;
21780
+ this.#max = max;
21781
+ }
21782
+ }
21783
+ test(s) {
21784
+ // The `/` check uses the string as given, so with normalization it
21785
+ // is a separate step. Otherwise, one scan finds both a `/` and the
21786
+ // surrogate pairs that stop the string being indexed by code point.
21787
+ if (this.#form) {
21788
+ noSlash(s);
21789
+ return this.#test(s.normalize(this.#form));
21790
+ }
21791
+ if (slashOrSurrogate.test(s)) {
21792
+ noSlash(s);
21793
+ return this.#test(s, [...s]);
21794
+ }
21795
+ return this.#test(s, s);
21796
+ }
21797
+ /**
21798
+ * The same as {@link Matcher#test}, but the string is not checked
21799
+ * for `/` chars, so the result for a string that has one is not
21800
+ * defined. Only use it for a string that is known to be one path
21801
+ * portion, such as a part of a path that was split on `/`. It can
21802
+ * be faster, because some patterns do not need to look at each char
21803
+ * of the string.
21804
+ */
21805
+ testUnchecked(s) {
21806
+ return this.#test(this.#form ? s.normalize(this.#form) : s);
21807
+ }
21808
+ // Test a string that has no `/`. `chars` indexes it by code point.
21809
+ // If it is not given, it is only found if a step needs it.
21810
+ #test(s, chars) {
21811
+ if (this.#literal !== undefined) {
21812
+ return this.#literalFold ?
21813
+ (this.#literalRe ??= newFoldRe(this.#literal)).test(s)
21814
+ : s === this.#literal;
21815
+ }
21816
+ // the length check means that the prefix and suffix do not
21817
+ // overlap. Without the chars, the length in code points is from
21818
+ // half of the UTF-16 length (if each one is a surrogate pair) to
21819
+ // all of it, which is often enough to know.
21820
+ const min = this.#min;
21821
+ const max = this.#max;
21822
+ if (chars === undefined) {
21823
+ const most = s.length;
21824
+ const least = (most + 1) >> 1;
21825
+ if (most < min || least > max)
21826
+ return false;
21827
+ if (least < min || most > max)
21828
+ chars = codePoints(s);
21829
+ }
21830
+ if (chars !== undefined &&
21831
+ (chars.length < min || chars.length > max)) {
21832
+ return false;
21833
+ }
21834
+ const prefix = this.#prefix.length;
21835
+ if (this.#affix) {
21836
+ if (this.#prefixStr !== undefined && this.#suffixStr !== undefined) {
21837
+ if (!s.startsWith(this.#prefixStr) ||
21838
+ !s.endsWith(this.#suffixStr)) {
21839
+ return false;
21883
21840
  }
21884
- continue;
21885
21841
  }
21886
- if (c === '[') {
21887
- const [src, needUflag, consumed, magic] = parseClass(glob, i);
21888
- if (consumed) {
21889
- re += src;
21890
- uflag = uflag || needUflag;
21891
- i += consumed - 1;
21892
- hasMagic = hasMagic || magic;
21893
- continue;
21842
+ else {
21843
+ chars ??= codePoints(s);
21844
+ const end = chars.length - this.#suffix.length;
21845
+ if (!hasAffix(this.#prefix, chars, 0) ||
21846
+ !hasAffix(this.#suffix, chars, end)) {
21847
+ return false;
21894
21848
  }
21895
21849
  }
21896
- if (c === '?') {
21897
- re += qmark$1;
21898
- hasMagic = true;
21899
- continue;
21850
+ }
21851
+ const dotStart = !this.options.dot && s.startsWith('.');
21852
+ if (this.#linear) {
21853
+ // `.` and `..` only match literal dots, even with `dot:true`
21854
+ if (s === '.' || s === '..')
21855
+ return false;
21856
+ // only a literal can match the leading dot. A `*` can not
21857
+ // start a dotfile, even if it matches nothing.
21858
+ if (dotStart && !prefix)
21859
+ return false;
21860
+ if (this.#wild)
21861
+ return true;
21862
+ chars ??= codePoints(s);
21863
+ const end = chars.length - this.#suffix.length;
21864
+ return testLinear(this.#linear, chars, prefix, end);
21865
+ }
21866
+ chars ??= codePoints(s);
21867
+ if (s === '.' || s === '..') {
21868
+ this.#marks ??= marks(this.#size);
21869
+ const sim = (this.#dotsSim ??= new Sim(true, false, this.#marks));
21870
+ const start = sim.state();
21871
+ sim.add(start, this.#start, 0, true);
21872
+ let state = sim.done(start);
21873
+ for (let i = 0; i < chars.length; i++) {
21874
+ state = sim.step(state, chars[i], i, true);
21900
21875
  }
21901
- re += regExpEscape$1(c);
21876
+ return state.match;
21877
+ }
21878
+ this.#marks ??= marks(this.#size);
21879
+ const dfa = (this.#dfa ??= new Dfa(this.#start, this.#marks));
21880
+ let d = dfa.start(dotStart);
21881
+ // read char codes from the string, so that a one-char string is
21882
+ // only made for a cache miss
21883
+ const str = chars === s;
21884
+ for (let i = 0; i < chars.length; i++) {
21885
+ if (d.dead)
21886
+ return false;
21887
+ const code = str ? s.charCodeAt(i) : chars[i].charCodeAt(0);
21888
+ d = d.ascii?.[code] ?? dfa.next(d, chars[i]);
21902
21889
  }
21903
- return [re, unescape(glob), !!hasMagic, uflag];
21890
+ return d.match;
21891
+ }
21892
+ /** a readable listing of the state machine */
21893
+ toString() {
21894
+ return show(this.#start);
21904
21895
  }
21905
21896
  }
21906
- _a = AST;
21897
+
21898
+ const MAX_PATTERN_LENGTH = 1024 * 64;
21899
+ const assertValidPattern = (pattern) => {
21900
+ if (typeof pattern !== 'string') {
21901
+ throw new TypeError('invalid pattern');
21902
+ }
21903
+ if (pattern.length > MAX_PATTERN_LENGTH) {
21904
+ throw new TypeError('pattern is too long');
21905
+ }
21906
+ };
21907
21907
 
21908
21908
  /**
21909
21909
  * Escape all magic characters in a glob pattern.
@@ -21931,71 +21931,72 @@ const escape = (s, { windowsPathsNoEscape = false, magicalBraces = false, } = {}
21931
21931
  : s.replace(/[?*()[\]\\]/g, '\\$&');
21932
21932
  };
21933
21933
 
21934
+ /**
21935
+ * Un-escape a string that has been escaped with {@link escape}.
21936
+ *
21937
+ * If the {@link MinimatchOptions.windowsPathsNoEscape} option is used, then
21938
+ * square-bracket escapes are removed, but not backslash escapes.
21939
+ *
21940
+ * For example, it will turn the string `'[*]'` into `*`, but it will not
21941
+ * turn `'\\*'` into `'*'`, because `\` is a path separator in
21942
+ * `windowsPathsNoEscape` mode.
21943
+ *
21944
+ * When `windowsPathsNoEscape` is not set, then both square-bracket escapes and
21945
+ * backslash escapes are removed.
21946
+ *
21947
+ * Slashes (and backslashes in `windowsPathsNoEscape` mode) cannot be escaped
21948
+ * or unescaped.
21949
+ *
21950
+ * When `magicalBraces` is not set, escapes of braces (`{` and `}`) will not be
21951
+ * unescaped.
21952
+ */
21953
+ const unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true, } = {}) => {
21954
+ if (magicalBraces) {
21955
+ return windowsPathsNoEscape ?
21956
+ s.replace(/\[([^/\\])\]/g, '$1')
21957
+ : s
21958
+ .replace(/((?!\\).|^)\[([^/\\])\]/g, '$1$2')
21959
+ .replace(/\\([^/])/g, '$1');
21960
+ }
21961
+ return windowsPathsNoEscape ?
21962
+ s.replace(/\[([^/\\{}])\]/g, '$1')
21963
+ : s
21964
+ .replace(/((?!\\).|^)\[([^/\\{}])\]/g, '$1$2')
21965
+ .replace(/\\([^/{}])/g, '$1');
21966
+ };
21967
+
21934
21968
  const minimatch = (p, pattern, options = {}) => {
21935
21969
  assertValidPattern(pattern);
21936
21970
  // shortcut: comments match nothing.
21937
21971
  if (!options.nocomment && pattern.charAt(0) === '#') {
21938
21972
  return false;
21939
21973
  }
21940
- return new Minimatch(pattern, options).match(p);
21941
- };
21942
- // Optimized checking for the most common glob patterns.
21943
- const starDotExtRE = /^\*+([^+@!?*[(]*)$/;
21944
- const starDotExtTest = (ext) => (f) => !f.startsWith('.') && f.endsWith(ext);
21945
- const starDotExtTestDot = (ext) => (f) => f.endsWith(ext);
21946
- const starDotExtTestNocase = (ext) => {
21947
- ext = ext.toLowerCase();
21948
- return (f) => !f.startsWith('.') && f.toLowerCase().endsWith(ext);
21949
- };
21950
- const starDotExtTestNocaseDot = (ext) => {
21951
- ext = ext.toLowerCase();
21952
- return (f) => f.toLowerCase().endsWith(ext);
21953
- };
21954
- const starDotStarRE = /^\*+\.\*+$/;
21955
- const starDotStarTest = (f) => !f.startsWith('.') && f.includes('.');
21956
- const starDotStarTestDot = (f) => f !== '.' && f !== '..' && f.includes('.');
21957
- const dotStarRE = /^\.\*+$/;
21958
- const dotStarTest = (f) => f !== '.' && f !== '..' && f.startsWith('.');
21959
- const starRE = /^\*+$/;
21960
- const starTest = (f) => f.length !== 0 && !f.startsWith('.');
21961
- const starTestDot = (f) => f.length !== 0 && f !== '.' && f !== '..';
21962
- const qmarksRE = /^\?+([^+@!?*[(]*)?$/;
21963
- const qmarksTestNocase = ([$0, ext = '']) => {
21964
- const noext = qmarksTestNoExt([$0]);
21965
- if (!ext)
21966
- return noext;
21967
- ext = ext.toLowerCase();
21968
- return (f) => noext(f) && f.toLowerCase().endsWith(ext);
21969
- };
21970
- const qmarksTestNocaseDot = ([$0, ext = '']) => {
21971
- const noext = qmarksTestNoExtDot([$0]);
21972
- if (!ext)
21973
- return noext;
21974
- ext = ext.toLowerCase();
21975
- return (f) => noext(f) && f.toLowerCase().endsWith(ext);
21976
- };
21977
- const qmarksTestDot = ([$0, ext = '']) => {
21978
- const noext = qmarksTestNoExtDot([$0]);
21979
- return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
21980
- };
21981
- const qmarksTest = ([$0, ext = '']) => {
21982
- const noext = qmarksTestNoExt([$0]);
21983
- return !ext ? noext : (f) => noext(f) && f.endsWith(ext);
21974
+ return cachedMinimatch(pattern, options).match(p);
21984
21975
  };
21985
- const qmarksTestNoExt = ([$0]) => {
21986
- const len = $0.length;
21987
- return (f) => f.length === len && !f.startsWith('.');
21988
- };
21989
- const qmarksTestNoExtDot = ([$0]) => {
21990
- const len = $0.length;
21991
- return (f) => f.length === len && f !== '.' && f !== '..';
21976
+ // The Minimatch objects that minimatch() used most recently. A Map
21977
+ // keeps its keys in the order they were set, so the first key is the
21978
+ // one used least recently.
21979
+ const cache = new Map();
21980
+ const cacheMax = 256;
21981
+ const cachedMinimatch = (pattern, options) => {
21982
+ const key = pattern + '\0' + JSON.stringify(options);
21983
+ let mm = cache.get(key);
21984
+ if (mm) {
21985
+ cache.delete(key);
21986
+ }
21987
+ else {
21988
+ // a copy, so a later change to the options can not change it
21989
+ mm = new Minimatch(pattern, { ...options });
21990
+ if (cache.size >= cacheMax) {
21991
+ cache.delete(cache.keys().next().value);
21992
+ }
21993
+ }
21994
+ cache.set(key, mm);
21995
+ return mm;
21992
21996
  };
21993
21997
  /* c8 ignore start */
21994
21998
  const defaultPlatform = (typeof process === 'object' && process ?
21995
- (typeof process.env === 'object' &&
21996
- process.env &&
21997
- process.env.__MINIMATCH_TESTING_PLATFORM__) ||
21998
- process.platform
21999
+ process.env?.__MINIMATCH_TESTING_PLATFORM__ || process.platform
21999
22000
  : 'posix');
22000
22001
  const path = {
22001
22002
  win32: { sep: '\\' },
@@ -22006,18 +22007,6 @@ const sep = defaultPlatform === 'win32' ? path.win32.sep : path.posix.sep;
22006
22007
  minimatch.sep = sep;
22007
22008
  const GLOBSTAR = Symbol('globstar **');
22008
22009
  minimatch.GLOBSTAR = GLOBSTAR;
22009
- // any single thing other than /
22010
- // don't need to escape / when using new RegExp()
22011
- const qmark = '[^/]';
22012
- // * => any number of characters
22013
- const star = qmark + '*?';
22014
- // ** when dots are allowed. Anything goes, except .. and .
22015
- // not (^ or / followed by one or two dots followed by $ or /),
22016
- // followed by anything, any number of times.
22017
- const twoStarDot = '(?:(?!(?:\\/|^)(?:\\.{1,2})($|\\/)).)*?';
22018
- // not a ^ or / followed by a dot,
22019
- // followed by anything, any number of times.
22020
- const twoStarNoDot = '(?:(?!(?:\\/|^)\\.).)*?';
22021
22010
  const filter = (pattern, options = {}) => (p) => minimatch(p, pattern, options);
22022
22011
  minimatch.filter = filter;
22023
22012
  const ext = (a, b = {}) => Object.assign({}, a, b);
@@ -22036,21 +22025,10 @@ const defaults = (def) => {
22036
22025
  return orig.defaults(ext(def, options)).Minimatch;
22037
22026
  }
22038
22027
  },
22039
- AST: class AST extends orig.AST {
22040
- /* c8 ignore start */
22041
- constructor(type, parent, options = {}) {
22042
- super(type, parent, ext(def, options));
22043
- }
22044
- /* c8 ignore stop */
22045
- static fromGlob(pattern, options = {}) {
22046
- return orig.AST.fromGlob(pattern, ext(def, options));
22047
- }
22048
- },
22049
22028
  unescape: (s, options = {}) => orig.unescape(s, ext(def, options)),
22050
22029
  escape: (s, options = {}) => orig.escape(s, ext(def, options)),
22051
22030
  filter: (pattern, options = {}) => orig.filter(pattern, ext(def, options)),
22052
22031
  defaults: (options) => orig.defaults(ext(def, options)),
22053
- makeRe: (pattern, options = {}) => orig.makeRe(pattern, ext(def, options)),
22054
22032
  braceExpand: (pattern, options = {}) => orig.braceExpand(pattern, ext(def, options)),
22055
22033
  match: (list, pattern, options = {}) => orig.match(list, pattern, ext(def, options)),
22056
22034
  sep: orig.sep,
@@ -22079,19 +22057,6 @@ const braceExpand = (pattern, options = {}) => {
22079
22057
  return expand(pattern, { max: options.braceExpandMax });
22080
22058
  };
22081
22059
  minimatch.braceExpand = braceExpand;
22082
- // parse a component of the expanded set.
22083
- // At this point, no pattern may contain "/" in it
22084
- // so we're going to return a 2d array, where each entry is the full
22085
- // pattern, split on '/', and then turned into a regular expression.
22086
- // A regexp is made at the end which joins each array with an
22087
- // escaped /, and another full one which joins each regexp with |.
22088
- //
22089
- // Following the lead of Bash 4.1, note that "**" only has special meaning
22090
- // when it is the *only* thing in a path portion. Otherwise, any series
22091
- // of * is equivalent to a single *. Globstar behavior is enabled by
22092
- // default, and can be disabled by setting options.noglobstar.
22093
- const makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
22094
- minimatch.makeRe = makeRe;
22095
22060
  const match = (list, pattern, options = {}) => {
22096
22061
  const mm = new Minimatch(pattern, options);
22097
22062
  list = list.filter(f => mm.match(f));
@@ -22101,9 +22066,201 @@ const match = (list, pattern, options = {}) => {
22101
22066
  return list;
22102
22067
  };
22103
22068
  minimatch.match = match;
22104
- // replace stuff like \* with *
22105
22069
  const globMagic = /[?*]|[+@!]\(.*?\)|\[|\]/;
22106
- const regExpEscape = (s) => s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
22070
+ // Index of the `]` that closes the class that starts at `start`,
22071
+ // or -1 if it is not closed. Posix classes such as `[:alpha:]` are
22072
+ // not skipped, so this can find a `]` before the real end. That
22073
+ // only makes a class look less like a literal.
22074
+ const classEnd = (pattern, start) => {
22075
+ let i = start + 1;
22076
+ if (pattern.charAt(i) === '!' || pattern.charAt(i) === '^')
22077
+ i++;
22078
+ let sawStart = false;
22079
+ for (; i < pattern.length; i++) {
22080
+ const c = pattern.charAt(i);
22081
+ if (c === ']' && sawStart)
22082
+ return i;
22083
+ sawStart = true;
22084
+ if (c === '\\')
22085
+ i++;
22086
+ }
22087
+ return -1;
22088
+ };
22089
+ // If a path portion pattern has no magic, return the string it
22090
+ // matches. Otherwise, return undefined. This can report magic for
22091
+ // a pattern that is in fact literal (eg, `[]`), but never the
22092
+ // reverse, so a string result is always safe to compare directly.
22093
+ const literal = (pattern, noext) => {
22094
+ if (!noext && (pattern === '+()' || pattern === '@()'))
22095
+ return pattern;
22096
+ let lit = '';
22097
+ for (let i = 0; i < pattern.length; i++) {
22098
+ const c = pattern.charAt(i);
22099
+ if (c === '\\') {
22100
+ // a trailing \ is a literal \
22101
+ lit += pattern.charAt(++i) || c;
22102
+ }
22103
+ else if (c === '*' || c === '?') {
22104
+ return undefined;
22105
+ }
22106
+ else if (c === '[') {
22107
+ const close = classEnd(pattern, i);
22108
+ if (close === -1) {
22109
+ // not closed, so the [ is a literal
22110
+ lit += c;
22111
+ continue;
22112
+ }
22113
+ // a class of one char, such as `[*]` or `[\b]`, is a literal
22114
+ let body = pattern.slice(i + 1, close);
22115
+ if (body.startsWith('\\'))
22116
+ body = body.slice(1);
22117
+ if ([...body].length !== 1 || '!^'.includes(pattern.charAt(i + 1))) {
22118
+ return undefined;
22119
+ }
22120
+ lit += body;
22121
+ i = close;
22122
+ }
22123
+ else if (!noext &&
22124
+ '+@!'.includes(c) &&
22125
+ pattern.charAt(i + 1) === '(' &&
22126
+ pattern.includes(')', i + 2)) {
22127
+ return undefined;
22128
+ }
22129
+ else {
22130
+ lit += c;
22131
+ }
22132
+ }
22133
+ return lit;
22134
+ };
22135
+ // True if the portion pattern has the same meaning as an alternative
22136
+ // inside `@(...)`. It must not have a `|` or `)` that is not in a
22137
+ // closed extglob (those are literal at the top level), an extglob
22138
+ // that is not closed (the `)` of the `@(...)` would close it), or a
22139
+ // trailing `\` (it would escape the next `|`). A pattern that is
22140
+ // only an empty extglob, such as `?()`, is literal on its own, but
22141
+ // not in `@(...)`. Posix classes such as `[:alpha:]` can hide a `]`
22142
+ // from classEnd, so patterns with them are not merged.
22143
+ const extSafe = (pattern) => {
22144
+ if (/^[?*+@]\(\)$/.test(pattern) || pattern.includes('[:'))
22145
+ return false;
22146
+ let depth = 0;
22147
+ for (let i = 0; i < pattern.length; i++) {
22148
+ const c = pattern.charAt(i);
22149
+ if (c === '\\') {
22150
+ if (++i === pattern.length)
22151
+ return false;
22152
+ }
22153
+ else if (c === '[') {
22154
+ const close = classEnd(pattern, i);
22155
+ if (close !== -1)
22156
+ i = close;
22157
+ }
22158
+ else if ('+@!?*'.includes(c) && pattern.charAt(i + 1) === '(') {
22159
+ depth++;
22160
+ i++;
22161
+ }
22162
+ else if (c === ')') {
22163
+ if (!depth--)
22164
+ return false;
22165
+ }
22166
+ else if (c === '|' && !depth) {
22167
+ return false;
22168
+ }
22169
+ }
22170
+ return !depth;
22171
+ };
22172
+ // The last path that Minimatch#match split, and its parts. A path is
22173
+ // often tested against many patterns in a row (such as a list of
22174
+ // ignore patterns), so this saves splitting it again. The parts are
22175
+ // never changed, so they can be shared.
22176
+ let lastSplit;
22177
+ // The last path that Minimatch#match checked before splitting it. If
22178
+ // the same path is checked again, it is being tested against many
22179
+ // patterns, so it is split and the parts are kept for the others.
22180
+ let lastChecked;
22181
+ // true if a pattern portion that is not ** matches a path portion.
22182
+ // An empty path portion only matches an empty pattern portion. A path
22183
+ // portion never has a `/`, so the Matcher does not need to check it.
22184
+ const portionMatch = (p, f) => typeof p === 'string' ? f === p : f !== '' && p.testUnchecked(f);
22185
+ // Where the last portion of a row matches in a path: the start of the
22186
+ // last portion of the path, or of the one before a trailing `/`. -1 if
22187
+ // it matches neither.
22188
+ const endIndex = (p, last, lastStart, prev, prevStart) => portionMatch(p, last) ? lastStart
22189
+ : prev !== undefined && portionMatch(p, prev) ? prevStart
22190
+ : -1;
22191
+ const isDots = (p) => p === '.' || p === '..';
22192
+ // a Windows drive letter portion, like `c:`
22193
+ const isDrive = (p) => typeof p === 'string' && /^[a-z]:$/i.test(p);
22194
+ // true for the `UNC` portion of a long UNC root, //?/UNC/host/share
22195
+ const isLongUNC = (p) => typeof p === 'string' && /^unc$/i.test(p);
22196
+ // The index of the host in a row or split path that starts with a
22197
+ // Windows UNC root: 2 for `//host/share`, and 4 for
22198
+ // `//?/UNC/host/share`. Otherwise -1. `//?/c:` is not one.
22199
+ const uncHost = (p) => {
22200
+ if (p[0] !== '' || p[1] !== '')
22201
+ return -1;
22202
+ const h = p[2] !== '?' ? 2
22203
+ : isLongUNC(p[3]) ? 4
22204
+ : -1;
22205
+ const host = p[h];
22206
+ const share = p[h + 1];
22207
+ return (h !== -1 &&
22208
+ host !== undefined &&
22209
+ host !== '' &&
22210
+ host !== GLOBSTAR &&
22211
+ share !== undefined &&
22212
+ share !== '' &&
22213
+ share !== GLOBSTAR) ?
22214
+ h
22215
+ : -1;
22216
+ };
22217
+ // true if ** can walk the path portion: not `.` or `..`, and not a
22218
+ // dotfile unless `dot` is set
22219
+ const walkable = (p, dot) => !isDots(p) && (dot || !p.startsWith('.'));
22220
+ // True if a portion of the path from `start` to `end` can not be
22221
+ // walked by **: one that starts with `.`, or with `dot`, one that is
22222
+ // `.` or `..`. `start` is the start of a portion, and `end` is the end
22223
+ // of the path, or just after a separator.
22224
+ const dotPortion = (f, start, end, dot, sep) => {
22225
+ for (let k = start; k < end;) {
22226
+ if (f.charCodeAt(k) === 46) {
22227
+ if (!dot)
22228
+ return true;
22229
+ const n = f.charCodeAt(k + 1) === 46 ? k + 2 : k + 1;
22230
+ if (n >= end || isSep(f.charCodeAt(n), sep))
22231
+ return true;
22232
+ }
22233
+ // the start of the next portion, after any run of separators
22234
+ const s = nextSep(f, k, sep);
22235
+ if (s === -1)
22236
+ return false;
22237
+ k = s + 1;
22238
+ while (k < end && isSep(f.charCodeAt(k), sep))
22239
+ k++;
22240
+ }
22241
+ return false;
22242
+ };
22243
+ const isSep = (c, sep) => sep === '/' ? c === 47
22244
+ : sep === '\\' ? c === 92
22245
+ : c === 47 || c === 92;
22246
+ // the index of the first separator at or after `from`, or -1
22247
+ const nextSep = (f, from, sep) => {
22248
+ if (sep !== 'both')
22249
+ return f.indexOf(sep, from);
22250
+ const s = f.indexOf('/', from);
22251
+ const b = f.indexOf('\\', from);
22252
+ return (s === -1 ? b
22253
+ : b === -1 ? s
22254
+ : Math.min(s, b));
22255
+ };
22256
+ // the index of the last separator at or before `from`, or -1
22257
+ const lastSep = (f, from, sep) => sep !== 'both' ?
22258
+ f.lastIndexOf(sep, from)
22259
+ : Math.max(f.lastIndexOf('/', from), f.lastIndexOf('\\', from));
22260
+ // What parse() returns in place of a Matcher while make() builds and
22261
+ // merges the rows. Most of the Matchers in rows that are merged would
22262
+ // never be used, so they are only made for the rows that are left.
22263
+ const PENDING = {};
22107
22264
  class Minimatch {
22108
22265
  options;
22109
22266
  set;
@@ -22122,7 +22279,27 @@ class Minimatch {
22122
22279
  platform;
22123
22280
  windowsNoMagicRoot;
22124
22281
  maxGlobstarRecursion;
22125
- regexp;
22282
+ // the path can be checked against the last portion, and the string
22283
+ // portions, of each row before it is split. See #canMatch.
22284
+ #endCheck;
22285
+ #literalCheck;
22286
+ // level 2 resolves `.` and `..` portions in the path
22287
+ #level2;
22288
+ // the unicode normalization form, if any
22289
+ #form;
22290
+ // the string portions of each row in the set
22291
+ #literals = [];
22292
+ // the rows that #canMatch can match without splitting the path
22293
+ #shapes = [];
22294
+ // the rows whose last portion can be checked against the path's
22295
+ #endRows = [];
22296
+ // brace expansion can make the same portion in many rows, so they
22297
+ // share one Matcher, and the states that it caches
22298
+ #matchers = new Map();
22299
+ // parse() returns PENDING in place of a Matcher
22300
+ #pending = false;
22301
+ // the GlobstarParts of each pattern row, by start index and partial
22302
+ #globstarParts = new WeakMap();
22126
22303
  constructor(pattern, options = {}) {
22127
22304
  assertValidPattern(pattern);
22128
22305
  options = options || {};
@@ -22139,7 +22316,6 @@ class Minimatch {
22139
22316
  this.pattern = this.pattern.replace(/\\/g, '/');
22140
22317
  }
22141
22318
  this.preserveMultipleSlashes = !!options.preserveMultipleSlashes;
22142
- this.regexp = null;
22143
22319
  this.negate = false;
22144
22320
  this.nonegate = !!options.nonegate;
22145
22321
  this.comment = false;
@@ -22153,8 +22329,81 @@ class Minimatch {
22153
22329
  this.globSet = [];
22154
22330
  this.globParts = [];
22155
22331
  this.set = [];
22156
- // make the set of regexps etc.
22332
+ this.#endCheck = !this.preserveMultipleSlashes;
22333
+ this.#level2 = (options.optimizationLevel ?? 1) >= 2;
22334
+ const n = options.normalize;
22335
+ this.#form = n === true ? 'NFC' : n || undefined;
22336
+ // make the set of Matchers etc.
22157
22337
  this.make();
22338
+ // On Windows, the portions of a row up to its drive letter or UNC
22339
+ // share are skipped or compared without case, if the path has one
22340
+ // too. So the checks only use the portions after it.
22341
+ const drives = this.set.map(row => this.#rootIndex(row));
22342
+ this.#endRows = this.set.map((row, r) => this.#endCheck && row.length - 1 > drives[r]);
22343
+ // with the end check, the last portion is already checked
22344
+ this.#literals = this.set.map((row, r) => {
22345
+ const end = this.#endRows[r] ? row.length - 1 : row.length;
22346
+ const lits = row
22347
+ .slice(drives[r] + 1, end)
22348
+ .filter((p) => typeof p === 'string' && !!p);
22349
+ return [...new Set(lits)];
22350
+ });
22351
+ this.#literalCheck = this.#literals.some(l => l.length);
22352
+ this.#shapes = this.set.map((row, r) => {
22353
+ const [p0, p1, p2] = row;
22354
+ if (this.#endRows[r] && row.length === 2) {
22355
+ if (p0 === GLOBSTAR && p1 !== GLOBSTAR)
22356
+ return 'tail';
22357
+ }
22358
+ // the basename of a path that is split on / is its last portion,
22359
+ // or the one before a trailing /, the same as the end check
22360
+ if (this.#endRows[r] &&
22361
+ options.matchBase &&
22362
+ row.length === 1 &&
22363
+ p0 !== GLOBSTAR &&
22364
+ p0 !== '') {
22365
+ return 'base';
22366
+ }
22367
+ // the portion must not be a root, or '', which can be a
22368
+ // different portion on Windows (where //x paths get an extra '')
22369
+ if (!this.#endCheck || drives[r] !== -1)
22370
+ return undefined;
22371
+ if (row.length === 3 &&
22372
+ p0 === GLOBSTAR &&
22373
+ p2 === GLOBSTAR &&
22374
+ p1 !== GLOBSTAR &&
22375
+ p1 !== '' &&
22376
+ // the portion between is only matched below this limit
22377
+ this.maxGlobstarRecursion > 0) {
22378
+ return 'mid';
22379
+ }
22380
+ if (row.length === 2 &&
22381
+ p1 === GLOBSTAR &&
22382
+ p0 !== GLOBSTAR &&
22383
+ p0 !== '') {
22384
+ return 'head';
22385
+ }
22386
+ return undefined;
22387
+ });
22388
+ }
22389
+ // a string portion of a pattern, normalized if the option is set
22390
+ #norm(p) {
22391
+ return this.#form ? p.normalize(this.#form) : p;
22392
+ }
22393
+ // On Windows, the index of the last root portion of a row, as
22394
+ // #matchRow finds it: 0 for `c:/...`, 3 for `//?/c:/...` and
22395
+ // `//host/share/...`, and 5 for `//?/UNC/host/share/...`. Otherwise
22396
+ // -1.
22397
+ #rootIndex(row) {
22398
+ if (!this.isWindows)
22399
+ return -1;
22400
+ if (isDrive(row[0]))
22401
+ return 0;
22402
+ const uncDrive = row[0] === '' && row[1] === '' && row[2] === '?' && isDrive(row[3]);
22403
+ if (uncDrive)
22404
+ return 3;
22405
+ const h = uncHost(row);
22406
+ return h === -1 ? -1 : h + 1;
22158
22407
  }
22159
22408
  hasMagic() {
22160
22409
  if (this.options.magicalBraces && this.set.length > 1) {
@@ -22168,7 +22417,6 @@ class Minimatch {
22168
22417
  }
22169
22418
  return false;
22170
22419
  }
22171
- debug(..._) { }
22172
22420
  make() {
22173
22421
  const pattern = this.pattern;
22174
22422
  const options = this.options;
@@ -22185,45 +22433,44 @@ class Minimatch {
22185
22433
  this.parseNegate();
22186
22434
  // step 2: expand braces
22187
22435
  this.globSet = [...new Set(this.braceExpand())];
22188
- if (options.debug) {
22189
- //oxlint-disable-next-line no-console
22190
- this.debug = (...args) => console.error(...args);
22191
- }
22192
- this.debug(this.pattern, this.globSet);
22193
22436
  // step 3: now we have a set, so turn each one into a series of
22194
22437
  // path-portion matching patterns.
22195
- // These will be regexps, except in the case of "**", which is
22438
+ // These will be Matchers, except in the case of "**", which is
22196
22439
  // set to the GLOBSTAR object for globstar behavior,
22197
22440
  // and will not contain any / characters
22198
22441
  //
22199
22442
  // First, we preprocess to make the glob pattern sets a bit simpler
22200
22443
  // and deduped. There are some perf-killing patterns that can cause
22201
22444
  // problems with a glob walk, but we can simplify them down a bit.
22202
- const rawGlobParts = this.globSet.map(s => this.slashSplit(s));
22445
+ const rawGlobParts = this.globSet.map(s => this.#slashSplit(s));
22203
22446
  this.globParts = this.preprocess(rawGlobParts);
22204
- this.debug(this.pattern, this.globParts);
22205
- // glob --> regexps
22447
+ // glob --> Matchers
22448
+ this.#pending = true;
22206
22449
  let set = this.globParts.map((s, _, __) => {
22207
22450
  if (this.isWindows && this.windowsNoMagicRoot) {
22208
- // check if it's a drive or unc path.
22451
+ // check if it's a drive or unc path. The root of a long UNC
22452
+ // path, //?/UNC/host/share, is 6 portions.
22453
+ const n = s[2] === '?' && isLongUNC(s[3]) ? 6 : 4;
22209
22454
  const isUNC = s[0] === '' &&
22210
22455
  s[1] === '' &&
22211
22456
  (s[2] === '?' || !globMagic.test(s[2])) &&
22212
- !globMagic.test(s[3]);
22457
+ s.slice(3, n).every(p => !globMagic.test(p));
22213
22458
  const isDrive = /^[a-z]:/i.test(s[0]);
22214
22459
  if (isUNC) {
22215
22460
  return [
22216
- ...s.slice(0, 4),
22217
- ...s.slice(4).map(ss => this.parse(ss)),
22461
+ ...s.slice(0, n).map(p => this.#norm(p)),
22462
+ ...s.slice(n).map(ss => this.parse(ss)),
22218
22463
  ];
22219
22464
  }
22220
22465
  else if (isDrive) {
22221
- return [s[0], ...s.slice(1).map(ss => this.parse(ss))];
22466
+ return [
22467
+ this.#norm(s[0]),
22468
+ ...s.slice(1).map(ss => this.parse(ss)),
22469
+ ];
22222
22470
  }
22223
22471
  }
22224
22472
  return s.map(ss => this.parse(ss));
22225
22473
  });
22226
- this.debug(this.pattern, set);
22227
22474
  // filter out everything that didn't compile properly.
22228
22475
  this.set = set.filter(s => s.indexOf(false) === -1);
22229
22476
  // do not treat the ? in UNC paths as magic
@@ -22233,13 +22480,70 @@ class Minimatch {
22233
22480
  if (p[0] === '' &&
22234
22481
  p[1] === '' &&
22235
22482
  this.globParts[i][2] === '?' &&
22236
- typeof p[3] === 'string' &&
22237
- /^[a-z]:$/i.test(p[3])) {
22483
+ (isDrive(p[3]) || isLongUNC(this.globParts[i][3]))) {
22484
+ if (isLongUNC(this.globParts[i][3]))
22485
+ p[3] = 'UNC';
22238
22486
  p[2] = '?';
22239
22487
  }
22240
22488
  }
22241
22489
  }
22242
- this.debug(this.pattern, this.set);
22490
+ // with noext, `@(...)` would be literal
22491
+ if (!options.noext)
22492
+ this.mergeRows();
22493
+ this.#pending = false;
22494
+ this.set.forEach((row, i) => {
22495
+ for (let k = 0; k < row.length; k++) {
22496
+ if (row[k] === PENDING) {
22497
+ row[k] = this.parse(this.globParts[i][k]);
22498
+ }
22499
+ }
22500
+ });
22501
+ }
22502
+ // Merge rows that only differ in one portion, where each of them
22503
+ // is a Matcher, into one row that matches any of them with
22504
+ // `@(a|b|...)`. Then a path is only walked once for all of them,
22505
+ // for example with `**/*.{js,ts}`.
22506
+ mergeRows() {
22507
+ let merged = true;
22508
+ while (merged) {
22509
+ merged = false;
22510
+ const len = Math.max(0, ...this.set.map(r => r.length));
22511
+ for (let k = 0; k < len; k++) {
22512
+ // the rows that are the same other than at portion k
22513
+ const groups = new Map();
22514
+ this.set.forEach((row, i) => {
22515
+ if (!(row[k] instanceof Matcher || row[k] === PENDING) ||
22516
+ !extSafe(this.globParts[i][k])) {
22517
+ return;
22518
+ }
22519
+ const parts = [...this.globParts[i]];
22520
+ parts[k] = '';
22521
+ const key = `${row.length}/${parts.join('/')}`;
22522
+ const g = groups.get(key);
22523
+ if (g)
22524
+ g.push(i);
22525
+ else
22526
+ groups.set(key, [i]);
22527
+ });
22528
+ const drop = new Set();
22529
+ for (const g of groups.values()) {
22530
+ if (g.length < 2)
22531
+ continue;
22532
+ const [first, ...rest] = g;
22533
+ const alts = g.map(i => this.globParts[i][k]);
22534
+ const glob = `@(${alts.join('|')})`;
22535
+ this.globParts[first][k] = glob;
22536
+ this.set[first][k] = this.parse(glob);
22537
+ for (const i of rest)
22538
+ drop.add(i);
22539
+ }
22540
+ if (drop.size) {
22541
+ merged = true;
22542
+ this.set = this.set.filter((_, i) => !drop.has(i));
22543
+ this.globParts = this.globParts.filter((_, i) => !drop.has(i));
22544
+ }
22545
+ }
22546
+ }
22243
22547
  }
22244
22548
  // various transforms to equivalent pattern sets that are
22245
22549
  // faster to process in a filesystem walk. The goal is to
@@ -22292,13 +22596,19 @@ class Minimatch {
22292
22596
  // get rid of adjascent ** and resolve .. portions
22293
22597
  levelOneOptimize(globParts) {
22294
22598
  return globParts.map(parts => {
22599
+ // .. can not remove the root of a Windows path
22600
+ const root = this.#rootIndex(parts) + 1;
22295
22601
  parts = parts.reduce((set, part) => {
22296
22602
  const prev = set[set.length - 1];
22297
22603
  if (part === '**' && prev === '**') {
22298
22604
  return set;
22299
22605
  }
22300
22606
  if (part === '..') {
22301
- if (prev && prev !== '..' && prev !== '.' && prev !== '**') {
22607
+ if (set.length > root &&
22608
+ prev &&
22609
+ prev !== '..' &&
22610
+ prev !== '.' &&
22611
+ prev !== '**') {
22302
22612
  set.pop();
22303
22613
  return set;
22304
22614
  }
@@ -22309,10 +22619,214 @@ class Minimatch {
22309
22619
  return parts.length === 0 ? [''] : parts;
22310
22620
  });
22311
22621
  }
22312
- levelTwoFileOptimize(parts) {
22313
- if (!Array.isArray(parts)) {
22314
- parts = this.slashSplit(parts);
22622
+ // Check the path against each row before it is split. 'no' if no
22623
+ // row can match, so it does not need to be split. 'yes' if a row of
22624
+ // `**` and one more portion matches. 'maybe' if it must be split to
22625
+ // know. None of this applies with partial, where the path can end
22626
+ // before the row does.
22627
+ //
22628
+ // On Windows, `\` is a separator in the path too, and the portions of
22629
+ // a row up to its drive letter or UNC share are not checked (see
22630
+ // #rootIndex).
22631
+ //
22632
+ // Each string portion of a row must be a portion of the path, so the
22633
+ // path must contain it. Level 2 can only take portions out of the
22634
+ // path, so this is still true then.
22635
+ //
22636
+ // The last portion of a row must match the last portion of the path,
22637
+ // or the one before a trailing `/` (as a/* and a/**/* match a/b/).
22638
+ // This does not apply to rows that end in **, rows that end in a
22639
+ // root, or preserveMultipleSlashes (which can leave more
22640
+ // than one '' at the end). At level 2, it does not apply if that
22641
+ // portion is `.` or `..`, which can be
22642
+ // resolved away. Level 2 only takes other portions out of the
22643
+ // middle of the path.
22644
+ //
22645
+ // A row of `**/x` then matches if the ** can walk the portions
22646
+ // before the one that x matches, the same as #matchGlobstar. At
22647
+ // level 2, this is only known if none of them are `.` or `..`, so
22648
+ // that the path does not change when it is resolved.
22649
+ #canMatch(f) {
22650
+ const sep = !this.isWindows || !f.includes('\\') ? '/'
22651
+ : f.includes('/') ? 'both'
22652
+ : '\\';
22653
+ // the portions that f.split(/\/+/) would end with, and where they
22654
+ // start in f, found when a row needs them. `ends` is 0 until they
22655
+ // are found, then 1, or -1 if they can not be used.
22656
+ let ends = this.#endCheck ? 0 : -1;
22657
+ let last = '';
22658
+ let lastStart = 0;
22659
+ let prev;
22660
+ let prevStart = 0;
22661
+ const dot = !!this.options.dot;
22662
+ // A string compare is cheaper than includes(), and a Matcher test
22663
+ // costs more, so the cheaper check goes first.
22664
+ ROWS: for (let r = 0; r < this.set.length; r++) {
22665
+ const row = this.set[r];
22666
+ const p = row[row.length - 1];
22667
+ let end = ends !== -1 && this.#endRows[r] && p !== GLOBSTAR;
22668
+ if (end && ends === 0) {
22669
+ const i = lastSep(f, f.length, sep);
22670
+ lastStart = i + 1;
22671
+ last = f.slice(lastStart);
22672
+ if (last === '' && i >= 0) {
22673
+ let j = i;
22674
+ while (j > 0 && isSep(f.charCodeAt(j - 1), sep))
22675
+ j--;
22676
+ prevStart = j === 0 ? 0 : lastSep(f, j - 1, sep) + 1;
22677
+ prev = f.slice(prevStart, j);
22678
+ }
22679
+ const resolves = this.#level2 &&
22680
+ (isDots(last) ||
22681
+ (last === '' && prev !== undefined && isDots(prev)));
22682
+ ends = resolves ? -1 : 1;
22683
+ end = !resolves;
22684
+ }
22685
+ let tailStart = -1;
22686
+ if (end && typeof p === 'string') {
22687
+ tailStart = endIndex(p, last, lastStart, prev, prevStart);
22688
+ if (tailStart === -1)
22689
+ continue;
22690
+ }
22691
+ if (this.#literalCheck) {
22692
+ for (const l of this.#literals[r]) {
22693
+ if (!f.includes(l))
22694
+ continue ROWS;
22695
+ }
22696
+ }
22697
+ if (end && typeof p === 'object') {
22698
+ tailStart = endIndex(p, last, lastStart, prev, prevStart);
22699
+ if (tailStart === -1)
22700
+ continue;
22701
+ }
22702
+ const shape = this.#shapes[r];
22703
+ if (shape === 'base' && end)
22704
+ return 'yes';
22705
+ if (shape === 'tail' && end) {
22706
+ if (this.#level2 && dotPortion(f, 0, tailStart, true, sep)) {
22707
+ return 'maybe';
22708
+ }
22709
+ if (!dotPortion(f, 0, tailStart, dot, sep))
22710
+ return 'yes';
22711
+ continue;
22712
+ }
22713
+ if (shape === 'mid' || shape === 'head') {
22714
+ const m = this.#walk(f, row, shape === 'head', dot, sep);
22715
+ if (m !== 'no')
22716
+ return m;
22717
+ continue;
22718
+ }
22719
+ return 'maybe';
22720
+ }
22721
+ return 'no';
22722
+ }
22723
+ // Match a row of a known shape against the split path, the same as
22724
+ // #matchGlobstar and #matchGlobStarBodySections do. None of these
22725
+ // rows have a drive letter, so they start at the first portion.
22726
+ #matchShape(file, row, shape) {
22727
+ const dot = !!this.options.dot;
22728
+ const n = file.length;
22729
+ if (shape === 'head') {
22730
+ // x, then at least one portion that ** can walk
22731
+ if (n < 2 || !portionMatch(row[0], file[0])) {
22732
+ return false;
22733
+ }
22734
+ for (let i = 1; i < n; i++)
22735
+ if (!walkable(file[i], dot))
22736
+ return false;
22737
+ return true;
22738
+ }
22739
+ const x = row[1];
22740
+ if (shape === 'tail') {
22741
+ // x is the last portion, or the one before a trailing ''
22742
+ let t = n - 1;
22743
+ if (!portionMatch(x, file[t])) {
22744
+ if (file[t] !== '' || n === 1 || !portionMatch(x, file[--t])) {
22745
+ return false;
22746
+ }
22747
+ }
22748
+ for (let i = 0; i < t; i++)
22749
+ if (!walkable(file[i], dot))
22750
+ return false;
22751
+ return true;
22752
+ }
22753
+ // mid: the first x that ** can walk to, with a portion after it,
22754
+ // and ** can walk all of those
22755
+ for (let k = 0; k < n; k++) {
22756
+ if (portionMatch(x, file[k]) && k + 1 < n) {
22757
+ let i = k + 1;
22758
+ while (i < n && walkable(file[i], dot))
22759
+ i++;
22760
+ if (i === n)
22761
+ return true;
22762
+ }
22763
+ if (!walkable(file[k], dot))
22764
+ return false;
22765
+ }
22766
+ return false;
22767
+ }
22768
+ // Match a row of `**/x/**` or `x/**` against the path, the same as
22769
+ // #matchGlobstar and #matchGlobStarBodySections do once it is split.
22770
+ // x must match a portion (the first one, for `x/**`), the ** before
22771
+ // it must be able to walk the portions before that one, and there
22772
+ // must be a portion after it, which the ** after it can walk. At
22773
+ // level 2, this is only known if no portion is `.` or `..`.
22774
+ #walk(f, row, head, dot, sep) {
22775
+ if (this.#level2 && dotPortion(f, 0, f.length, true, sep))
22776
+ return 'maybe';
22777
+ const x = row[head ? 0 : 1];
22778
+ // the last portion has none after it, so it can not be x
22779
+ let s = 0;
22780
+ for (let e = nextSep(f, s, sep); e !== -1; e = nextSep(f, s, sep)) {
22781
+ // the start of the next portion
22782
+ let next = e + 1;
22783
+ while (next < f.length && isSep(f.charCodeAt(next), sep))
22784
+ next++;
22785
+ const hit = typeof x === 'string' ?
22786
+ e - s === x.length && f.startsWith(x, s)
22787
+ : e > s && x.testUnchecked(f.slice(s, e));
22788
+ if (hit && !dotPortion(f, next, f.length, dot, sep))
22789
+ return 'yes';
22790
+ if (head || dotPortion(f, s, next, dot, sep))
22791
+ return 'no';
22792
+ s = next;
22315
22793
  }
22794
+ return 'no';
22795
+ }
22796
+ // the last split, if it was the same path with the same split
22797
+ // settings
22798
+ #cachedSplit(f) {
22799
+ const last = lastSplit;
22800
+ return (last?.f === f &&
22801
+ last.isWindows === this.isWindows &&
22802
+ last.preserveMultipleSlashes === this.preserveMultipleSlashes &&
22803
+ last.normalize === this.#form) ?
22804
+ last
22805
+ : undefined;
22806
+ }
22807
+ // split a path for match(), and keep the parts for the next call
22808
+ #split(f) {
22809
+ const { isWindows, preserveMultipleSlashes } = this;
22810
+ // windows: need to use /, not \
22811
+ let parts = this.#slashSplit(isWindows ? f.split('\\').join('/') : f);
22812
+ // normalize each portion, so that a char that normalizes to `/`
22813
+ // does not make a new portion
22814
+ const normalize = this.#form;
22815
+ if (normalize)
22816
+ parts = parts.map(p => p.normalize(normalize));
22817
+ lastSplit = {
22818
+ f,
22819
+ isWindows,
22820
+ preserveMultipleSlashes,
22821
+ normalize,
22822
+ parts,
22823
+ };
22824
+ return lastSplit;
22825
+ }
22826
+ // resolve `.` and `..` portions of a path, for level 2
22827
+ #levelTwoFileOptimize(file) {
22828
+ // work on a copy, so that the cached parts do not change
22829
+ const parts = [...file];
22316
22830
  let didSomething = false;
22317
22831
  do {
22318
22832
  didSomething = false;
@@ -22337,17 +22851,15 @@ class Minimatch {
22337
22851
  }
22338
22852
  }
22339
22853
  // <pre>/<p>/../<rest> -> <pre>/<rest>
22340
- let dd = 0;
22854
+ // .. can not remove the root of a Windows path
22855
+ const root = this.#rootIndex(parts) + 1;
22856
+ let dd = root;
22341
22857
  while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
22342
22858
  const p = parts[dd - 1];
22343
- if (p &&
22344
- p !== '.' &&
22345
- p !== '..' &&
22346
- p !== '**' &&
22347
- !(this.isWindows && /^[a-z]:$/i.test(p))) {
22859
+ if (p && p !== '.' && p !== '..' && p !== '**') {
22348
22860
  didSomething = true;
22349
22861
  parts.splice(dd - 1, 2);
22350
- dd -= 2;
22862
+ dd = Math.max(dd - 2, root);
22351
22863
  }
22352
22864
  }
22353
22865
  } while (didSomething);
@@ -22372,6 +22884,8 @@ class Minimatch {
22372
22884
  // **/*/<rest> -> */**/<rest> <== not valid because ** doesn't follow
22373
22885
  // this WOULD be allowed if ** did follow symlinks, or * didn't
22374
22886
  firstPhasePreProcess(globParts) {
22887
+ // each **/.. split doubles the patterns, so stop at the brace max
22888
+ const { braceExpandMax = 100_000 } = this.options;
22375
22889
  let didSomething = false;
22376
22890
  do {
22377
22891
  didSomething = false;
@@ -22399,7 +22913,8 @@ class Minimatch {
22399
22913
  p === '..' ||
22400
22914
  !p2 ||
22401
22915
  p2 === '.' ||
22402
- p2 === '..') {
22916
+ p2 === '..' ||
22917
+ globParts.length >= braceExpandMax) {
22403
22918
  continue;
22404
22919
  }
22405
22920
  didSomething = true;
@@ -22431,7 +22946,9 @@ class Minimatch {
22431
22946
  }
22432
22947
  }
22433
22948
  // <pre>/<p>/../<rest> -> <pre>/<rest>
22434
- let dd = 0;
22949
+ // .. can not remove the root of a Windows path
22950
+ const root = this.#rootIndex(parts) + 1;
22951
+ let dd = root;
22435
22952
  while (-1 !== (dd = parts.indexOf('..', dd + 1))) {
22436
22953
  const p = parts[dd - 1];
22437
22954
  if (p && p !== '.' && p !== '..' && p !== '**') {
@@ -22441,7 +22958,7 @@ class Minimatch {
22441
22958
  parts.splice(dd - 1, 2, ...splin);
22442
22959
  if (parts.length === 0)
22443
22960
  parts.push('');
22444
- dd -= 2;
22961
+ dd = Math.max(dd - 2, root);
22445
22962
  }
22446
22963
  }
22447
22964
  }
@@ -22456,6 +22973,10 @@ class Minimatch {
22456
22973
  // {<pre>/**/<rest>,<pre>/**/<p>/<rest>} -> <pre>/**/<rest>
22457
22974
  // ^-- not valid because ** doens't follow symlinks
22458
22975
  secondPhasePreProcess(globParts) {
22976
+ // each pattern is compared to each other one, so skip this for
22977
+ // large sets. The patterns match the same paths either way.
22978
+ if (globParts.length > 1000)
22979
+ return globParts;
22459
22980
  for (let i = 0; i < globParts.length - 1; i++) {
22460
22981
  for (let j = i + 1; j < globParts.length; j++) {
22461
22982
  const matched = this.partsMatch(globParts[i], globParts[j], !this.preserveMultipleSlashes);
@@ -22531,18 +23052,36 @@ class Minimatch {
22531
23052
  this.pattern = pattern.slice(negateOffset);
22532
23053
  this.negate = negate;
22533
23054
  }
22534
- // set partial to true to test if, for example,
22535
- // "/a/b" matches the start of "/*/b/*/d"
22536
- // Partial means, if you run out of file before you run
22537
- // out of pattern, then that's fine, as long as all
22538
- // the parts match.
22539
- matchOne(file, pattern, partial = false) {
23055
+ // Match the parts of a path against one row of the set. `resolved`
23056
+ // is the parts after level 2 resolves `.` and `..` in them, or the
23057
+ // same parts below level 2. Set partial to true to test if, for
23058
+ // example, "/a/b" matches the start of "/*/b/*/d". Partial means, if
23059
+ // you run out of file before you run out of pattern, then that's
23060
+ // fine, as long as all the parts match.
23061
+ #matchRow(file, resolved, pattern, partial) {
22540
23062
  let fileStartIndex = 0;
22541
23063
  let patternStartIndex = 0;
22542
- // UNC paths like //?/X:/... can match X:/... and vice versa
22543
- // Drive letters in absolute drive or unc paths are always compared
22544
- // case-insensitively.
22545
- if (this.isWindows) {
23064
+ // UNC paths like //?/X:/... can match X:/... and vice versa, and
23065
+ // //?/UNC/host/share/... can match //host/share/... and vice versa.
23066
+ // Drive letters in absolute drive or unc paths, and the host and
23067
+ // share of UNC paths, are always compared case-insensitively.
23068
+ const fh = this.isWindows ? uncHost(resolved) : -1;
23069
+ const ph = fh === -1 ? -1 : uncHost(pattern);
23070
+ if (ph !== -1) {
23071
+ const host = pattern[ph];
23072
+ const share = pattern[ph + 1];
23073
+ if (typeof host !== 'string' || typeof share !== 'string') {
23074
+ // a host or share with magic is matched as usual
23075
+ fileStartIndex = fh;
23076
+ patternStartIndex = ph;
23077
+ }
23078
+ else if (resolved[fh].toLowerCase() === host.toLowerCase() &&
23079
+ resolved[fh + 1].toLowerCase() === share.toLowerCase()) {
23080
+ fileStartIndex = fh + 2;
23081
+ patternStartIndex = ph + 2;
23082
+ }
23083
+ }
23084
+ else if (this.isWindows) {
22546
23085
  const fileDrive = typeof file[0] === 'string' && /^[a-z]:$/i.test(file[0]);
22547
23086
  const fileUNC = !fileDrive &&
22548
23087
  file[0] === '' &&
@@ -22567,47 +23106,27 @@ class Minimatch {
22567
23106
  file[fdi],
22568
23107
  pattern[pdi],
22569
23108
  ];
22570
- // start matching at the drive letter index of each
23109
+ // the drives match, so start matching after them
22571
23110
  if (fd.toLowerCase() === pd.toLowerCase()) {
22572
- pattern[pdi] = fd;
22573
- patternStartIndex = pdi;
22574
- fileStartIndex = fdi;
23111
+ patternStartIndex = pdi + 1;
23112
+ fileStartIndex = fdi + 1;
22575
23113
  }
22576
23114
  }
22577
23115
  }
22578
- // resolve and reduce . and .. portions in the file as well.
22579
- // don't need to do the second phase, because it's only one string[]
22580
- const { optimizationLevel = 1 } = this.options;
22581
- if (optimizationLevel >= 2) {
22582
- file = this.levelTwoFileOptimize(file);
22583
- }
23116
+ // the drive letters are found in the parts before level 2 resolves
23117
+ // them, and the match uses the resolved parts
23118
+ file = resolved;
22584
23119
  if (pattern.includes(GLOBSTAR)) {
22585
23120
  return this.#matchGlobstar(file, pattern, partial, fileStartIndex, patternStartIndex);
22586
23121
  }
22587
23122
  return this.#matchOne(file, pattern, partial, fileStartIndex, patternStartIndex);
22588
23123
  }
22589
23124
  #matchGlobstar(file, pattern, partial, fileIndex, patternIndex) {
22590
- // split the pattern into head, tail, and middle of ** delimited parts
22591
- const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
22592
- const lastgs = pattern.lastIndexOf(GLOBSTAR);
22593
- // split the pattern up into globstar-delimited sections
22594
- // the tail has to be at the end, and the others just have
22595
- // to be found in order from the head.
22596
- const [head, body, tail] = partial ?
22597
- [
22598
- pattern.slice(patternIndex, firstgs),
22599
- pattern.slice(firstgs + 1),
22600
- [],
22601
- ]
22602
- : [
22603
- pattern.slice(patternIndex, firstgs),
22604
- pattern.slice(firstgs + 1, lastgs),
22605
- pattern.slice(lastgs + 1),
22606
- ];
23125
+ const { head, body, tail, segments } = this.#getGlobstarParts(pattern, partial, patternIndex);
22607
23126
  // check the head, from the current file/pattern index.
22608
23127
  if (head.length) {
22609
- const fileHead = file.slice(fileIndex, fileIndex + head.length);
22610
- if (!this.#matchOne(fileHead, head, partial, 0, 0)) {
23128
+ const headEnd = fileIndex + head.length;
23129
+ if (!this.#matchOne(file, head, partial, fileIndex, 0, headEnd)) {
22611
23130
  return false;
22612
23131
  }
22613
23132
  fileIndex += head.length;
@@ -22665,36 +23184,64 @@ class Minimatch {
22665
23184
  // now we know that there's one or more body sections, which can
22666
23185
  // be matched anywhere from the 0 index (because the head was pruned)
22667
23186
  // through to the length-fileTailMatch index.
22668
- // split the body up into sections, and note the minimum index it can
22669
- // be found at (start with the length of all previous segments)
22670
- // [section, before, after]
22671
- const bodySegments = [[[], 0]];
22672
- let currentBody = bodySegments[0];
23187
+ return !!this.#matchGlobStarBodySections(file, segments, file.length - fileTailMatch, fileIndex, 0, partial, 0, !!fileTailMatch, new Set());
23188
+ }
23189
+ // Split the pattern into head, tail, and middle of ** delimited
23190
+ // parts. The tail has to be at the end, and the others just have to
23191
+ // be found in order from the head. These only depend on the pattern
23192
+ // row, so they are kept for the next path.
23193
+ #getGlobstarParts(pattern, partial, patternIndex) {
23194
+ let byStart = this.#globstarParts.get(pattern);
23195
+ if (!byStart) {
23196
+ byStart = [];
23197
+ this.#globstarParts.set(pattern, byStart);
23198
+ }
23199
+ const key = patternIndex * 2 + (partial ? 1 : 0);
23200
+ const found = byStart[key];
23201
+ if (found)
23202
+ return found;
23203
+ const firstgs = pattern.indexOf(GLOBSTAR, patternIndex);
23204
+ const lastgs = pattern.lastIndexOf(GLOBSTAR);
23205
+ const head = pattern.slice(patternIndex, firstgs);
23206
+ const body = pattern.slice(firstgs + 1, partial ? undefined : lastgs);
23207
+ const tail = partial ? [] : pattern.slice(lastgs + 1);
23208
+ // split the body up into sections, and note the number of
23209
+ // portions that must come after the start of each one
23210
+ const segments = [[[], 0]];
23211
+ let current = segments[0];
22673
23212
  let nonGsParts = 0;
22674
23213
  const nonGsPartsSums = [0];
22675
23214
  for (const b of body) {
22676
23215
  if (b === GLOBSTAR) {
22677
23216
  nonGsPartsSums.push(nonGsParts);
22678
- currentBody = [[], 0];
22679
- bodySegments.push(currentBody);
23217
+ current = [[], 0];
23218
+ segments.push(current);
22680
23219
  }
22681
23220
  else {
22682
- currentBody[0].push(b);
23221
+ current[0].push(b);
22683
23222
  nonGsParts++;
22684
23223
  }
22685
23224
  }
22686
- let i = bodySegments.length - 1;
22687
- const fileLength = file.length - fileTailMatch;
22688
- for (const b of bodySegments) {
22689
- b[1] = fileLength - (nonGsPartsSums[i--] + b[0].length);
22690
- }
22691
- return !!this.#matchGlobStarBodySections(file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch);
23225
+ // the portions in this section and the ones after it
23226
+ segments.forEach((b, i) => {
23227
+ b[1] = nonGsParts - nonGsPartsSums[i];
23228
+ });
23229
+ const parts = { head, body, tail, segments };
23230
+ byStart[key] = parts;
23231
+ return parts;
22692
23232
  }
22693
23233
  // return false for "nope, not matching"
22694
23234
  // return null for "not matching, cannot keep trying"
22695
23235
  #matchGlobStarBodySections(file,
22696
- // pattern section, last possible position for it
22697
- bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail) {
23236
+ // pattern section, and the portions that it and the rest need
23237
+ bodySegments,
23238
+ // the end of the file portions that the body can match
23239
+ fileLength, fileIndex, bodyIndex, partial, globStarDepth, sawTail,
23240
+ // the places that a walk has already tried, as
23241
+ // bodyIndex * (file.length + 1) + fileIndex. A walk from a place
23242
+ // does the same no matter how it got there, and it only comes back
23243
+ // if it found no match, so a walk that gets to one stops.
23244
+ tried) {
22698
23245
  // take the first body segment, and walk from fileIndex to its "after"
22699
23246
  // value at the end
22700
23247
  // If it doesn't match at that position, we increment, until we hit
@@ -22706,8 +23253,16 @@ class Minimatch {
22706
23253
  // can't possibly be a valid matching condition.
22707
23254
  const bs = bodySegments[bodyIndex];
22708
23255
  if (!bs) {
22709
- // just make sure that there's no bad dots
22710
- for (let i = fileIndex; i < file.length; i++) {
23256
+ // with partial, the body is the rest of the pattern, so it
23257
+ // matches if no portions are left, or only the '' of a trailing
23258
+ // / (as a/* matches a/b/). A ** at the end walks them as its own
23259
+ // empty section.
23260
+ if (partial) {
23261
+ return (fileIndex >= fileLength ||
23262
+ (fileIndex === fileLength - 1 && file[fileIndex] === ''));
23263
+ }
23264
+ // just make sure that there's no bad dots before the tail
23265
+ for (let i = fileIndex; i < fileLength; i++) {
22711
23266
  sawTail = true;
22712
23267
  const f = file[i];
22713
23268
  if (f === '.' ||
@@ -22718,19 +23273,29 @@ class Minimatch {
22718
23273
  }
22719
23274
  return sawTail;
22720
23275
  }
22721
- // have a non-globstar body section to test
22722
- const [body, after] = bs;
23276
+ // have a non-globstar body section to test, up to the last index
23277
+ // where it can start
23278
+ const [body, need] = bs;
23279
+ // with partial, the path can end before the body does
23280
+ const after = partial ? fileLength : fileLength - need;
22723
23281
  while (fileIndex <= after) {
22724
- const m = this.#matchOne(file.slice(0, fileIndex + body.length), body, partial, fileIndex, 0);
23282
+ const key = bodyIndex * (file.length + 1) + fileIndex;
23283
+ if (tried.has(key))
23284
+ return false;
23285
+ tried.add(key);
23286
+ const m = this.#matchOne(file, body, partial, fileIndex, 0, fileIndex + body.length);
22725
23287
  // if limit exceeded, no match. intentional false negative,
22726
23288
  // acceptable break in correctness for security.
22727
23289
  if (m && globStarDepth < this.maxGlobstarRecursion) {
22728
23290
  // match! see if the rest match. if so, we're done!
22729
- const sub = this.#matchGlobStarBodySections(file, bodySegments, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail);
23291
+ const sub = this.#matchGlobStarBodySections(file, bodySegments, fileLength, fileIndex + body.length, bodyIndex + 1, partial, globStarDepth + 1, sawTail, tried);
22730
23292
  if (sub !== false) {
22731
23293
  return sub;
22732
23294
  }
22733
23295
  }
23296
+ // the ** does not walk past the last place the body can start
23297
+ if (fileIndex === after)
23298
+ break;
22734
23299
  const f = file[fileIndex];
22735
23300
  if (f === '.' ||
22736
23301
  f === '..' ||
@@ -22742,21 +23307,20 @@ class Minimatch {
22742
23307
  // walked off. no point continuing
22743
23308
  return partial || null;
22744
23309
  }
22745
- #matchOne(file, pattern, partial, fileIndex, patternIndex) {
23310
+ #matchOne(file, pattern, partial, fileIndex, patternIndex,
23311
+ // only match the file portions before this index
23312
+ fileEnd = file.length) {
22746
23313
  let fi;
22747
23314
  let pi;
22748
23315
  let pl;
22749
23316
  let fl;
22750
23317
  for (fi = fileIndex,
22751
23318
  pi = patternIndex,
22752
- fl = file.length,
23319
+ fl = Math.min(fileEnd, file.length),
22753
23320
  pl = pattern.length; fi < fl && pi < pl; fi++, pi++) {
22754
- this.debug('matchOne loop');
22755
23321
  let p = pattern[pi];
22756
23322
  let f = file[fi];
22757
- this.debug(pattern, p, f);
22758
23323
  // should be impossible.
22759
- // some invalid regexp stuff in the set.
22760
23324
  /* c8 ignore start */
22761
23325
  if (p === false || p === GLOBSTAR) {
22762
23326
  return false;
@@ -22764,17 +23328,8 @@ class Minimatch {
22764
23328
  /* c8 ignore stop */
22765
23329
  // something other than **
22766
23330
  // non-magic patterns just have to match exactly
22767
- // patterns with magic have been turned into regexps.
22768
- let hit;
22769
- if (typeof p === 'string') {
22770
- hit = f === p;
22771
- this.debug('string match', p, f, hit);
22772
- }
22773
- else {
22774
- hit = p.test(f);
22775
- this.debug('pattern match', p, f, hit);
22776
- }
22777
- if (!hit)
23331
+ // patterns with magic have been turned into Matchers.
23332
+ if (!portionMatch(p, f))
22778
23333
  return false;
22779
23334
  }
22780
23335
  // Note: ending in / means that we'll get a final ""
@@ -22783,9 +23338,8 @@ class Minimatch {
22783
23338
  // If the file ends in /, then it can only match a
22784
23339
  // a pattern that ends in /, unless the pattern just
22785
23340
  // doesn't have any more for it. But, a/b/ should *not*
22786
- // match "a/b/*", even though "" matches against the
22787
- // [^/]*? pattern, except in partial mode, where it might
22788
- // simply not be reached yet.
23341
+ // match "a/b/*", even though * can match "", except in
23342
+ // partial mode, where it might simply not be reached yet.
22789
23343
  // However, a/b/ should still satisfy a/*
22790
23344
  // now either we fell off the end of the pattern, or we're done.
22791
23345
  if (fi === fl && pi === pl) {
@@ -22824,139 +23378,26 @@ class Minimatch {
22824
23378
  return GLOBSTAR;
22825
23379
  if (pattern === '')
22826
23380
  return '';
22827
- // far and away, the most common glob pattern parts are
22828
- // *, *.*, and *.<ext> Add a fast check method for those.
22829
- let m;
22830
- let fastTest = null;
22831
- if ((m = pattern.match(starRE))) {
22832
- fastTest = options.dot ? starTestDot : starTest;
22833
- }
22834
- else if ((m = pattern.match(starDotExtRE))) {
22835
- fastTest = (options.nocase ?
22836
- options.dot ?
22837
- starDotExtTestNocaseDot
22838
- : starDotExtTestNocase
22839
- : options.dot ? starDotExtTestDot
22840
- : starDotExtTest)(m[1]);
22841
- }
22842
- else if ((m = pattern.match(qmarksRE))) {
22843
- fastTest = (options.nocase ?
22844
- options.dot ?
22845
- qmarksTestNocaseDot
22846
- : qmarksTestNocase
22847
- : options.dot ? qmarksTestDot
22848
- : qmarksTest)(m);
22849
- }
22850
- else if ((m = pattern.match(starDotStarRE))) {
22851
- fastTest = options.dot ? starDotStarTestDot : starDotStarTest;
22852
- }
22853
- else if ((m = pattern.match(dotStarRE))) {
22854
- fastTest = dotStarTest;
22855
- }
22856
- const re = AST.fromGlob(pattern, this.options).toMMPattern();
22857
- if (fastTest && typeof re === 'object') {
22858
- // Avoids overriding in frozen environments
22859
- Reflect.defineProperty(re, 'test', { value: fastTest });
22860
- }
22861
- return re;
22862
- }
22863
- makeRe() {
22864
- if (this.regexp || this.regexp === false)
22865
- return this.regexp;
22866
- // at this point, this.set is a 2d array of partial
22867
- // pattern strings, or "**".
22868
- //
22869
- // It's better to use .match(). This function shouldn't
22870
- // be used, really, but it's pretty convenient sometimes,
22871
- // when you just want to work with a regex.
22872
- const set = this.set;
22873
- if (!set.length) {
22874
- this.regexp = false;
22875
- return this.regexp;
22876
- }
22877
- const options = this.options;
22878
- const twoStar = options.noglobstar ? star
22879
- : options.dot ? twoStarDot
22880
- : twoStarNoDot;
22881
- const flags = new Set(options.nocase ? ['i'] : []);
22882
- // regexpify non-globstar patterns
22883
- // if ** is only item, then we just do one twoStar
22884
- // if ** is first, and there are more, prepend (\/|twoStar\/)? to next
22885
- // if ** is last, append (\/twoStar|) to previous
22886
- // if ** is in the middle, append (\/|\/twoStar\/) to previous
22887
- // then filter out GLOBSTAR symbols
22888
- let re = set
22889
- .map(pattern => {
22890
- const pp = pattern.map(p => {
22891
- if (p instanceof RegExp) {
22892
- for (const f of p.flags.split(''))
22893
- flags.add(f);
22894
- }
22895
- return (typeof p === 'string' ? regExpEscape(p)
22896
- : p === GLOBSTAR ? GLOBSTAR
22897
- : p._src);
22898
- });
22899
- pp.forEach((p, i) => {
22900
- const next = pp[i + 1];
22901
- const prev = pp[i - 1];
22902
- if (p !== GLOBSTAR || prev === GLOBSTAR) {
22903
- return;
22904
- }
22905
- if (prev === undefined) {
22906
- if (next !== undefined && next !== GLOBSTAR) {
22907
- pp[i + 1] = '(?:\\/|' + twoStar + '\\/)?' + next;
22908
- }
22909
- else {
22910
- pp[i] = twoStar;
22911
- }
22912
- }
22913
- else if (next === undefined) {
22914
- pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + ')?';
22915
- }
22916
- else if (next !== GLOBSTAR) {
22917
- pp[i - 1] = prev + '(?:\\/|\\/' + twoStar + '\\/)' + next;
22918
- pp[i + 1] = GLOBSTAR;
22919
- }
22920
- });
22921
- const filtered = pp.filter(p => p !== GLOBSTAR);
22922
- // For partial matches, we need to make the pattern match
22923
- // any prefix of the full path. We do this by generating
22924
- // alternative patterns that match progressively longer prefixes.
22925
- if (this.partial && filtered.length >= 1) {
22926
- const prefixes = [];
22927
- for (let i = 1; i <= filtered.length; i++) {
22928
- prefixes.push(filtered.slice(0, i).join('/'));
22929
- }
22930
- return '(?:' + prefixes.join('|') + ')';
22931
- }
22932
- return filtered.join('/');
22933
- })
22934
- .join('|');
22935
- // need to wrap in parens if we had more than one thing with |,
22936
- // otherwise only the first will be anchored to ^ and the last to $
22937
- const [open, close] = set.length > 1 ? ['(?:', ')'] : ['', ''];
22938
- // must match entire pattern
22939
- // ending in a * or ** will make it less strict.
22940
- re = '^' + open + re + close + '$';
22941
- // In partial mode, '/' should always match as it's a valid prefix for any pattern
22942
- if (this.partial) {
22943
- re = '^(?:\\/|' + open + re.slice(1, -1) + close + ')$';
22944
- }
22945
- // can match anything, as long as it's not this.
22946
- if (this.negate)
22947
- re = '^(?!' + re + ').+$';
22948
- try {
22949
- this.regexp = new RegExp(re, [...flags].join(''));
22950
- /* c8 ignore start */
23381
+ const { nocase, nocaseMagicOnly, noext = false, dot, normalize, } = options;
23382
+ const lit = literal(pattern, noext);
23383
+ // in nocase mode, a literal with cased chars still needs a
23384
+ // Matcher, unless nocaseMagicOnly is set.
23385
+ if (lit !== undefined &&
23386
+ !(nocase &&
23387
+ !nocaseMagicOnly &&
23388
+ pattern.toUpperCase() !== pattern.toLowerCase())) {
23389
+ return this.#norm(lit);
22951
23390
  }
22952
- catch {
22953
- // should be impossible
22954
- this.regexp = false;
23391
+ if (this.#pending)
23392
+ return PENDING;
23393
+ let m = this.#matchers.get(pattern);
23394
+ if (!m) {
23395
+ m = new Matcher(pattern, { nocase, noext, dot, normalize });
23396
+ this.#matchers.set(pattern, m);
22955
23397
  }
22956
- /* c8 ignore stop */
22957
- return this.regexp;
23398
+ return m;
22958
23399
  }
22959
- slashSplit(p) {
23400
+ #slashSplit(p) {
22960
23401
  // if p starts with // on windows, we preserve that
22961
23402
  // so that UNC paths aren't broken. Otherwise, any number of
22962
23403
  // / characters are coalesced into one, unless
@@ -22973,7 +23414,6 @@ class Minimatch {
22973
23414
  }
22974
23415
  }
22975
23416
  match(f, partial = this.partial) {
22976
- this.debug('match', f, this.pattern);
22977
23417
  // short-circuit in the case of busted things.
22978
23418
  // comments, etc.
22979
23419
  if (this.comment) {
@@ -22986,19 +23426,36 @@ class Minimatch {
22986
23426
  return true;
22987
23427
  }
22988
23428
  const options = this.options;
22989
- // windows: need to use /, not \
22990
- if (this.isWindows) {
22991
- f = f.split('\\').join('/');
23429
+ // treat the test path as a set of pathparts. If it was not just
23430
+ // split or checked, check that it can match before splitting it.
23431
+ let split = this.#cachedSplit(f);
23432
+ if (!split) {
23433
+ // the checks look at the path as given, so not if it is
23434
+ // normalized
23435
+ if (!partial &&
23436
+ !this.#form &&
23437
+ (this.#endCheck || this.#literalCheck) &&
23438
+ lastChecked !== f) {
23439
+ lastChecked = f;
23440
+ const can = this.#canMatch(f);
23441
+ if (can === 'no')
23442
+ return options.flipNegate ? false : this.negate;
23443
+ if (can === 'yes')
23444
+ return options.flipNegate ? true : !this.negate;
23445
+ }
23446
+ split = this.#split(f);
22992
23447
  }
22993
- // treat the test path as a set of pathparts.
22994
- const ff = this.slashSplit(f);
22995
- this.debug(this.pattern, 'split', ff);
23448
+ const ff = split.parts;
23449
+ // the parts after level 2 resolves them, kept for the next pattern
23450
+ // that tests the same path
23451
+ const resolved = this.#level2 ?
23452
+ (split.resolved ??= this.#levelTwoFileOptimize(ff))
23453
+ : ff;
22996
23454
  // just ONE of the pattern sets in this.set needs to match
22997
23455
  // in order for it to be valid. If negating, then just one
22998
23456
  // match means that we have failed.
22999
23457
  // Either way, return on the first hit.
23000
23458
  const set = this.set;
23001
- this.debug(this.pattern, 'set', set);
23002
23459
  // Find the basename of the path by looking for the last non-empty segment
23003
23460
  let filename = ff[ff.length - 1];
23004
23461
  if (!filename) {
@@ -23006,12 +23463,16 @@ class Minimatch {
23006
23463
  filename = ff[i];
23007
23464
  }
23008
23465
  }
23009
- for (const pattern of set) {
23010
- let file = ff;
23011
- if (options.matchBase && pattern.length === 1) {
23012
- file = [filename];
23013
- }
23014
- const hit = this.matchOne(file, pattern, partial);
23466
+ for (let r = 0; r < set.length; r++) {
23467
+ const pattern = set[r];
23468
+ const shape = partial ? undefined : this.#shapes[r];
23469
+ // level 2 does not change a path of one portion
23470
+ const hit = shape === 'base' ?
23471
+ portionMatch(pattern[0], filename)
23472
+ : shape ? this.#matchShape(resolved, pattern, shape)
23473
+ : options.matchBase && pattern.length === 1 ?
23474
+ this.#matchRow([filename], [filename], pattern, partial)
23475
+ : this.#matchRow(ff, resolved, pattern, partial);
23015
23476
  if (hit) {
23016
23477
  if (options.flipNegate) {
23017
23478
  return true;
@@ -23031,7 +23492,6 @@ class Minimatch {
23031
23492
  }
23032
23493
  }
23033
23494
  /* c8 ignore stop */
23034
- minimatch.AST = AST;
23035
23495
  minimatch.Minimatch = Minimatch;
23036
23496
  minimatch.escape = escape;
23037
23497
  minimatch.unescape = unescape;
@@ -23832,7 +24292,10 @@ function resolveWithTS(containingFile, source, ts2, fs) {
23832
24292
  }
23833
24293
  }
23834
24294
  function loadTSConfig(configPath, ts2, fs, visited = /* @__PURE__ */ new Set()) {
23835
- const parseConfigHost = ts2.sys;
24295
+ const parseConfigHost = {
24296
+ ...ts2.sys,
24297
+ readDirectory: () => []
24298
+ };
23836
24299
  const config = ts2.parseJsonConfigFileContent(
23837
24300
  ts2.readConfigFile(configPath, fs.readFile).config,
23838
24301
  parseConfigHost,
@@ -25147,7 +25610,8 @@ function transformDestructuredProps(ctx, vueImportAliases) {
25147
25610
  }
25148
25611
  }
25149
25612
  function walkScope(node, isRoot = false) {
25150
- for (const stmt of node.body) {
25613
+ const body = node.type === "SwitchCase" ? node.consequent : node.body;
25614
+ for (const stmt of body) {
25151
25615
  if (stmt.type === "VariableDeclaration") {
25152
25616
  walkVariableDeclaration(stmt, isRoot);
25153
25617
  } else if (stmt.type === "FunctionDeclaration" || stmt.type === "ClassDeclaration") {
@@ -25256,12 +25720,19 @@ function transformDestructuredProps(ctx, vueImportAliases) {
25256
25720
  }
25257
25721
  return;
25258
25722
  }
25723
+ if (node.type === "StaticBlock") {
25724
+ pushScope(true);
25725
+ walkFunctionScopeVarDeclarations(node);
25726
+ walkScope(node);
25727
+ return;
25728
+ }
25259
25729
  if (node.type === "CatchClause") {
25260
25730
  pushScope();
25261
- if (node.param && node.param.type === "Identifier") {
25262
- registerLocalBinding(node.param);
25731
+ if (node.param) {
25732
+ for (const id of CompilerDOM.extractIdentifiers(node.param)) {
25733
+ registerLocalBinding(id);
25734
+ }
25263
25735
  }
25264
- walkScope(node.body);
25265
25736
  return;
25266
25737
  }
25267
25738
  if (node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "ForStatement") {
@@ -25293,13 +25764,19 @@ function transformDestructuredProps(ctx, vueImportAliases) {
25293
25764
  if (node === currentModelOption) {
25294
25765
  currentModelOption = null;
25295
25766
  }
25296
- if (CompilerDOM.isFunctionType(node)) {
25767
+ if (CompilerDOM.isFunctionType(node) || node.type === "StaticBlock") {
25297
25768
  popScope(true);
25298
25769
  } else if (node.type === "BlockStatement" && !CompilerDOM.isFunctionType(parent)) {
25299
25770
  popScope();
25300
- } else if (node.type === "CatchClause" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "ForStatement") {
25771
+ } else if (node.type === "CatchClause" || node.type === "SwitchStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "ForStatement") {
25301
25772
  popScope();
25302
25773
  }
25774
+ if (parent && parent.type === "SwitchStatement" && node === parent.discriminant) {
25775
+ pushScope();
25776
+ for (const switchCase of parent.cases) {
25777
+ walkScope(switchCase);
25778
+ }
25779
+ }
25303
25780
  }
25304
25781
  });
25305
25782
  }
@@ -26396,7 +26873,7 @@ function mergeSourceMaps(scriptMap, templateMap, templateLineOffset) {
26396
26873
  return generator.toJSON();
26397
26874
  }
26398
26875
 
26399
- const version = "3.5.44";
26876
+ const version = "3.5.45";
26400
26877
  const parseCache = parseCache$1;
26401
26878
  const errorMessages = {
26402
26879
  ...CompilerDOM.errorMessages,