general.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { attributeRules } from "./attributes.js";
  2. import { compilePseudoSelector } from "./pseudo-selectors/index.js";
  3. import { SelectorType } from "css-what";
  4. function getElementParent(node, adapter) {
  5. const parent = adapter.getParent(node);
  6. if (parent && adapter.isTag(parent)) {
  7. return parent;
  8. }
  9. return null;
  10. }
  11. /*
  12. * All available rules
  13. */
  14. export function compileGeneralSelector(next, selector, options, context, compileToken) {
  15. const { adapter, equals } = options;
  16. switch (selector.type) {
  17. case SelectorType.PseudoElement: {
  18. throw new Error("Pseudo-elements are not supported by css-select");
  19. }
  20. case SelectorType.ColumnCombinator: {
  21. throw new Error("Column combinators are not yet supported by css-select");
  22. }
  23. case SelectorType.Attribute: {
  24. if (selector.namespace != null) {
  25. throw new Error("Namespaced attributes are not yet supported by css-select");
  26. }
  27. if (!options.xmlMode || options.lowerCaseAttributeNames) {
  28. selector.name = selector.name.toLowerCase();
  29. }
  30. return attributeRules[selector.action](next, selector, options);
  31. }
  32. case SelectorType.Pseudo: {
  33. return compilePseudoSelector(next, selector, options, context, compileToken);
  34. }
  35. // Tags
  36. case SelectorType.Tag: {
  37. if (selector.namespace != null) {
  38. throw new Error("Namespaced tag names are not yet supported by css-select");
  39. }
  40. let { name } = selector;
  41. if (!options.xmlMode || options.lowerCaseTags) {
  42. name = name.toLowerCase();
  43. }
  44. return function tag(elem) {
  45. return adapter.getName(elem) === name && next(elem);
  46. };
  47. }
  48. // Traversal
  49. case SelectorType.Descendant: {
  50. if (options.cacheResults === false ||
  51. typeof WeakSet === "undefined") {
  52. return function descendant(elem) {
  53. let current = elem;
  54. while ((current = getElementParent(current, adapter))) {
  55. if (next(current)) {
  56. return true;
  57. }
  58. }
  59. return false;
  60. };
  61. }
  62. // @ts-expect-error `ElementNode` is not extending object
  63. const isFalseCache = new WeakSet();
  64. return function cachedDescendant(elem) {
  65. let current = elem;
  66. while ((current = getElementParent(current, adapter))) {
  67. if (!isFalseCache.has(current)) {
  68. if (adapter.isTag(current) && next(current)) {
  69. return true;
  70. }
  71. isFalseCache.add(current);
  72. }
  73. }
  74. return false;
  75. };
  76. }
  77. case "_flexibleDescendant": {
  78. // Include element itself, only used while querying an array
  79. return function flexibleDescendant(elem) {
  80. let current = elem;
  81. do {
  82. if (next(current))
  83. return true;
  84. } while ((current = getElementParent(current, adapter)));
  85. return false;
  86. };
  87. }
  88. case SelectorType.Parent: {
  89. return function parent(elem) {
  90. return adapter
  91. .getChildren(elem)
  92. .some((elem) => adapter.isTag(elem) && next(elem));
  93. };
  94. }
  95. case SelectorType.Child: {
  96. return function child(elem) {
  97. const parent = adapter.getParent(elem);
  98. return parent != null && adapter.isTag(parent) && next(parent);
  99. };
  100. }
  101. case SelectorType.Sibling: {
  102. return function sibling(elem) {
  103. const siblings = adapter.getSiblings(elem);
  104. for (let i = 0; i < siblings.length; i++) {
  105. const currentSibling = siblings[i];
  106. if (equals(elem, currentSibling))
  107. break;
  108. if (adapter.isTag(currentSibling) && next(currentSibling)) {
  109. return true;
  110. }
  111. }
  112. return false;
  113. };
  114. }
  115. case SelectorType.Adjacent: {
  116. if (adapter.prevElementSibling) {
  117. return function adjacent(elem) {
  118. const previous = adapter.prevElementSibling(elem);
  119. return previous != null && next(previous);
  120. };
  121. }
  122. return function adjacent(elem) {
  123. const siblings = adapter.getSiblings(elem);
  124. let lastElement;
  125. for (let i = 0; i < siblings.length; i++) {
  126. const currentSibling = siblings[i];
  127. if (equals(elem, currentSibling))
  128. break;
  129. if (adapter.isTag(currentSibling)) {
  130. lastElement = currentSibling;
  131. }
  132. }
  133. return !!lastElement && next(lastElement);
  134. };
  135. }
  136. case SelectorType.Universal: {
  137. if (selector.namespace != null && selector.namespace !== "*") {
  138. throw new Error("Namespaced universal selectors are not yet supported by css-select");
  139. }
  140. return next;
  141. }
  142. }
  143. }
  144. //# sourceMappingURL=general.js.map