selectors.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { AttributeAction, SelectorType, isTraversal as isTraversalBase, } from "css-what";
  2. export function isTraversal(token) {
  3. return token.type === "_flexibleDescendant" || isTraversalBase(token);
  4. }
  5. /**
  6. * Sort the parts of the passed selector, as there is potential for
  7. * optimization (some types of selectors are faster than others).
  8. *
  9. * @param arr Selector to sort
  10. */
  11. export function sortRules(arr) {
  12. const ratings = arr.map(getQuality);
  13. for (let i = 1; i < arr.length; i++) {
  14. const procNew = ratings[i];
  15. if (procNew < 0)
  16. continue;
  17. // Use insertion sort to move the token to the correct position.
  18. for (let j = i; j > 0 && procNew < ratings[j - 1]; j--) {
  19. const token = arr[j];
  20. arr[j] = arr[j - 1];
  21. arr[j - 1] = token;
  22. ratings[j] = ratings[j - 1];
  23. ratings[j - 1] = procNew;
  24. }
  25. }
  26. }
  27. function getAttributeQuality(token) {
  28. switch (token.action) {
  29. case AttributeAction.Exists: {
  30. return 10;
  31. }
  32. case AttributeAction.Equals: {
  33. // Prefer ID selectors (eg. #ID)
  34. return token.name === "id" ? 9 : 8;
  35. }
  36. case AttributeAction.Not: {
  37. return 7;
  38. }
  39. case AttributeAction.Start: {
  40. return 6;
  41. }
  42. case AttributeAction.End: {
  43. return 6;
  44. }
  45. case AttributeAction.Any: {
  46. return 5;
  47. }
  48. case AttributeAction.Hyphen: {
  49. return 4;
  50. }
  51. case AttributeAction.Element: {
  52. return 3;
  53. }
  54. }
  55. }
  56. /**
  57. * Determine the quality of the passed token. The higher the number, the
  58. * faster the token is to execute.
  59. *
  60. * @param token Token to get the quality of.
  61. * @returns The token's quality.
  62. */
  63. export function getQuality(token) {
  64. // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
  65. switch (token.type) {
  66. case SelectorType.Universal: {
  67. return 50;
  68. }
  69. case SelectorType.Tag: {
  70. return 30;
  71. }
  72. case SelectorType.Attribute: {
  73. return Math.floor(getAttributeQuality(token) /
  74. // `ignoreCase` adds some overhead, half the result if applicable.
  75. (token.ignoreCase ? 2 : 1));
  76. }
  77. case SelectorType.Pseudo: {
  78. return !token.data
  79. ? 3
  80. : token.name === "has" ||
  81. token.name === "contains" ||
  82. token.name === "icontains"
  83. ? // Expensive in any case — run as late as possible.
  84. 0
  85. : Array.isArray(token.data)
  86. ? // Eg. `:is`, `:not`
  87. Math.max(
  88. // If we have traversals, try to avoid executing this selector
  89. 0, Math.min(...token.data.map((d) => Math.min(...d.map(getQuality)))))
  90. : 2;
  91. }
  92. default: {
  93. return -1;
  94. }
  95. }
  96. }
  97. export function includesScopePseudo(t) {
  98. return (t.type === SelectorType.Pseudo &&
  99. (t.name === "scope" ||
  100. (Array.isArray(t.data) &&
  101. t.data.some((data) => data.some(includesScopePseudo)))));
  102. }
  103. //# sourceMappingURL=selectors.js.map