@m2c2kit/build-helpers 0.3.27 → 0.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +690 -396
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -3103,7 +3103,7 @@ function findOneChild(test, nodes) {
3103
3103
  * @param recurse Also consider child nodes.
3104
3104
  * @returns The first node that passes `test`.
3105
3105
  */
3106
- function findOne(test, nodes, recurse = true) {
3106
+ function findOne$1(test, nodes, recurse = true) {
3107
3107
  const searchedNodes = Array.isArray(nodes) ? nodes : [nodes];
3108
3108
  for (let i = 0; i < searchedNodes.length; i++) {
3109
3109
  const node = searchedNodes[i];
@@ -3111,7 +3111,9 @@ function findOne(test, nodes, recurse = true) {
3111
3111
  return node;
3112
3112
  }
3113
3113
  if (recurse && hasChildren(node) && node.children.length > 0) {
3114
- return findOne(test, node.children, true);
3114
+ const found = findOne$1(test, node.children, true);
3115
+ if (found)
3116
+ return found;
3115
3117
  }
3116
3118
  }
3117
3119
  return null;
@@ -3138,7 +3140,7 @@ function existsOne(test, nodes) {
3138
3140
  * @param nodes Array of nodes to search.
3139
3141
  * @returns All nodes passing `test`.
3140
3142
  */
3141
- function findAll(test, nodes) {
3143
+ function findAll$1(test, nodes) {
3142
3144
  const result = [];
3143
3145
  const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]];
3144
3146
  const indexStack = [0];
@@ -3271,7 +3273,7 @@ function getElements(options, nodes, recurse, limit = Infinity) {
3271
3273
  function getElementById(id, nodes, recurse = true) {
3272
3274
  if (!Array.isArray(nodes))
3273
3275
  nodes = [nodes];
3274
- return findOne(getAttribCheck("id", id), nodes, recurse);
3276
+ return findOne$1(getAttribCheck("id", id), nodes, recurse);
3275
3277
  }
3276
3278
  /**
3277
3279
  * Returns all nodes with the supplied `tagName`.
@@ -3638,8 +3640,8 @@ var DomUtils = /*#__PURE__*/Object.freeze({
3638
3640
  existsOne: existsOne,
3639
3641
  filter: filter,
3640
3642
  find: find,
3641
- findAll: findAll,
3642
- findOne: findOne,
3643
+ findAll: findAll$1,
3644
+ findOne: findOne$1,
3643
3645
  findOneChild: findOneChild,
3644
3646
  getAttributeValue: getAttributeValue,
3645
3647
  getChildren: getChildren,
@@ -3741,15 +3743,49 @@ var AttributeAction;
3741
3743
  AttributeAction["Start"] = "start";
3742
3744
  })(AttributeAction || (AttributeAction = {}));
3743
3745
 
3744
- const reName = /^[^\\#]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
3746
+ const reName = /^[^#\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\u00B0-\uFFFF-])+/;
3745
3747
  const reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
3748
+ var CharCode;
3749
+ (function (CharCode) {
3750
+ CharCode[CharCode["LeftParenthesis"] = 40] = "LeftParenthesis";
3751
+ CharCode[CharCode["RightParenthesis"] = 41] = "RightParenthesis";
3752
+ CharCode[CharCode["LeftSquareBracket"] = 91] = "LeftSquareBracket";
3753
+ CharCode[CharCode["RightSquareBracket"] = 93] = "RightSquareBracket";
3754
+ CharCode[CharCode["Comma"] = 44] = "Comma";
3755
+ CharCode[CharCode["Period"] = 46] = "Period";
3756
+ CharCode[CharCode["Colon"] = 58] = "Colon";
3757
+ CharCode[CharCode["SingleQuote"] = 39] = "SingleQuote";
3758
+ CharCode[CharCode["DoubleQuote"] = 34] = "DoubleQuote";
3759
+ CharCode[CharCode["Plus"] = 43] = "Plus";
3760
+ CharCode[CharCode["Tilde"] = 126] = "Tilde";
3761
+ CharCode[CharCode["QuestionMark"] = 63] = "QuestionMark";
3762
+ CharCode[CharCode["ExclamationMark"] = 33] = "ExclamationMark";
3763
+ CharCode[CharCode["Slash"] = 47] = "Slash";
3764
+ CharCode[CharCode["Equal"] = 61] = "Equal";
3765
+ CharCode[CharCode["Dollar"] = 36] = "Dollar";
3766
+ CharCode[CharCode["Pipe"] = 124] = "Pipe";
3767
+ CharCode[CharCode["Circumflex"] = 94] = "Circumflex";
3768
+ CharCode[CharCode["Asterisk"] = 42] = "Asterisk";
3769
+ CharCode[CharCode["GreaterThan"] = 62] = "GreaterThan";
3770
+ CharCode[CharCode["LessThan"] = 60] = "LessThan";
3771
+ CharCode[CharCode["Hash"] = 35] = "Hash";
3772
+ CharCode[CharCode["LowerI"] = 105] = "LowerI";
3773
+ CharCode[CharCode["LowerS"] = 115] = "LowerS";
3774
+ CharCode[CharCode["BackSlash"] = 92] = "BackSlash";
3775
+ // Whitespace
3776
+ CharCode[CharCode["Space"] = 32] = "Space";
3777
+ CharCode[CharCode["Tab"] = 9] = "Tab";
3778
+ CharCode[CharCode["NewLine"] = 10] = "NewLine";
3779
+ CharCode[CharCode["FormFeed"] = 12] = "FormFeed";
3780
+ CharCode[CharCode["CarriageReturn"] = 13] = "CarriageReturn";
3781
+ })(CharCode || (CharCode = {}));
3746
3782
  const actionTypes = new Map([
3747
- [126 /* Tilde */, AttributeAction.Element],
3748
- [94 /* Circumflex */, AttributeAction.Start],
3749
- [36 /* Dollar */, AttributeAction.End],
3750
- [42 /* Asterisk */, AttributeAction.Any],
3751
- [33 /* ExclamationMark */, AttributeAction.Not],
3752
- [124 /* Pipe */, AttributeAction.Hyphen],
3783
+ [CharCode.Tilde, AttributeAction.Element],
3784
+ [CharCode.Circumflex, AttributeAction.Start],
3785
+ [CharCode.Dollar, AttributeAction.End],
3786
+ [CharCode.Asterisk, AttributeAction.Any],
3787
+ [CharCode.ExclamationMark, AttributeAction.Not],
3788
+ [CharCode.Pipe, AttributeAction.Hyphen],
3753
3789
  ]);
3754
3790
  // Pseudos, whose data property is parsed as well.
3755
3791
  const unpackPseudos = new Set([
@@ -3761,6 +3797,18 @@ const unpackPseudos = new Set([
3761
3797
  "host",
3762
3798
  "host-context",
3763
3799
  ]);
3800
+ /**
3801
+ * Pseudo elements defined in CSS Level 1 and CSS Level 2 can be written with
3802
+ * a single colon; eg. :before will turn into ::before.
3803
+ *
3804
+ * @see {@link https://www.w3.org/TR/2018/WD-selectors-4-20181121/#pseudo-element-syntax}
3805
+ */
3806
+ const pseudosToPseudoElements = new Set([
3807
+ "before",
3808
+ "after",
3809
+ "first-line",
3810
+ "first-letter",
3811
+ ]);
3764
3812
  /**
3765
3813
  * Checks whether a specific selector is a traversal.
3766
3814
  * This is useful eg. in swapping the order of elements that
@@ -3775,43 +3823,44 @@ function isTraversal$1(selector) {
3775
3823
  case SelectorType.Descendant:
3776
3824
  case SelectorType.Parent:
3777
3825
  case SelectorType.Sibling:
3778
- case SelectorType.ColumnCombinator:
3826
+ case SelectorType.ColumnCombinator: {
3779
3827
  return true;
3780
- default:
3828
+ }
3829
+ default: {
3781
3830
  return false;
3831
+ }
3782
3832
  }
3783
3833
  }
3784
3834
  const stripQuotesFromPseudos = new Set(["contains", "icontains"]);
3785
3835
  // Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
3786
3836
  function funescape(_, escaped, escapedWhitespace) {
3787
- const high = parseInt(escaped, 16) - 0x10000;
3837
+ const high = Number.parseInt(escaped, 16) - 65536;
3788
3838
  // NaN means non-codepoint
3789
3839
  return high !== high || escapedWhitespace
3790
3840
  ? escaped
3791
3841
  : high < 0
3792
3842
  ? // BMP codepoint
3793
- String.fromCharCode(high + 0x10000)
3843
+ String.fromCharCode(high + 65536)
3794
3844
  : // Supplemental Plane codepoint (surrogate pair)
3795
- String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
3845
+ String.fromCharCode((high >> 10) | 55296, (high & 1023) | 56320);
3796
3846
  }
3797
- function unescapeCSS(str) {
3798
- return str.replace(reEscape, funescape);
3847
+ function unescapeCSS(cssString) {
3848
+ return cssString.replace(reEscape, funescape);
3799
3849
  }
3800
3850
  function isQuote(c) {
3801
- return c === 39 /* SingleQuote */ || c === 34 /* DoubleQuote */;
3851
+ return c === CharCode.SingleQuote || c === CharCode.DoubleQuote;
3802
3852
  }
3803
3853
  function isWhitespace(c) {
3804
- return (c === 32 /* Space */ ||
3805
- c === 9 /* Tab */ ||
3806
- c === 10 /* NewLine */ ||
3807
- c === 12 /* FormFeed */ ||
3808
- c === 13 /* CarriageReturn */);
3854
+ return (c === CharCode.Space ||
3855
+ c === CharCode.Tab ||
3856
+ c === CharCode.NewLine ||
3857
+ c === CharCode.FormFeed ||
3858
+ c === CharCode.CarriageReturn);
3809
3859
  }
3810
3860
  /**
3811
- * Parses `selector`, optionally with the passed `options`.
3861
+ * Parses `selector`.
3812
3862
  *
3813
3863
  * @param selector Selector to parse.
3814
- * @param options Options for parsing.
3815
3864
  * @returns Returns a two-dimensional array.
3816
3865
  * The first dimension represents selectors separated by commas (eg. `sub1, sub2`),
3817
3866
  * the second contains the relevant tokens for that selector.
@@ -3845,29 +3894,27 @@ function parseSelector(subselects, selector, selectorIndex) {
3845
3894
  function readValueWithParenthesis() {
3846
3895
  selectorIndex += 1;
3847
3896
  const start = selectorIndex;
3848
- let counter = 1;
3849
- for (; counter > 0 && selectorIndex < selector.length; selectorIndex++) {
3850
- if (selector.charCodeAt(selectorIndex) ===
3851
- 40 /* LeftParenthesis */ &&
3852
- !isEscaped(selectorIndex)) {
3853
- counter++;
3854
- }
3855
- else if (selector.charCodeAt(selectorIndex) ===
3856
- 41 /* RightParenthesis */ &&
3857
- !isEscaped(selectorIndex)) {
3858
- counter--;
3897
+ for (let counter = 1; selectorIndex < selector.length; selectorIndex++) {
3898
+ switch (selector.charCodeAt(selectorIndex)) {
3899
+ case CharCode.BackSlash: {
3900
+ // Skip next character
3901
+ selectorIndex += 1;
3902
+ break;
3903
+ }
3904
+ case CharCode.LeftParenthesis: {
3905
+ counter += 1;
3906
+ break;
3907
+ }
3908
+ case CharCode.RightParenthesis: {
3909
+ counter -= 1;
3910
+ if (counter === 0) {
3911
+ return unescapeCSS(selector.slice(start, selectorIndex++));
3912
+ }
3913
+ break;
3914
+ }
3859
3915
  }
3860
3916
  }
3861
- if (counter) {
3862
- throw new Error("Parenthesis not matched");
3863
- }
3864
- return unescapeCSS(selector.slice(start, selectorIndex - 1));
3865
- }
3866
- function isEscaped(pos) {
3867
- let slashCount = 0;
3868
- while (selector.charCodeAt(--pos) === 92 /* BackSlash */)
3869
- slashCount++;
3870
- return (slashCount & 1) === 1;
3917
+ throw new Error("Parenthesis not matched");
3871
3918
  }
3872
3919
  function ensureNotTraversal() {
3873
3920
  if (tokens.length > 0 && isTraversal$1(tokens[tokens.length - 1])) {
@@ -3901,7 +3948,7 @@ function parseSelector(subselects, selector, selectorIndex) {
3901
3948
  * picked up from here.
3902
3949
  */
3903
3950
  function finalizeSubselector() {
3904
- if (tokens.length &&
3951
+ if (tokens.length > 0 &&
3905
3952
  tokens[tokens.length - 1].type === SelectorType.Descendant) {
3906
3953
  tokens.pop();
3907
3954
  }
@@ -3918,11 +3965,11 @@ function parseSelector(subselects, selector, selectorIndex) {
3918
3965
  const firstChar = selector.charCodeAt(selectorIndex);
3919
3966
  switch (firstChar) {
3920
3967
  // Whitespace
3921
- case 32 /* Space */:
3922
- case 9 /* Tab */:
3923
- case 10 /* NewLine */:
3924
- case 12 /* FormFeed */:
3925
- case 13 /* CarriageReturn */: {
3968
+ case CharCode.Space:
3969
+ case CharCode.Tab:
3970
+ case CharCode.NewLine:
3971
+ case CharCode.FormFeed:
3972
+ case CharCode.CarriageReturn: {
3926
3973
  if (tokens.length === 0 ||
3927
3974
  tokens[0].type !== SelectorType.Descendant) {
3928
3975
  ensureNotTraversal();
@@ -3932,41 +3979,41 @@ function parseSelector(subselects, selector, selectorIndex) {
3932
3979
  break;
3933
3980
  }
3934
3981
  // Traversals
3935
- case 62 /* GreaterThan */: {
3982
+ case CharCode.GreaterThan: {
3936
3983
  addTraversal(SelectorType.Child);
3937
3984
  stripWhitespace(1);
3938
3985
  break;
3939
3986
  }
3940
- case 60 /* LessThan */: {
3987
+ case CharCode.LessThan: {
3941
3988
  addTraversal(SelectorType.Parent);
3942
3989
  stripWhitespace(1);
3943
3990
  break;
3944
3991
  }
3945
- case 126 /* Tilde */: {
3992
+ case CharCode.Tilde: {
3946
3993
  addTraversal(SelectorType.Sibling);
3947
3994
  stripWhitespace(1);
3948
3995
  break;
3949
3996
  }
3950
- case 43 /* Plus */: {
3997
+ case CharCode.Plus: {
3951
3998
  addTraversal(SelectorType.Adjacent);
3952
3999
  stripWhitespace(1);
3953
4000
  break;
3954
4001
  }
3955
4002
  // Special attribute selectors: .class, #id
3956
- case 46 /* Period */: {
4003
+ case CharCode.Period: {
3957
4004
  addSpecialAttribute("class", AttributeAction.Element);
3958
4005
  break;
3959
4006
  }
3960
- case 35 /* Hash */: {
4007
+ case CharCode.Hash: {
3961
4008
  addSpecialAttribute("id", AttributeAction.Equals);
3962
4009
  break;
3963
4010
  }
3964
- case 91 /* LeftSquareBracket */: {
4011
+ case CharCode.LeftSquareBracket: {
3965
4012
  stripWhitespace(1);
3966
4013
  // Determine attribute name and namespace
3967
4014
  let name;
3968
4015
  let namespace = null;
3969
- if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */) {
4016
+ if (selector.charCodeAt(selectorIndex) === CharCode.Pipe) {
3970
4017
  // Equivalent to no namespace
3971
4018
  name = getName(1);
3972
4019
  }
@@ -3976,9 +4023,9 @@ function parseSelector(subselects, selector, selectorIndex) {
3976
4023
  }
3977
4024
  else {
3978
4025
  name = getName(0);
3979
- if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */ &&
4026
+ if (selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
3980
4027
  selector.charCodeAt(selectorIndex + 1) !==
3981
- 61 /* Equal */) {
4028
+ CharCode.Equal) {
3982
4029
  namespace = name;
3983
4030
  name = getName(1);
3984
4031
  }
@@ -3990,12 +4037,12 @@ function parseSelector(subselects, selector, selectorIndex) {
3990
4037
  if (possibleAction) {
3991
4038
  action = possibleAction;
3992
4039
  if (selector.charCodeAt(selectorIndex + 1) !==
3993
- 61 /* Equal */) {
4040
+ CharCode.Equal) {
3994
4041
  throw new Error("Expected `=`");
3995
4042
  }
3996
4043
  stripWhitespace(2);
3997
4044
  }
3998
- else if (selector.charCodeAt(selectorIndex) === 61 /* Equal */) {
4045
+ else if (selector.charCodeAt(selectorIndex) === CharCode.Equal) {
3999
4046
  action = AttributeAction.Equals;
4000
4047
  stripWhitespace(1);
4001
4048
  }
@@ -4005,44 +4052,56 @@ function parseSelector(subselects, selector, selectorIndex) {
4005
4052
  if (action !== "exists") {
4006
4053
  if (isQuote(selector.charCodeAt(selectorIndex))) {
4007
4054
  const quote = selector.charCodeAt(selectorIndex);
4008
- let sectionEnd = selectorIndex + 1;
4009
- while (sectionEnd < selector.length &&
4010
- (selector.charCodeAt(sectionEnd) !== quote ||
4011
- isEscaped(sectionEnd))) {
4012
- sectionEnd += 1;
4055
+ selectorIndex += 1;
4056
+ const sectionStart = selectorIndex;
4057
+ while (selectorIndex < selector.length &&
4058
+ selector.charCodeAt(selectorIndex) !== quote) {
4059
+ selectorIndex +=
4060
+ // Skip next character if it is escaped
4061
+ selector.charCodeAt(selectorIndex) ===
4062
+ CharCode.BackSlash
4063
+ ? 2
4064
+ : 1;
4013
4065
  }
4014
- if (selector.charCodeAt(sectionEnd) !== quote) {
4066
+ if (selector.charCodeAt(selectorIndex) !== quote) {
4015
4067
  throw new Error("Attribute value didn't end");
4016
4068
  }
4017
- value = unescapeCSS(selector.slice(selectorIndex + 1, sectionEnd));
4018
- selectorIndex = sectionEnd + 1;
4069
+ value = unescapeCSS(selector.slice(sectionStart, selectorIndex));
4070
+ selectorIndex += 1;
4019
4071
  }
4020
4072
  else {
4021
4073
  const valueStart = selectorIndex;
4022
4074
  while (selectorIndex < selector.length &&
4023
- ((!isWhitespace(selector.charCodeAt(selectorIndex)) &&
4024
- selector.charCodeAt(selectorIndex) !==
4025
- 93 /* RightSquareBracket */) ||
4026
- isEscaped(selectorIndex))) {
4027
- selectorIndex += 1;
4075
+ !isWhitespace(selector.charCodeAt(selectorIndex)) &&
4076
+ selector.charCodeAt(selectorIndex) !==
4077
+ CharCode.RightSquareBracket) {
4078
+ selectorIndex +=
4079
+ // Skip next character if it is escaped
4080
+ selector.charCodeAt(selectorIndex) ===
4081
+ CharCode.BackSlash
4082
+ ? 2
4083
+ : 1;
4028
4084
  }
4029
4085
  value = unescapeCSS(selector.slice(valueStart, selectorIndex));
4030
4086
  }
4031
4087
  stripWhitespace(0);
4032
4088
  // See if we have a force ignore flag
4033
- const forceIgnore = selector.charCodeAt(selectorIndex) | 0x20;
4034
- // If the forceIgnore flag is set (either `i` or `s`), use that value
4035
- if (forceIgnore === 115 /* LowerS */) {
4036
- ignoreCase = false;
4037
- stripWhitespace(1);
4038
- }
4039
- else if (forceIgnore === 105 /* LowerI */) {
4040
- ignoreCase = true;
4041
- stripWhitespace(1);
4089
+ switch (selector.charCodeAt(selectorIndex) | 0x20) {
4090
+ // If the forceIgnore flag is set (either `i` or `s`), use that value
4091
+ case CharCode.LowerI: {
4092
+ ignoreCase = true;
4093
+ stripWhitespace(1);
4094
+ break;
4095
+ }
4096
+ case CharCode.LowerS: {
4097
+ ignoreCase = false;
4098
+ stripWhitespace(1);
4099
+ break;
4100
+ }
4042
4101
  }
4043
4102
  }
4044
4103
  if (selector.charCodeAt(selectorIndex) !==
4045
- 93 /* RightSquareBracket */) {
4104
+ CharCode.RightSquareBracket) {
4046
4105
  throw new Error("Attribute selector didn't terminate");
4047
4106
  }
4048
4107
  selectorIndex += 1;
@@ -4057,22 +4116,30 @@ function parseSelector(subselects, selector, selectorIndex) {
4057
4116
  tokens.push(attributeSelector);
4058
4117
  break;
4059
4118
  }
4060
- case 58 /* Colon */: {
4061
- if (selector.charCodeAt(selectorIndex + 1) === 58 /* Colon */) {
4119
+ case CharCode.Colon: {
4120
+ if (selector.charCodeAt(selectorIndex + 1) === CharCode.Colon) {
4062
4121
  tokens.push({
4063
4122
  type: SelectorType.PseudoElement,
4064
4123
  name: getName(2).toLowerCase(),
4065
4124
  data: selector.charCodeAt(selectorIndex) ===
4066
- 40 /* LeftParenthesis */
4125
+ CharCode.LeftParenthesis
4067
4126
  ? readValueWithParenthesis()
4068
4127
  : null,
4069
4128
  });
4070
- continue;
4129
+ break;
4071
4130
  }
4072
4131
  const name = getName(1).toLowerCase();
4132
+ if (pseudosToPseudoElements.has(name)) {
4133
+ tokens.push({
4134
+ type: SelectorType.PseudoElement,
4135
+ name,
4136
+ data: null,
4137
+ });
4138
+ break;
4139
+ }
4073
4140
  let data = null;
4074
4141
  if (selector.charCodeAt(selectorIndex) ===
4075
- 40 /* LeftParenthesis */) {
4142
+ CharCode.LeftParenthesis) {
4076
4143
  if (unpackPseudos.has(name)) {
4077
4144
  if (isQuote(selector.charCodeAt(selectorIndex + 1))) {
4078
4145
  throw new Error(`Pseudo-selector ${name} cannot be quoted`);
@@ -4080,7 +4147,7 @@ function parseSelector(subselects, selector, selectorIndex) {
4080
4147
  data = [];
4081
4148
  selectorIndex = parseSelector(data, selector, selectorIndex + 1);
4082
4149
  if (selector.charCodeAt(selectorIndex) !==
4083
- 41 /* RightParenthesis */) {
4150
+ CharCode.RightParenthesis) {
4084
4151
  throw new Error(`Missing closing parenthesis in :${name} (${selector})`);
4085
4152
  }
4086
4153
  selectorIndex += 1;
@@ -4100,7 +4167,7 @@ function parseSelector(subselects, selector, selectorIndex) {
4100
4167
  tokens.push({ type: SelectorType.Pseudo, name, data });
4101
4168
  break;
4102
4169
  }
4103
- case 44 /* Comma */: {
4170
+ case CharCode.Comma: {
4104
4171
  finalizeSubselector();
4105
4172
  tokens = [];
4106
4173
  stripWhitespace(1);
@@ -4121,13 +4188,13 @@ function parseSelector(subselects, selector, selectorIndex) {
4121
4188
  }
4122
4189
  let namespace = null;
4123
4190
  let name;
4124
- if (firstChar === 42 /* Asterisk */) {
4191
+ if (firstChar === CharCode.Asterisk) {
4125
4192
  selectorIndex += 1;
4126
4193
  name = "*";
4127
4194
  }
4128
- else if (firstChar === 124 /* Pipe */) {
4195
+ else if (firstChar === CharCode.Pipe) {
4129
4196
  name = "";
4130
- if (selector.charCodeAt(selectorIndex + 1) === 124 /* Pipe */) {
4197
+ if (selector.charCodeAt(selectorIndex + 1) === CharCode.Pipe) {
4131
4198
  addTraversal(SelectorType.ColumnCombinator);
4132
4199
  stripWhitespace(2);
4133
4200
  break;
@@ -4139,11 +4206,11 @@ function parseSelector(subselects, selector, selectorIndex) {
4139
4206
  else {
4140
4207
  break loop;
4141
4208
  }
4142
- if (selector.charCodeAt(selectorIndex) === 124 /* Pipe */ &&
4143
- selector.charCodeAt(selectorIndex + 1) !== 124 /* Pipe */) {
4209
+ if (selector.charCodeAt(selectorIndex) === CharCode.Pipe &&
4210
+ selector.charCodeAt(selectorIndex + 1) !== CharCode.Pipe) {
4144
4211
  namespace = name;
4145
4212
  if (selector.charCodeAt(selectorIndex + 1) ===
4146
- 42 /* Asterisk */) {
4213
+ CharCode.Asterisk) {
4147
4214
  name = "*";
4148
4215
  selectorIndex += 2;
4149
4216
  }
@@ -4161,84 +4228,6 @@ function parseSelector(subselects, selector, selectorIndex) {
4161
4228
  return selectorIndex;
4162
4229
  }
4163
4230
 
4164
- const procedure = new Map([
4165
- [SelectorType.Universal, 50],
4166
- [SelectorType.Tag, 30],
4167
- [SelectorType.Attribute, 1],
4168
- [SelectorType.Pseudo, 0],
4169
- ]);
4170
- function isTraversal(token) {
4171
- return !procedure.has(token.type);
4172
- }
4173
- const attributes = new Map([
4174
- [AttributeAction.Exists, 10],
4175
- [AttributeAction.Equals, 8],
4176
- [AttributeAction.Not, 7],
4177
- [AttributeAction.Start, 6],
4178
- [AttributeAction.End, 6],
4179
- [AttributeAction.Any, 5],
4180
- ]);
4181
- /**
4182
- * Sort the parts of the passed selector,
4183
- * as there is potential for optimization
4184
- * (some types of selectors are faster than others)
4185
- *
4186
- * @param arr Selector to sort
4187
- */
4188
- function sortByProcedure(arr) {
4189
- const procs = arr.map(getProcedure);
4190
- for (let i = 1; i < arr.length; i++) {
4191
- const procNew = procs[i];
4192
- if (procNew < 0)
4193
- continue;
4194
- for (let j = i - 1; j >= 0 && procNew < procs[j]; j--) {
4195
- const token = arr[j + 1];
4196
- arr[j + 1] = arr[j];
4197
- arr[j] = token;
4198
- procs[j + 1] = procs[j];
4199
- procs[j] = procNew;
4200
- }
4201
- }
4202
- }
4203
- function getProcedure(token) {
4204
- var _a, _b;
4205
- let proc = (_a = procedure.get(token.type)) !== null && _a !== void 0 ? _a : -1;
4206
- if (token.type === SelectorType.Attribute) {
4207
- proc = (_b = attributes.get(token.action)) !== null && _b !== void 0 ? _b : 4;
4208
- if (token.action === AttributeAction.Equals && token.name === "id") {
4209
- // Prefer ID selectors (eg. #ID)
4210
- proc = 9;
4211
- }
4212
- if (token.ignoreCase) {
4213
- /*
4214
- * IgnoreCase adds some overhead, prefer "normal" token
4215
- * this is a binary operation, to ensure it's still an int
4216
- */
4217
- proc >>= 1;
4218
- }
4219
- }
4220
- else if (token.type === SelectorType.Pseudo) {
4221
- if (!token.data) {
4222
- proc = 3;
4223
- }
4224
- else if (token.name === "has" || token.name === "contains") {
4225
- proc = 0; // Expensive in any case
4226
- }
4227
- else if (Array.isArray(token.data)) {
4228
- // Eg. :matches, :not
4229
- proc = Math.min(...token.data.map((d) => Math.min(...d.map(getProcedure))));
4230
- // If we have traversals, try to avoid executing this selector
4231
- if (proc < 0) {
4232
- proc = 0;
4233
- }
4234
- }
4235
- else {
4236
- proc = 2;
4237
- }
4238
- }
4239
- return proc;
4240
- }
4241
-
4242
4231
  /**
4243
4232
  * All reserved characters in a regex, used for escaping.
4244
4233
  *
@@ -4357,7 +4346,7 @@ const attributeRules = {
4357
4346
  const { adapter } = options;
4358
4347
  const { name, value } = data;
4359
4348
  if (/\s/.test(value)) {
4360
- return boolbase.falseFunc;
4349
+ return boolbaseExports.falseFunc;
4361
4350
  }
4362
4351
  const regex = new RegExp(`(?:^|\\s)${escapeRegex(value)}(?:$|\\s)`, shouldIgnoreCase(data, options) ? "i" : "");
4363
4352
  return function element(elem) {
@@ -4377,7 +4366,7 @@ const attributeRules = {
4377
4366
  let { value } = data;
4378
4367
  const len = value.length;
4379
4368
  if (len === 0) {
4380
- return boolbase.falseFunc;
4369
+ return boolbaseExports.falseFunc;
4381
4370
  }
4382
4371
  if (shouldIgnoreCase(data, options)) {
4383
4372
  value = value.toLowerCase();
@@ -4389,11 +4378,8 @@ const attributeRules = {
4389
4378
  next(elem));
4390
4379
  };
4391
4380
  }
4392
- return (elem) => {
4393
- var _a;
4394
- return !!((_a = adapter.getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.startsWith(value)) &&
4395
- next(elem);
4396
- };
4381
+ return (elem) => !!adapter.getAttributeValue(elem, name)?.startsWith(value) &&
4382
+ next(elem);
4397
4383
  },
4398
4384
  end(next, data, options) {
4399
4385
  const { adapter } = options;
@@ -4401,27 +4387,23 @@ const attributeRules = {
4401
4387
  let { value } = data;
4402
4388
  const len = -value.length;
4403
4389
  if (len === 0) {
4404
- return boolbase.falseFunc;
4390
+ return boolbaseExports.falseFunc;
4405
4391
  }
4406
4392
  if (shouldIgnoreCase(data, options)) {
4407
4393
  value = value.toLowerCase();
4408
- return (elem) => {
4409
- var _a;
4410
- return ((_a = adapter
4411
- .getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.substr(len).toLowerCase()) === value && next(elem);
4412
- };
4394
+ return (elem) => adapter
4395
+ .getAttributeValue(elem, name)
4396
+ ?.substr(len)
4397
+ .toLowerCase() === value && next(elem);
4413
4398
  }
4414
- return (elem) => {
4415
- var _a;
4416
- return !!((_a = adapter.getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.endsWith(value)) &&
4417
- next(elem);
4418
- };
4399
+ return (elem) => !!adapter.getAttributeValue(elem, name)?.endsWith(value) &&
4400
+ next(elem);
4419
4401
  },
4420
4402
  any(next, data, options) {
4421
4403
  const { adapter } = options;
4422
4404
  const { name, value } = data;
4423
4405
  if (value === "") {
4424
- return boolbase.falseFunc;
4406
+ return boolbaseExports.falseFunc;
4425
4407
  }
4426
4408
  if (shouldIgnoreCase(data, options)) {
4427
4409
  const regex = new RegExp(escapeRegex(value), "i");
@@ -4433,11 +4415,8 @@ const attributeRules = {
4433
4415
  next(elem));
4434
4416
  };
4435
4417
  }
4436
- return (elem) => {
4437
- var _a;
4438
- return !!((_a = adapter.getAttributeValue(elem, name)) === null || _a === void 0 ? void 0 : _a.includes(value)) &&
4439
- next(elem);
4440
- };
4418
+ return (elem) => !!adapter.getAttributeValue(elem, name)?.includes(value) &&
4419
+ next(elem);
4441
4420
  },
4442
4421
  not(next, data, options) {
4443
4422
  const { adapter } = options;
@@ -4446,7 +4425,7 @@ const attributeRules = {
4446
4425
  if (value === "") {
4447
4426
  return (elem) => !!adapter.getAttributeValue(elem, name) && next(elem);
4448
4427
  }
4449
- else if (shouldIgnoreCase(data, options)) {
4428
+ if (shouldIgnoreCase(data, options)) {
4450
4429
  value = value.toLowerCase();
4451
4430
  return (elem) => {
4452
4431
  const attr = adapter.getAttributeValue(elem, name);
@@ -4460,6 +4439,169 @@ const attributeRules = {
4460
4439
  },
4461
4440
  };
4462
4441
 
4442
+ /**
4443
+ * Find all elements matching the query. If not in XML mode, the query will ignore
4444
+ * the contents of `<template>` elements.
4445
+ *
4446
+ * @param query - Function that returns true if the element matches the query.
4447
+ * @param elems - Nodes to query. If a node is an element, its children will be queried.
4448
+ * @param options - Options for querying the document.
4449
+ * @returns All matching elements.
4450
+ */
4451
+ function findAll(query, elems, options) {
4452
+ const { adapter, xmlMode = false } = options;
4453
+ const result = [];
4454
+ /** Stack of the arrays we are looking at. */
4455
+ const nodeStack = [elems];
4456
+ /** Stack of the indices within the arrays. */
4457
+ const indexStack = [0];
4458
+ for (;;) {
4459
+ // First, check if the current array has any more elements to look at.
4460
+ if (indexStack[0] >= nodeStack[0].length) {
4461
+ // If we have no more arrays to look at, we are done.
4462
+ if (nodeStack.length === 1) {
4463
+ return result;
4464
+ }
4465
+ nodeStack.shift();
4466
+ indexStack.shift();
4467
+ // Loop back to the start to continue with the next array.
4468
+ continue;
4469
+ }
4470
+ const elem = nodeStack[0][indexStack[0]++];
4471
+ if (!adapter.isTag(elem)) {
4472
+ continue;
4473
+ }
4474
+ if (query(elem)) {
4475
+ result.push(elem);
4476
+ }
4477
+ if (xmlMode || adapter.getName(elem) !== "template") {
4478
+ /*
4479
+ * Add the children to the stack. We are depth-first, so this is
4480
+ * the next array we look at.
4481
+ */
4482
+ const children = adapter.getChildren(elem);
4483
+ if (children.length > 0) {
4484
+ nodeStack.unshift(children);
4485
+ indexStack.unshift(0);
4486
+ }
4487
+ }
4488
+ }
4489
+ }
4490
+ /**
4491
+ * Find the first element matching the query. If not in XML mode, the query will ignore
4492
+ * the contents of `<template>` elements.
4493
+ *
4494
+ * @param query - Function that returns true if the element matches the query.
4495
+ * @param elems - Nodes to query. If a node is an element, its children will be queried.
4496
+ * @param options - Options for querying the document.
4497
+ * @returns The first matching element, or null if there was no match.
4498
+ */
4499
+ function findOne(query, elems, options) {
4500
+ const { adapter, xmlMode = false } = options;
4501
+ /** Stack of the arrays we are looking at. */
4502
+ const nodeStack = [elems];
4503
+ /** Stack of the indices within the arrays. */
4504
+ const indexStack = [0];
4505
+ for (;;) {
4506
+ // First, check if the current array has any more elements to look at.
4507
+ if (indexStack[0] >= nodeStack[0].length) {
4508
+ // If we have no more arrays to look at, we are done.
4509
+ if (nodeStack.length === 1) {
4510
+ return null;
4511
+ }
4512
+ nodeStack.shift();
4513
+ indexStack.shift();
4514
+ // Loop back to the start to continue with the next array.
4515
+ continue;
4516
+ }
4517
+ const elem = nodeStack[0][indexStack[0]++];
4518
+ if (!adapter.isTag(elem)) {
4519
+ continue;
4520
+ }
4521
+ if (query(elem)) {
4522
+ return elem;
4523
+ }
4524
+ if (xmlMode || adapter.getName(elem) !== "template") {
4525
+ /*
4526
+ * Add the children to the stack. We are depth-first, so this is
4527
+ * the next array we look at.
4528
+ */
4529
+ const children = adapter.getChildren(elem);
4530
+ if (children.length > 0) {
4531
+ nodeStack.unshift(children);
4532
+ indexStack.unshift(0);
4533
+ }
4534
+ }
4535
+ }
4536
+ }
4537
+ function getNextSiblings(elem, adapter) {
4538
+ const siblings = adapter.getSiblings(elem);
4539
+ if (siblings.length <= 1) {
4540
+ return [];
4541
+ }
4542
+ const elemIndex = siblings.indexOf(elem);
4543
+ if (elemIndex < 0 || elemIndex === siblings.length - 1) {
4544
+ return [];
4545
+ }
4546
+ return siblings.slice(elemIndex + 1).filter(adapter.isTag);
4547
+ }
4548
+ function getElementParent(node, adapter) {
4549
+ const parent = adapter.getParent(node);
4550
+ return parent != null && adapter.isTag(parent) ? parent : null;
4551
+ }
4552
+
4553
+ /**
4554
+ * Only text controls can be made read-only, since for other controls (such
4555
+ * as checkboxes and buttons) there is no useful distinction between being
4556
+ * read-only and being disabled.
4557
+ *
4558
+ * @see {@link https://html.spec.whatwg.org/multipage/input.html#attr-input-readonly}
4559
+ */
4560
+ const textControl = "input:is([type=text i],[type=search i],[type=url i],[type=tel i],[type=email i],[type=password i],[type=date i],[type=month i],[type=week i],[type=time i],[type=datetime-local i],[type=number i])";
4561
+ /**
4562
+ * Aliases are pseudos that are expressed as selectors.
4563
+ */
4564
+ const aliases = {
4565
+ // Links
4566
+ "any-link": ":is(a, area, link)[href]",
4567
+ link: ":any-link:not(:visited)",
4568
+ // Forms
4569
+ // https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements
4570
+ disabled: `:is(
4571
+ :is(button, input, select, textarea, optgroup, option)[disabled],
4572
+ optgroup[disabled] > option,
4573
+ fieldset[disabled]:not(fieldset[disabled] legend:first-of-type *)
4574
+ )`,
4575
+ enabled: ":not(:disabled)",
4576
+ checked: ":is(:is(input[type=radio], input[type=checkbox])[checked], :selected)",
4577
+ required: ":is(input, select, textarea)[required]",
4578
+ optional: ":is(input, select, textarea):not([required])",
4579
+ "read-only": `[readonly]:is(textarea, ${textControl})`,
4580
+ "read-write": `:not([readonly]):is(textarea, ${textControl})`,
4581
+ // JQuery extensions
4582
+ /**
4583
+ * `:selected` matches option elements that have the `selected` attribute,
4584
+ * or are the first option element in a select element that does not have
4585
+ * the `multiple` attribute and does not have any option elements with the
4586
+ * `selected` attribute.
4587
+ *
4588
+ * @see https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-selectedness
4589
+ */
4590
+ selected: "option:is([selected], select:not([multiple]):not(:has(> option[selected])) > :first-of-type)",
4591
+ checkbox: "[type=checkbox]",
4592
+ file: "[type=file]",
4593
+ password: "[type=password]",
4594
+ radio: "[type=radio]",
4595
+ reset: "[type=reset]",
4596
+ image: "[type=image]",
4597
+ submit: "[type=submit]",
4598
+ parent: ":not(:empty)",
4599
+ header: ":is(h1, h2, h3, h4, h5, h6)",
4600
+ button: ":is(button, input[type=button])",
4601
+ input: ":is(input, textarea, select, button)",
4602
+ text: "input:is(:not([type!='']), [type=text])",
4603
+ };
4604
+
4463
4605
  // Following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
4464
4606
  // Whitespace as per https://www.w3.org/TR/selectors-3/#lex is " \t\r\n\f"
4465
4607
  const whitespace = new Set([9, 10, 12, 13, 32]);
@@ -4614,38 +4756,73 @@ function nthCheck(formula) {
4614
4756
  return compile(parse(formula));
4615
4757
  }
4616
4758
 
4617
- function getChildFunc(next, adapter) {
4618
- return (elem) => {
4619
- const parent = adapter.getParent(elem);
4620
- return parent != null && adapter.isTag(parent) && next(elem);
4759
+ /**
4760
+ * Some selectors such as `:contains` and (non-relative) `:has` will only be
4761
+ * able to match elements if their parents match the selector (as they contain
4762
+ * a subset of the elements that the parent contains).
4763
+ *
4764
+ * This function wraps the given `matches` function in a function that caches
4765
+ * the results of the parent elements, so that the `matches` function only
4766
+ * needs to be called once for each subtree.
4767
+ */
4768
+ function cacheParentResults(next, { adapter, cacheResults }, matches) {
4769
+ if (cacheResults === false || typeof WeakMap === "undefined") {
4770
+ return (elem) => next(elem) && matches(elem);
4771
+ }
4772
+ // Use a cache to avoid re-checking children of an element.
4773
+ // @ts-expect-error `Node` is not extending object
4774
+ const resultCache = new WeakMap();
4775
+ function addResultToCache(elem) {
4776
+ const result = matches(elem);
4777
+ resultCache.set(elem, result);
4778
+ return result;
4779
+ }
4780
+ return function cachedMatcher(elem) {
4781
+ if (!next(elem)) {
4782
+ return false;
4783
+ }
4784
+ if (resultCache.has(elem)) {
4785
+ return resultCache.get(elem);
4786
+ }
4787
+ // Check all of the element's parents.
4788
+ let node = elem;
4789
+ do {
4790
+ const parent = getElementParent(node, adapter);
4791
+ if (parent === null) {
4792
+ return addResultToCache(elem);
4793
+ }
4794
+ node = parent;
4795
+ } while (!resultCache.has(node));
4796
+ return resultCache.get(node) && addResultToCache(elem);
4621
4797
  };
4622
4798
  }
4799
+
4623
4800
  const filters = {
4624
- contains(next, text, { adapter }) {
4625
- return function contains(elem) {
4626
- return next(elem) && adapter.getText(elem).includes(text);
4627
- };
4801
+ contains(next, text, options) {
4802
+ const { getText } = options.adapter;
4803
+ return cacheParentResults(next, options, (elem) => getText(elem).includes(text));
4628
4804
  },
4629
- icontains(next, text, { adapter }) {
4805
+ icontains(next, text, options) {
4630
4806
  const itext = text.toLowerCase();
4631
- return function icontains(elem) {
4632
- return (next(elem) &&
4633
- adapter.getText(elem).toLowerCase().includes(itext));
4634
- };
4807
+ const { getText } = options.adapter;
4808
+ return cacheParentResults(next, options, (elem) => getText(elem).toLowerCase().includes(itext));
4635
4809
  },
4636
4810
  // Location specific methods
4637
4811
  "nth-child"(next, rule, { adapter, equals }) {
4638
4812
  const func = nthCheck(rule);
4639
- if (func === boolbase.falseFunc)
4640
- return boolbase.falseFunc;
4641
- if (func === boolbase.trueFunc)
4642
- return getChildFunc(next, adapter);
4813
+ if (func === boolbaseExports.falseFunc) {
4814
+ return boolbaseExports.falseFunc;
4815
+ }
4816
+ if (func === boolbaseExports.trueFunc) {
4817
+ return (elem) => getElementParent(elem, adapter) !== null && next(elem);
4818
+ }
4643
4819
  return function nthChild(elem) {
4644
4820
  const siblings = adapter.getSiblings(elem);
4645
4821
  let pos = 0;
4646
4822
  for (let i = 0; i < siblings.length; i++) {
4647
- if (equals(elem, siblings[i]))
4823
+ if (equals(elem, siblings[i])) {
4648
4824
  break;
4825
+ }
4649
4826
  if (adapter.isTag(siblings[i])) {
4650
4827
  pos++;
4651
4828
  }
@@ -4655,16 +4832,19 @@ const filters = {
4655
4832
  },
4656
4833
  "nth-last-child"(next, rule, { adapter, equals }) {
4657
4834
  const func = nthCheck(rule);
4658
- if (func === boolbase.falseFunc)
4659
- return boolbase.falseFunc;
4660
- if (func === boolbase.trueFunc)
4661
- return getChildFunc(next, adapter);
4835
+ if (func === boolbaseExports.falseFunc) {
4836
+ return boolbaseExports.falseFunc;
4837
+ }
4838
+ if (func === boolbaseExports.trueFunc) {
4839
+ return (elem) => getElementParent(elem, adapter) !== null && next(elem);
4840
+ }
4662
4841
  return function nthLastChild(elem) {
4663
4842
  const siblings = adapter.getSiblings(elem);
4664
4843
  let pos = 0;
4665
4844
  for (let i = siblings.length - 1; i >= 0; i--) {
4666
- if (equals(elem, siblings[i]))
4845
+ if (equals(elem, siblings[i])) {
4667
4846
  break;
4847
+ }
4668
4848
  if (adapter.isTag(siblings[i])) {
4669
4849
  pos++;
4670
4850
  }
@@ -4674,17 +4854,20 @@ const filters = {
4674
4854
  },
4675
4855
  "nth-of-type"(next, rule, { adapter, equals }) {
4676
4856
  const func = nthCheck(rule);
4677
- if (func === boolbase.falseFunc)
4678
- return boolbase.falseFunc;
4679
- if (func === boolbase.trueFunc)
4680
- return getChildFunc(next, adapter);
4857
+ if (func === boolbaseExports.falseFunc) {
4858
+ return boolbaseExports.falseFunc;
4859
+ }
4860
+ if (func === boolbaseExports.trueFunc) {
4861
+ return (elem) => getElementParent(elem, adapter) !== null && next(elem);
4862
+ }
4681
4863
  return function nthOfType(elem) {
4682
4864
  const siblings = adapter.getSiblings(elem);
4683
4865
  let pos = 0;
4684
4866
  for (let i = 0; i < siblings.length; i++) {
4685
4867
  const currentSibling = siblings[i];
4686
- if (equals(elem, currentSibling))
4868
+ if (equals(elem, currentSibling)) {
4687
4869
  break;
4870
+ }
4688
4871
  if (adapter.isTag(currentSibling) &&
4689
4872
  adapter.getName(currentSibling) === adapter.getName(elem)) {
4690
4873
  pos++;
@@ -4695,17 +4878,20 @@ const filters = {
4695
4878
  },
4696
4879
  "nth-last-of-type"(next, rule, { adapter, equals }) {
4697
4880
  const func = nthCheck(rule);
4698
- if (func === boolbase.falseFunc)
4699
- return boolbase.falseFunc;
4700
- if (func === boolbase.trueFunc)
4701
- return getChildFunc(next, adapter);
4881
+ if (func === boolbaseExports.falseFunc) {
4882
+ return boolbaseExports.falseFunc;
4883
+ }
4884
+ if (func === boolbaseExports.trueFunc) {
4885
+ return (elem) => getElementParent(elem, adapter) !== null && next(elem);
4886
+ }
4702
4887
  return function nthLastOfType(elem) {
4703
4888
  const siblings = adapter.getSiblings(elem);
4704
4889
  let pos = 0;
4705
4890
  for (let i = siblings.length - 1; i >= 0; i--) {
4706
4891
  const currentSibling = siblings[i];
4707
- if (equals(elem, currentSibling))
4892
+ if (equals(elem, currentSibling)) {
4708
4893
  break;
4894
+ }
4709
4895
  if (adapter.isTag(currentSibling) &&
4710
4896
  adapter.getName(currentSibling) === adapter.getName(elem)) {
4711
4897
  pos++;
@@ -4716,10 +4902,7 @@ const filters = {
4716
4902
  },
4717
4903
  // TODO determine the actual root element
4718
4904
  root(next, _rule, { adapter }) {
4719
- return (elem) => {
4720
- const parent = adapter.getParent(elem);
4721
- return (parent == null || !adapter.isTag(parent)) && next(elem);
4722
- };
4905
+ return (elem) => getElementParent(elem, adapter) === null && next(elem);
4723
4906
  },
4724
4907
  scope(next, rule, options, context) {
4725
4908
  const { equals } = options;
@@ -4747,7 +4930,7 @@ function dynamicStatePseudo(name) {
4747
4930
  return function dynamicPseudo(next, _rule, { adapter }) {
4748
4931
  const func = adapter[name];
4749
4932
  if (typeof func !== "function") {
4750
- return boolbase.falseFunc;
4933
+ return boolbaseExports.falseFunc;
4751
4934
  }
4752
4935
  return function active(elem) {
4753
4936
  return func(elem) && next(elem);
@@ -4755,12 +4938,25 @@ function dynamicStatePseudo(name) {
4755
4938
  };
4756
4939
  }
4757
4940
 
4941
+ /**
4942
+ * CSS limits the characters considered as whitespace to space, tab & line
4943
+ * feed. We add carriage returns as htmlparser2 doesn't normalize them to
4944
+ * line feeds.
4945
+ *
4946
+ * @see {@link https://www.w3.org/TR/css-text-3/#white-space}
4947
+ */
4948
+ const isDocumentWhiteSpace = /^[ \t\r\n]*$/;
4758
4949
  // While filters are precompiled, pseudos get called when they are needed
4759
4950
  const pseudos = {
4760
4951
  empty(elem, { adapter }) {
4761
- return !adapter.getChildren(elem).some((elem) =>
4762
- // FIXME: `getText` call is potentially expensive.
4763
- adapter.isTag(elem) || adapter.getText(elem) !== "");
4952
+ const children = adapter.getChildren(elem);
4953
+ return (
4954
+ // First, make sure the tag does not have any element children.
4955
+ children.every((elem) => !adapter.isTag(elem)) &&
4956
+ // Then, check that the text content is only whitespace.
4957
+ children.every((elem) =>
4958
+ // FIXME: `getText` call is potentially expensive.
4959
+ isDocumentWhiteSpace.test(adapter.getText(elem))));
4764
4960
  },
4765
4961
  "first-child"(elem, { adapter, equals }) {
4766
4962
  if (adapter.prevElementSibling) {
@@ -4774,10 +4970,12 @@ const pseudos = {
4774
4970
  "last-child"(elem, { adapter, equals }) {
4775
4971
  const siblings = adapter.getSiblings(elem);
4776
4972
  for (let i = siblings.length - 1; i >= 0; i--) {
4777
- if (equals(elem, siblings[i]))
4973
+ if (equals(elem, siblings[i])) {
4778
4974
  return true;
4779
- if (adapter.isTag(siblings[i]))
4975
+ }
4976
+ if (adapter.isTag(siblings[i])) {
4780
4977
  break;
4978
+ }
4781
4979
  }
4782
4980
  return false;
4783
4981
  },
@@ -4786,8 +4984,9 @@ const pseudos = {
4786
4984
  const elemName = adapter.getName(elem);
4787
4985
  for (let i = 0; i < siblings.length; i++) {
4788
4986
  const currentSibling = siblings[i];
4789
- if (equals(elem, currentSibling))
4987
+ if (equals(elem, currentSibling)) {
4790
4988
  return true;
4989
+ }
4791
4990
  if (adapter.isTag(currentSibling) &&
4792
4991
  adapter.getName(currentSibling) === elemName) {
4793
4992
  break;
@@ -4800,8 +4999,9 @@ const pseudos = {
4800
4999
  const elemName = adapter.getName(elem);
4801
5000
  for (let i = siblings.length - 1; i >= 0; i--) {
4802
5001
  const currentSibling = siblings[i];
4803
- if (equals(elem, currentSibling))
5002
+ if (equals(elem, currentSibling)) {
4804
5003
  return true;
5004
+ }
4805
5005
  if (adapter.isTag(currentSibling) &&
4806
5006
  adapter.getName(currentSibling) === elemName) {
4807
5007
  break;
@@ -4834,56 +5034,123 @@ function verifyPseudoArgs(func, name, subselect, argIndex) {
4834
5034
  }
4835
5035
  }
4836
5036
 
5037
+ function isTraversal(token) {
5038
+ return token.type === "_flexibleDescendant" || isTraversal$1(token);
5039
+ }
4837
5040
  /**
4838
- * Aliases are pseudos that are expressed as selectors.
5041
+ * Sort the parts of the passed selector, as there is potential for
5042
+ * optimization (some types of selectors are faster than others).
5043
+ *
5044
+ * @param arr Selector to sort
4839
5045
  */
4840
- const aliases = {
4841
- // Links
4842
- "any-link": ":is(a, area, link)[href]",
4843
- link: ":any-link:not(:visited)",
4844
- // Forms
4845
- // https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements
4846
- disabled: `:is(
4847
- :is(button, input, select, textarea, optgroup, option)[disabled],
4848
- optgroup[disabled] > option,
4849
- fieldset[disabled]:not(fieldset[disabled] legend:first-of-type *)
4850
- )`,
4851
- enabled: ":not(:disabled)",
4852
- checked: ":is(:is(input[type=radio], input[type=checkbox])[checked], option:selected)",
4853
- required: ":is(input, select, textarea)[required]",
4854
- optional: ":is(input, select, textarea):not([required])",
4855
- // JQuery extensions
4856
- // https://html.spec.whatwg.org/multipage/form-elements.html#concept-option-selectedness
4857
- selected: "option:is([selected], select:not([multiple]):not(:has(> option[selected])) > :first-of-type)",
4858
- checkbox: "[type=checkbox]",
4859
- file: "[type=file]",
4860
- password: "[type=password]",
4861
- radio: "[type=radio]",
4862
- reset: "[type=reset]",
4863
- image: "[type=image]",
4864
- submit: "[type=submit]",
4865
- parent: ":not(:empty)",
4866
- header: ":is(h1, h2, h3, h4, h5, h6)",
4867
- button: ":is(button, input[type=button])",
4868
- input: ":is(input, textarea, select, button)",
4869
- text: "input:is(:not([type!='']), [type=text])",
4870
- };
5046
+ function sortRules(arr) {
5047
+ const ratings = arr.map(getQuality);
5048
+ for (let i = 1; i < arr.length; i++) {
5049
+ const procNew = ratings[i];
5050
+ if (procNew < 0) {
5051
+ continue;
5052
+ }
5053
+ // Use insertion sort to move the token to the correct position.
5054
+ for (let j = i; j > 0 && procNew < ratings[j - 1]; j--) {
5055
+ const token = arr[j];
5056
+ arr[j] = arr[j - 1];
5057
+ arr[j - 1] = token;
5058
+ ratings[j] = ratings[j - 1];
5059
+ ratings[j - 1] = procNew;
5060
+ }
5061
+ }
5062
+ }
5063
+ function getAttributeQuality(token) {
5064
+ switch (token.action) {
5065
+ case AttributeAction.Exists: {
5066
+ return 10;
5067
+ }
5068
+ case AttributeAction.Equals: {
5069
+ // Prefer ID selectors (eg. #ID)
5070
+ return token.name === "id" ? 9 : 8;
5071
+ }
5072
+ case AttributeAction.Not: {
5073
+ return 7;
5074
+ }
5075
+ case AttributeAction.Start: {
5076
+ return 6;
5077
+ }
5078
+ case AttributeAction.End: {
5079
+ return 6;
5080
+ }
5081
+ case AttributeAction.Any: {
5082
+ return 5;
5083
+ }
5084
+ case AttributeAction.Hyphen: {
5085
+ return 4;
5086
+ }
5087
+ case AttributeAction.Element: {
5088
+ return 3;
5089
+ }
5090
+ }
5091
+ }
5092
+ /**
5093
+ * Determine the quality of the passed token. The higher the number, the
5094
+ * faster the token is to execute.
5095
+ *
5096
+ * @param token Token to get the quality of.
5097
+ * @returns The token's quality.
5098
+ */
5099
+ function getQuality(token) {
5100
+ switch (token.type) {
5101
+ case SelectorType.Universal: {
5102
+ return 50;
5103
+ }
5104
+ case SelectorType.Tag: {
5105
+ return 30;
5106
+ }
5107
+ case SelectorType.Attribute: {
5108
+ return Math.floor(getAttributeQuality(token) /
5109
+ // `ignoreCase` adds some overhead, half the result if applicable.
5110
+ (token.ignoreCase ? 2 : 1));
5111
+ }
5112
+ case SelectorType.Pseudo: {
5113
+ return !token.data
5114
+ ? 3
5115
+ : token.name === "has" ||
5116
+ token.name === "contains" ||
5117
+ token.name === "icontains"
5118
+ ? // Expensive in any case — run as late as possible.
5119
+ 0
5120
+ : Array.isArray(token.data)
5121
+ ? // Eg. `:is`, `:not`
5122
+ Math.max(
5123
+ // If we have traversals, try to avoid executing this selector
5124
+ 0, Math.min(...token.data.map((d) => Math.min(...d.map(getQuality)))))
5125
+ : 2;
5126
+ }
5127
+ default: {
5128
+ return -1;
5129
+ }
5130
+ }
5131
+ }
5132
+ function includesScopePseudo(t) {
5133
+ return (t.type === SelectorType.Pseudo &&
5134
+ (t.name === "scope" ||
5135
+ (Array.isArray(t.data) &&
5136
+ t.data.some((data) => data.some(includesScopePseudo)))));
5137
+ }
4871
5138
 
4872
5139
  /** Used as a placeholder for :has. Will be replaced with the actual element. */
4873
5140
  const PLACEHOLDER_ELEMENT = {};
4874
- function ensureIsTag(next, adapter) {
4875
- if (next === boolbase.falseFunc)
4876
- return boolbase.falseFunc;
4877
- return (elem) => adapter.isTag(elem) && next(elem);
4878
- }
4879
- function getNextSiblings(elem, adapter) {
4880
- const siblings = adapter.getSiblings(elem);
4881
- if (siblings.length <= 1)
4882
- return [];
4883
- const elemIndex = siblings.indexOf(elem);
4884
- if (elemIndex < 0 || elemIndex === siblings.length - 1)
4885
- return [];
4886
- return siblings.slice(elemIndex + 1).filter(adapter.isTag);
5141
+ /**
5142
+ * Check if the selector has any properties that rely on the current element.
5143
+ * If not, we can cache the result of the selector.
5144
+ *
5145
+ * We can't cache selectors that start with a traversal (e.g. `>`, `+`, `~`),
5146
+ * or include a `:scope`.
5147
+ *
5148
+ * @param selector - The selector to check.
5149
+ * @returns Whether the selector has any properties that rely on the current element.
5150
+ */
5151
+ function hasDependsOnCurrentElement(selector) {
5152
+ return selector.some((sel) => sel.length > 0 &&
5153
+ (isTraversal(sel[0]) || sel.some(includesScopePseudo)));
4887
5154
  }
4888
5155
  function copyOptions(options) {
4889
5156
  // Not copied: context, rootFunc
@@ -4900,10 +5167,10 @@ function copyOptions(options) {
4900
5167
  }
4901
5168
  const is = (next, token, options, context, compileToken) => {
4902
5169
  const func = compileToken(token, copyOptions(options), context);
4903
- return func === boolbase.trueFunc
5170
+ return func === boolbaseExports.trueFunc
4904
5171
  ? next
4905
- : func === boolbase.falseFunc
4906
- ? boolbase.falseFunc
5172
+ : func === boolbaseExports.falseFunc
5173
+ ? boolbaseExports.falseFunc
4907
5174
  : (elem) => func(elem) && next(elem);
4908
5175
  };
4909
5176
  /*
@@ -4920,10 +5187,10 @@ const subselects = {
4920
5187
  where: is,
4921
5188
  not(next, token, options, context, compileToken) {
4922
5189
  const func = compileToken(token, copyOptions(options), context);
4923
- return func === boolbase.falseFunc
5190
+ return func === boolbaseExports.falseFunc
4924
5191
  ? next
4925
- : func === boolbase.trueFunc
4926
- ? boolbase.falseFunc
5192
+ : func === boolbaseExports.trueFunc
5193
+ ? boolbaseExports.falseFunc
4927
5194
  : (elem) => !func(elem) && next(elem);
4928
5195
  },
4929
5196
  has(next, subselect, options, _context, compileToken) {
@@ -4934,35 +5201,54 @@ const subselects = {
4934
5201
  ? // Used as a placeholder. Will be replaced with the actual element.
4935
5202
  [PLACEHOLDER_ELEMENT]
4936
5203
  : undefined;
5204
+ const skipCache = hasDependsOnCurrentElement(subselect);
4937
5205
  const compiled = compileToken(subselect, opts, context);
4938
- if (compiled === boolbase.falseFunc)
4939
- return boolbase.falseFunc;
4940
- const hasElement = ensureIsTag(compiled, adapter);
5206
+ if (compiled === boolbaseExports.falseFunc) {
5207
+ return boolbaseExports.falseFunc;
5208
+ }
4941
5209
  // If `compiled` is `trueFunc`, we can skip this.
4942
- if (context && compiled !== boolbase.trueFunc) {
4943
- /*
4944
- * `shouldTestNextSiblings` will only be true if the query starts with
4945
- * a traversal (sibling or adjacent). That means we will always have a context.
4946
- */
4947
- const { shouldTestNextSiblings = false } = compiled;
4948
- return (elem) => {
4949
- if (!next(elem))
4950
- return false;
4951
- context[0] = elem;
4952
- const childs = adapter.getChildren(elem);
4953
- const nextElements = shouldTestNextSiblings
4954
- ? [...childs, ...getNextSiblings(elem, adapter)]
4955
- : childs;
4956
- return adapter.existsOne(hasElement, nextElements);
4957
- };
5210
+ if (context && compiled !== boolbaseExports.trueFunc) {
5211
+ return skipCache
5212
+ ? (elem) => {
5213
+ if (!next(elem)) {
5214
+ return false;
5215
+ }
5216
+ context[0] = elem;
5217
+ const childs = adapter.getChildren(elem);
5218
+ return (findOne(compiled, compiled.shouldTestNextSiblings
5219
+ ? [
5220
+ ...childs,
5221
+ ...getNextSiblings(elem, adapter),
5222
+ ]
5223
+ : childs, options) !== null);
5224
+ }
5225
+ : cacheParentResults(next, options, (elem) => {
5226
+ context[0] = elem;
5227
+ return (findOne(compiled, adapter.getChildren(elem), options) !== null);
5228
+ });
4958
5229
  }
4959
- return (elem) => next(elem) &&
4960
- adapter.existsOne(hasElement, adapter.getChildren(elem));
5230
+ const hasOne = (elem) => findOne(compiled, adapter.getChildren(elem), options) !== null;
5231
+ return skipCache
5232
+ ? (elem) => next(elem) && hasOne(elem)
5233
+ : cacheParentResults(next, options, hasOne);
4961
5234
  },
4962
5235
  };
4963
5236
 
5237
+ /*
5238
+ * Pseudo selectors
5239
+ *
5240
+ * Pseudo selectors are available in three forms:
5241
+ *
5242
+ * 1. Filters are called when the selector is compiled and return a function
5243
+ * that has to return either false, or the results of `next()`.
5244
+ * 2. Pseudos are called on execution. They have to return a boolean.
5245
+ * 3. Subselects work like filters, but have an embedded selector that will be run separately.
5246
+ *
5247
+ * Filters are great if you want to do some pre-processing, or change the call order
5248
+ * of `next()` and your code.
5249
+ * Pseudos should be used to implement simple checks.
5250
+ */
4964
5251
  function compilePseudoSelector(next, selector, options, context, compileToken) {
4965
- var _a;
4966
5252
  const { name, data } = selector;
4967
5253
  if (Array.isArray(data)) {
4968
5254
  if (!(name in subselects)) {
@@ -4970,7 +5256,7 @@ function compilePseudoSelector(next, selector, options, context, compileToken) {
4970
5256
  }
4971
5257
  return subselects[name](next, data, options, context, compileToken);
4972
5258
  }
4973
- const userPseudo = (_a = options.pseudos) === null || _a === void 0 ? void 0 : _a[name];
5259
+ const userPseudo = options.pseudos?.[name];
4974
5260
  const stringPseudo = typeof userPseudo === "string" ? userPseudo : aliases[name];
4975
5261
  if (typeof stringPseudo === "string") {
4976
5262
  if (data != null) {
@@ -4995,18 +5281,11 @@ function compilePseudoSelector(next, selector, options, context, compileToken) {
4995
5281
  throw new Error(`Unknown pseudo-class :${name}`);
4996
5282
  }
4997
5283
 
4998
- function getElementParent(node, adapter) {
4999
- const parent = adapter.getParent(node);
5000
- if (parent && adapter.isTag(parent)) {
5001
- return parent;
5002
- }
5003
- return null;
5004
- }
5005
5284
  /*
5006
5285
  * All available rules
5007
5286
  */
5008
- function compileGeneralSelector(next, selector, options, context, compileToken) {
5009
- const { adapter, equals } = options;
5287
+ function compileGeneralSelector(next, selector, options, context, compileToken, hasExpensiveSubselector) {
5288
+ const { adapter, equals, cacheResults } = options;
5010
5289
  switch (selector.type) {
5011
5290
  case SelectorType.PseudoElement: {
5012
5291
  throw new Error("Pseudo-elements are not supported by css-select");
@@ -5041,10 +5320,12 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5041
5320
  }
5042
5321
  // Traversal
5043
5322
  case SelectorType.Descendant: {
5044
- if (options.cacheResults === false ||
5045
- typeof WeakSet === "undefined") {
5323
+ if (!hasExpensiveSubselector ||
5324
+ cacheResults === false ||
5325
+ typeof WeakMap === "undefined") {
5046
5326
  return function descendant(elem) {
5047
5327
  let current = elem;
5328
+ // biome-ignore lint/suspicious/noAssignInExpressions: TODO
5048
5329
  while ((current = getElementParent(current, adapter))) {
5049
5330
  if (next(current)) {
5050
5331
  return true;
@@ -5053,16 +5334,26 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5053
5334
  return false;
5054
5335
  };
5055
5336
  }
5056
- // @ts-expect-error `ElementNode` is not extending object
5057
- const isFalseCache = new WeakSet();
5337
+ const resultCache = new WeakMap();
5058
5338
  return function cachedDescendant(elem) {
5059
5339
  let current = elem;
5340
+ let result;
5341
+ // biome-ignore lint/suspicious/noAssignInExpressions: TODO
5060
5342
  while ((current = getElementParent(current, adapter))) {
5061
- if (!isFalseCache.has(current)) {
5062
- if (adapter.isTag(current) && next(current)) {
5343
+ const cached = resultCache.get(current);
5344
+ if (cached === undefined) {
5345
+ result ?? (result = { matches: false });
5346
+ result.matches = next(current);
5347
+ resultCache.set(current, result);
5348
+ if (result.matches) {
5063
5349
  return true;
5064
5350
  }
5065
- isFalseCache.add(current);
5351
+ }
5352
+ else {
5353
+ if (result) {
5354
+ result.matches = cached.matches;
5355
+ }
5356
+ return cached.matches;
5066
5357
  }
5067
5358
  }
5068
5359
  return false;
@@ -5073,9 +5364,11 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5073
5364
  return function flexibleDescendant(elem) {
5074
5365
  let current = elem;
5075
5366
  do {
5076
- if (next(current))
5367
+ if (next(current)) {
5077
5368
  return true;
5078
- } while ((current = getElementParent(current, adapter)));
5369
+ }
5370
+ current = getElementParent(current, adapter);
5371
+ } while (current);
5079
5372
  return false;
5080
5373
  };
5081
5374
  }
@@ -5088,8 +5381,8 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5088
5381
  }
5089
5382
  case SelectorType.Child: {
5090
5383
  return function child(elem) {
5091
- const parent = adapter.getParent(elem);
5092
- return parent != null && adapter.isTag(parent) && next(parent);
5384
+ const parent = getElementParent(elem, adapter);
5385
+ return parent !== null && next(parent);
5093
5386
  };
5094
5387
  }
5095
5388
  case SelectorType.Sibling: {
@@ -5097,8 +5390,9 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5097
5390
  const siblings = adapter.getSiblings(elem);
5098
5391
  for (let i = 0; i < siblings.length; i++) {
5099
5392
  const currentSibling = siblings[i];
5100
- if (equals(elem, currentSibling))
5393
+ if (equals(elem, currentSibling)) {
5101
5394
  break;
5395
+ }
5102
5396
  if (adapter.isTag(currentSibling) && next(currentSibling)) {
5103
5397
  return true;
5104
5398
  }
@@ -5118,8 +5412,9 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5118
5412
  let lastElement;
5119
5413
  for (let i = 0; i < siblings.length; i++) {
5120
5414
  const currentSibling = siblings[i];
5121
- if (equals(elem, currentSibling))
5415
+ if (equals(elem, currentSibling)) {
5122
5416
  break;
5417
+ }
5123
5418
  if (adapter.isTag(currentSibling)) {
5124
5419
  lastElement = currentSibling;
5125
5420
  }
@@ -5136,16 +5431,6 @@ function compileGeneralSelector(next, selector, options, context, compileToken)
5136
5431
  }
5137
5432
  }
5138
5433
 
5139
- function compileUnsafe(selector, options, context) {
5140
- const token = typeof selector === "string" ? parse$1(selector) : selector;
5141
- return compileToken(token, options, context);
5142
- }
5143
- function includesScopePseudo(t) {
5144
- return (t.type === SelectorType.Pseudo &&
5145
- (t.name === "scope" ||
5146
- (Array.isArray(t.data) &&
5147
- t.data.some((data) => data.some(includesScopePseudo)))));
5148
- }
5149
5434
  const DESCENDANT_TOKEN = { type: SelectorType.Descendant };
5150
5435
  const FLEXIBLE_DESCENDANT_TOKEN = {
5151
5436
  type: "_flexibleDescendant",
@@ -5161,10 +5446,8 @@ const SCOPE_TOKEN = {
5161
5446
  */
5162
5447
  function absolutize(token, { adapter }, context) {
5163
5448
  // TODO Use better check if the context is a document
5164
- const hasContext = !!(context === null || context === void 0 ? void 0 : context.every((e) => {
5165
- const parent = adapter.isTag(e) && adapter.getParent(e);
5166
- return e === PLACEHOLDER_ELEMENT || (parent && adapter.isTag(parent));
5167
- }));
5449
+ const hasContext = !!context?.every((e) => e === PLACEHOLDER_ELEMENT ||
5450
+ (adapter.isTag(e) && getElementParent(e, adapter) !== null));
5168
5451
  for (const t of token) {
5169
5452
  if (t.length > 0 &&
5170
5453
  isTraversal(t[0]) &&
@@ -5178,10 +5461,9 @@ function absolutize(token, { adapter }, context) {
5178
5461
  t.unshift(SCOPE_TOKEN);
5179
5462
  }
5180
5463
  }
5181
- function compileToken(token, options, context) {
5182
- var _a;
5183
- token.forEach(sortByProcedure);
5184
- context = (_a = options.context) !== null && _a !== void 0 ? _a : context;
5464
+ function compileToken(token, options, ctx) {
5465
+ token.forEach(sortRules);
5466
+ const { context = ctx, rootFunc = boolbaseExports.trueFunc } = options;
5185
5467
  const isArrayContext = Array.isArray(context);
5186
5468
  const finalContext = context && (Array.isArray(context) ? context : [context]);
5187
5469
  // Check if the selector is relative
@@ -5192,12 +5474,11 @@ function compileToken(token, options, context) {
5192
5474
  throw new Error("Relative selectors are not allowed when the `relativeSelector` option is disabled");
5193
5475
  }
5194
5476
  let shouldTestNextSiblings = false;
5195
- const query = token
5196
- .map((rules) => {
5477
+ let query = boolbaseExports.falseFunc;
5478
+ combineLoop: for (const rules of token) {
5197
5479
  if (rules.length >= 2) {
5198
5480
  const [first, second] = rules;
5199
- if (first.type !== SelectorType.Pseudo ||
5200
- first.name !== "scope") ;
5481
+ if (first.type !== SelectorType.Pseudo || first.name !== "scope") ;
5201
5482
  else if (isArrayContext &&
5202
5483
  second.type === SelectorType.Descendant) {
5203
5484
  rules[1] = FLEXIBLE_DESCENDANT_TOKEN;
@@ -5207,28 +5488,30 @@ function compileToken(token, options, context) {
5207
5488
  shouldTestNextSiblings = true;
5208
5489
  }
5209
5490
  }
5210
- return compileRules(rules, options, finalContext);
5211
- })
5212
- .reduce(reduceRules, boolbase.falseFunc);
5491
+ let next = rootFunc;
5492
+ let hasExpensiveSubselector = false;
5493
+ for (const rule of rules) {
5494
+ next = compileGeneralSelector(next, rule, options, finalContext, compileToken, hasExpensiveSubselector);
5495
+ const quality = getQuality(rule);
5496
+ if (quality === 0) {
5497
+ hasExpensiveSubselector = true;
5498
+ }
5499
+ // If the sub-selector won't match any elements, skip it.
5500
+ if (next === boolbaseExports.falseFunc) {
5501
+ continue combineLoop;
5502
+ }
5503
+ }
5504
+ // If we have a function that always returns true, we can stop here.
5505
+ if (next === rootFunc) {
5506
+ return rootFunc;
5507
+ }
5508
+ query = query === boolbaseExports.falseFunc ? next : or(query, next);
5509
+ }
5213
5510
  query.shouldTestNextSiblings = shouldTestNextSiblings;
5214
5511
  return query;
5215
5512
  }
5216
- function compileRules(rules, options, context) {
5217
- var _a;
5218
- return rules.reduce((previous, rule) => previous === boolbase.falseFunc
5219
- ? boolbase.falseFunc
5220
- : compileGeneralSelector(previous, rule, options, context, compileToken), (_a = options.rootFunc) !== null && _a !== void 0 ? _a : boolbase.trueFunc);
5221
- }
5222
- function reduceRules(a, b) {
5223
- if (b === boolbase.falseFunc || a === boolbase.trueFunc) {
5224
- return a;
5225
- }
5226
- if (a === boolbase.falseFunc || b === boolbase.trueFunc) {
5227
- return b;
5228
- }
5229
- return function combine(elem) {
5230
- return a(elem) || b(elem);
5231
- };
5513
+ function or(a, b) {
5514
+ return (elem) => a(elem) || b(elem);
5232
5515
  }
5233
5516
 
5234
5517
  const defaultEquals = (a, b) => a === b;
@@ -5237,23 +5520,34 @@ const defaultOptions$1 = {
5237
5520
  equals: defaultEquals,
5238
5521
  };
5239
5522
  function convertOptionFormats(options) {
5240
- var _a, _b, _c, _d;
5241
5523
  /*
5242
5524
  * We force one format of options to the other one.
5243
5525
  */
5244
5526
  // @ts-expect-error Default options may have incompatible `Node` / `ElementNode`.
5245
- const opts = options !== null && options !== void 0 ? options : defaultOptions$1;
5527
+ const opts = options ?? defaultOptions$1;
5246
5528
  // @ts-expect-error Same as above.
5247
- (_a = opts.adapter) !== null && _a !== void 0 ? _a : (opts.adapter = DomUtils);
5529
+ opts.adapter ?? (opts.adapter = DomUtils);
5248
5530
  // @ts-expect-error `equals` does not exist on `Options`
5249
- (_b = opts.equals) !== null && _b !== void 0 ? _b : (opts.equals = (_d = (_c = opts.adapter) === null || _c === void 0 ? void 0 : _c.equals) !== null && _d !== void 0 ? _d : defaultEquals);
5531
+ opts.equals ?? (opts.equals = opts.adapter?.equals ?? defaultEquals);
5250
5532
  return opts;
5251
5533
  }
5534
+ /**
5535
+ * Like `compile`, but does not add a check if elements are tags.
5536
+ */
5537
+ function _compileUnsafe(selector, options, context) {
5538
+ return _compileToken(typeof selector === "string" ? parse$1(selector) : selector, options, context);
5539
+ }
5540
+ /**
5541
+ * @deprecated Use `_compileUnsafe` instead.
5542
+ */
5543
+ function _compileToken(selector, options, context) {
5544
+ return compileToken(selector, convertOptionFormats(options), context);
5545
+ }
5252
5546
  function getSelectorFunc(searchFunc) {
5253
5547
  return function select(query, elements, options) {
5254
5548
  const opts = convertOptionFormats(options);
5255
5549
  if (typeof query !== "function") {
5256
- query = compileUnsafe(query, opts, elements);
5550
+ query = _compileUnsafe(query, opts, elements);
5257
5551
  }
5258
5552
  const filteredElements = prepareContext(elements, opts.adapter, query.shouldTestNextSiblings);
5259
5553
  return searchFunc(query, filteredElements, opts);
@@ -5284,16 +5578,16 @@ function appendNextSiblings(elem, adapter) {
5284
5578
  /**
5285
5579
  * @template Node The generic Node type for the DOM adapter being used.
5286
5580
  * @template ElementNode The Node type for elements for the DOM adapter being used.
5287
- * @param elems Elements to query. If it is an element, its children will be queried..
5581
+ * @param elems Elements to query. If it is an element, its children will be queried.
5288
5582
  * @param query can be either a CSS selector string or a compiled query function.
5289
5583
  * @param [options] options for querying the document.
5290
5584
  * @see compile for supported selector queries.
5291
5585
  * @returns All matching elements.
5292
5586
  *
5293
5587
  */
5294
- const selectAll = getSelectorFunc((query, elems, options) => query === boolbase.falseFunc || !elems || elems.length === 0
5588
+ const selectAll = getSelectorFunc((query, elems, options) => query === boolbaseExports.falseFunc || !elems || elems.length === 0
5295
5589
  ? []
5296
- : options.adapter.findAll(query, elems));
5590
+ : findAll(query, elems, options));
5297
5591
 
5298
5592
  const comma = ','.charCodeAt(0);
5299
5593
  const semicolon = ';'.charCodeAt(0);