@jesscss/core 2.0.0-alpha.5 → 2.0.0-alpha.6

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 (637) hide show
  1. package/lib/index.cjs +20159 -0
  2. package/lib/index.d.cts +5993 -0
  3. package/lib/index.d.cts.map +1 -0
  4. package/lib/index.d.ts +5992 -21
  5. package/lib/index.d.ts.map +1 -1
  6. package/lib/index.js +19926 -22
  7. package/lib/index.js.map +1 -1
  8. package/package.json +15 -14
  9. package/src/__tests__/define-function-record.test.ts +58 -0
  10. package/src/__tests__/define-function-simple.test.ts +55 -0
  11. package/src/__tests__/define-function-split-sequence.test.ts +547 -0
  12. package/src/__tests__/define-function-type-parity.test.ts +9 -0
  13. package/src/__tests__/define-function.test.ts +763 -0
  14. package/src/__tests__/num-operations.test.ts +91 -0
  15. package/src/__tests__/safe-parse.test.ts +374 -0
  16. package/src/context.ts +896 -0
  17. package/src/conversions.ts +282 -0
  18. package/src/debug-log.ts +29 -0
  19. package/src/define-function.ts +1006 -0
  20. package/src/deprecation.ts +67 -0
  21. package/src/globals.d.ts +26 -0
  22. package/src/index.ts +31 -0
  23. package/src/jess-error.ts +773 -0
  24. package/src/logger/deprecation-processing.ts +109 -0
  25. package/src/logger.ts +31 -0
  26. package/src/plugin.ts +292 -0
  27. package/src/tree/LOOKUP_CHAINS.md +35 -0
  28. package/src/tree/README.md +18 -0
  29. package/src/tree/__tests__/__snapshots__/extend-eval-integration.test.ts.snap +1455 -0
  30. package/src/tree/__tests__/ampersand.test.ts +382 -0
  31. package/src/tree/__tests__/at-rule.test.ts +2047 -0
  32. package/src/tree/__tests__/basic-render.test.ts +212 -0
  33. package/src/tree/__tests__/block.test.ts +40 -0
  34. package/src/tree/__tests__/call.test.ts +346 -0
  35. package/src/tree/__tests__/color.test.ts +537 -0
  36. package/src/tree/__tests__/condition.test.ts +186 -0
  37. package/src/tree/__tests__/control.test.ts +564 -0
  38. package/src/tree/__tests__/declaration.test.ts +253 -0
  39. package/src/tree/__tests__/dependency-graph.test.ts +177 -0
  40. package/src/tree/__tests__/detached-rulesets.test.ts +213 -0
  41. package/src/tree/__tests__/dimension.test.ts +236 -0
  42. package/src/tree/__tests__/expression.test.ts +73 -0
  43. package/src/tree/__tests__/ext-node.test.ts +31 -0
  44. package/src/tree/__tests__/extend-eval-integration.test.ts +1033 -0
  45. package/src/tree/__tests__/extend-import-style.test.ts +929 -0
  46. package/src/tree/__tests__/extend-less-fixtures.test.ts +851 -0
  47. package/src/tree/__tests__/extend-list.test.ts +31 -0
  48. package/src/tree/__tests__/extend-roots.test.ts +1045 -0
  49. package/src/tree/__tests__/extend-rules.test.ts +740 -0
  50. package/src/tree/__tests__/func.test.ts +171 -0
  51. package/src/tree/__tests__/import-js.test.ts +33 -0
  52. package/src/tree/__tests__/import-style-test-helpers.ts +56 -0
  53. package/src/tree/__tests__/import-style.test.ts +1967 -0
  54. package/src/tree/__tests__/interpolated-reference.test.ts +44 -0
  55. package/src/tree/__tests__/interpolated.test.ts +41 -0
  56. package/src/tree/__tests__/list.test.ts +177 -0
  57. package/src/tree/__tests__/log.test.ts +83 -0
  58. package/src/tree/__tests__/mixin-recursion.test.ts +639 -0
  59. package/src/tree/__tests__/mixin.test.ts +2171 -0
  60. package/src/tree/__tests__/negative.test.ts +45 -0
  61. package/src/tree/__tests__/nesting-collapse.test.ts +519 -0
  62. package/src/tree/__tests__/node-flags-perf.test.ts +195 -0
  63. package/src/tree/__tests__/node-flags.test.ts +410 -0
  64. package/src/tree/__tests__/node-graph.test.ts +598 -0
  65. package/src/tree/__tests__/node-mutation.test.ts +182 -0
  66. package/src/tree/__tests__/operation.test.ts +18 -0
  67. package/src/tree/__tests__/paren.test.ts +90 -0
  68. package/src/tree/__tests__/preserve-mode-output.test.ts +50 -0
  69. package/src/tree/__tests__/quoted.test.ts +72 -0
  70. package/src/tree/__tests__/range.test.ts +59 -0
  71. package/src/tree/__tests__/reference.test.ts +743 -0
  72. package/src/tree/__tests__/rest.test.ts +29 -0
  73. package/src/tree/__tests__/rules-raw.test.ts +14 -0
  74. package/src/tree/__tests__/rules.test.ts +1271 -0
  75. package/src/tree/__tests__/ruleset.test.ts +597 -0
  76. package/src/tree/__tests__/selector-attr.test.ts +50 -0
  77. package/src/tree/__tests__/selector-basic.test.ts +44 -0
  78. package/src/tree/__tests__/selector-capture.test.ts +22 -0
  79. package/src/tree/__tests__/selector-complex.test.ts +120 -0
  80. package/src/tree/__tests__/selector-compound.test.ts +74 -0
  81. package/src/tree/__tests__/selector-interpolated.test.ts +50 -0
  82. package/src/tree/__tests__/selector-list.test.ts +59 -0
  83. package/src/tree/__tests__/selector-pseudo.test.ts +23 -0
  84. package/src/tree/__tests__/selector.test.ts +182 -0
  85. package/src/tree/__tests__/sequence.test.ts +226 -0
  86. package/src/tree/__tests__/serialize-types.test.ts +529 -0
  87. package/src/tree/__tests__/spaced.test.ts +8 -0
  88. package/src/tree/__tests__/url.test.ts +72 -0
  89. package/src/tree/__tests__/var-declaration.test.ts +90 -0
  90. package/src/tree/ampersand.ts +538 -0
  91. package/src/tree/any.ts +169 -0
  92. package/src/tree/at-rule.ts +760 -0
  93. package/src/tree/block.ts +72 -0
  94. package/src/tree/bool.ts +46 -0
  95. package/src/tree/call.ts +593 -0
  96. package/src/tree/collection.ts +52 -0
  97. package/src/tree/color.ts +629 -0
  98. package/src/tree/combinator.ts +30 -0
  99. package/src/tree/comment.ts +36 -0
  100. package/src/tree/condition.ts +194 -0
  101. package/src/tree/control.ts +452 -0
  102. package/src/tree/declaration-custom.ts +56 -0
  103. package/src/tree/declaration-var.ts +87 -0
  104. package/src/tree/declaration.ts +742 -0
  105. package/src/tree/default-guard.ts +35 -0
  106. package/src/tree/dimension.ts +392 -0
  107. package/src/tree/expression.ts +97 -0
  108. package/src/tree/extend-list.ts +51 -0
  109. package/src/tree/extend.ts +391 -0
  110. package/src/tree/function.ts +254 -0
  111. package/src/tree/import-js.ts +130 -0
  112. package/src/tree/import-style.ts +875 -0
  113. package/{lib/tree/index.js → src/tree/index.ts} +49 -22
  114. package/src/tree/interpolated.ts +346 -0
  115. package/src/tree/js-array.ts +21 -0
  116. package/src/tree/js-expr.ts +50 -0
  117. package/src/tree/js-function.ts +31 -0
  118. package/src/tree/js-object.ts +22 -0
  119. package/src/tree/list.ts +415 -0
  120. package/src/tree/log.ts +89 -0
  121. package/src/tree/mixin.ts +331 -0
  122. package/src/tree/negative.ts +58 -0
  123. package/src/tree/nil.ts +57 -0
  124. package/src/tree/node-base.ts +1716 -0
  125. package/src/tree/node-type.ts +122 -0
  126. package/src/tree/node.ts +118 -0
  127. package/src/tree/number.ts +54 -0
  128. package/src/tree/operation.ts +187 -0
  129. package/src/tree/paren.ts +132 -0
  130. package/src/tree/query-condition.ts +47 -0
  131. package/src/tree/quoted.ts +119 -0
  132. package/src/tree/range.ts +101 -0
  133. package/src/tree/reference.ts +1099 -0
  134. package/src/tree/rest.ts +55 -0
  135. package/src/tree/rules-raw.ts +52 -0
  136. package/src/tree/rules.ts +2896 -0
  137. package/src/tree/ruleset.ts +1217 -0
  138. package/src/tree/selector-attr.ts +172 -0
  139. package/src/tree/selector-basic.ts +75 -0
  140. package/src/tree/selector-capture.ts +85 -0
  141. package/src/tree/selector-complex.ts +189 -0
  142. package/src/tree/selector-compound.ts +205 -0
  143. package/src/tree/selector-interpolated.ts +95 -0
  144. package/src/tree/selector-list.ts +245 -0
  145. package/src/tree/selector-pseudo.ts +173 -0
  146. package/src/tree/selector-simple.ts +10 -0
  147. package/src/tree/selector.ts +152 -0
  148. package/src/tree/sequence.ts +463 -0
  149. package/src/tree/tree.ts +130 -0
  150. package/src/tree/url.ts +95 -0
  151. package/src/tree/util/EXTEND_ARCHITECTURE_ANALYSIS.md +215 -0
  152. package/src/tree/util/EXTEND_AUDIT.md +233 -0
  153. package/src/tree/util/EXTEND_BASELINE.md +64 -0
  154. package/src/tree/util/EXTEND_CALL_GRAPH_ANALYSIS.md +244 -0
  155. package/src/tree/util/EXTEND_DOCS.md +24 -0
  156. package/src/tree/util/EXTEND_FINAL_SUMMARY.md +95 -0
  157. package/src/tree/util/EXTEND_FUNCTION_AUDIT.md +1433 -0
  158. package/src/tree/util/EXTEND_OPTIMIZATION_PLAN.md +114 -0
  159. package/src/tree/util/EXTEND_REFACTORING_SUMMARY.md +152 -0
  160. package/src/tree/util/EXTEND_RULES.md +74 -0
  161. package/src/tree/util/EXTEND_UNUSED_FUNCTIONS.md +127 -0
  162. package/src/tree/util/EXTEND_UNUSED_FUNCTIONS_ANALYSIS.md +227 -0
  163. package/src/tree/util/NODE_COPY_REDUCTION_PLAN.md +12 -0
  164. package/src/tree/util/__tests__/EXTEND_TEST_INDEX.md +59 -0
  165. package/src/tree/util/__tests__/OPTIMIZATION-ANALYSIS.md +130 -0
  166. package/src/tree/util/__tests__/WALK_AND_CONSUME_DESIGN.md +138 -0
  167. package/src/tree/util/__tests__/_archive/2026-02-09__OPTIMIZATION-ANALYSIS.md +9 -0
  168. package/src/tree/util/__tests__/_archive/README.md +4 -0
  169. package/src/tree/util/__tests__/bitset.test.ts +142 -0
  170. package/src/tree/util/__tests__/debug-log.ts +50 -0
  171. package/src/tree/util/__tests__/extend-comment-handling.test.ts +187 -0
  172. package/src/tree/util/__tests__/extend-core-unit.test.ts +941 -0
  173. package/src/tree/util/__tests__/extend-pipeline-bench.test.ts +154 -0
  174. package/src/tree/util/__tests__/extend-pipeline-bench.ts +190 -0
  175. package/src/tree/util/__tests__/fast-reject.test.ts +377 -0
  176. package/src/tree/util/__tests__/is-node.test.ts +63 -0
  177. package/src/tree/util/__tests__/list-like.test.ts +63 -0
  178. package/src/tree/util/__tests__/outputwriter.test.ts +523 -0
  179. package/src/tree/util/__tests__/print.test.ts +183 -0
  180. package/src/tree/util/__tests__/process-extends.test.ts +226 -0
  181. package/src/tree/util/__tests__/process-leading-is.test.ts +205 -0
  182. package/src/tree/util/__tests__/recursion-helper.test.ts +184 -0
  183. package/src/tree/util/__tests__/selector-match-unit.test.ts +1427 -0
  184. package/src/tree/util/__tests__/sourcemap.test.ts +117 -0
  185. package/src/tree/util/ampersand-template.ts +9 -0
  186. package/src/tree/util/bitset.ts +194 -0
  187. package/src/tree/util/calculate.ts +11 -0
  188. package/src/tree/util/cast.ts +89 -0
  189. package/src/tree/util/cloning.ts +8 -0
  190. package/src/tree/util/collections.ts +299 -0
  191. package/src/tree/util/compare.ts +90 -0
  192. package/src/tree/util/cursor.ts +171 -0
  193. package/src/tree/util/extend-core.ts +2139 -0
  194. package/src/tree/util/extend-roots.ts +1108 -0
  195. package/src/tree/util/field-helpers.ts +354 -0
  196. package/src/tree/util/is-node.ts +43 -0
  197. package/src/tree/util/list-like.ts +93 -0
  198. package/src/tree/util/mixin-instance-primitives.ts +2020 -0
  199. package/src/tree/util/print.ts +303 -0
  200. package/src/tree/util/process-leading-is.ts +421 -0
  201. package/src/tree/util/recursion-helper.ts +54 -0
  202. package/src/tree/util/regex.ts +2 -0
  203. package/src/tree/util/registry-utils.ts +1953 -0
  204. package/src/tree/util/ruleset-trace.ts +17 -0
  205. package/src/tree/util/scoped-body-eval.ts +320 -0
  206. package/src/tree/util/selector-match-core.ts +2005 -0
  207. package/src/tree/util/selector-utils.ts +757 -0
  208. package/src/tree/util/serialize-helper.ts +535 -0
  209. package/src/tree/util/serialize-types.ts +318 -0
  210. package/src/tree/util/should-operate.ts +78 -0
  211. package/src/tree/util/sourcemap.ts +37 -0
  212. package/src/types/config.ts +247 -0
  213. package/src/types/index.ts +12 -0
  214. package/{lib/types/modes.d.ts → src/types/modes.ts} +2 -1
  215. package/src/types.d.ts +9 -0
  216. package/src/types.ts +68 -0
  217. package/src/use-webpack-resolver.ts +56 -0
  218. package/src/visitor/__tests__/visitor.test.ts +136 -0
  219. package/src/visitor/index.ts +263 -0
  220. package/{lib/visitor/less-visitor.js → src/visitor/less-visitor.ts} +3 -2
  221. package/lib/context.d.ts +0 -352
  222. package/lib/context.d.ts.map +0 -1
  223. package/lib/context.js +0 -636
  224. package/lib/context.js.map +0 -1
  225. package/lib/conversions.d.ts +0 -73
  226. package/lib/conversions.d.ts.map +0 -1
  227. package/lib/conversions.js +0 -253
  228. package/lib/conversions.js.map +0 -1
  229. package/lib/debug-log.d.ts +0 -2
  230. package/lib/debug-log.d.ts.map +0 -1
  231. package/lib/debug-log.js +0 -27
  232. package/lib/debug-log.js.map +0 -1
  233. package/lib/define-function.d.ts +0 -587
  234. package/lib/define-function.d.ts.map +0 -1
  235. package/lib/define-function.js +0 -726
  236. package/lib/define-function.js.map +0 -1
  237. package/lib/deprecation.d.ts +0 -34
  238. package/lib/deprecation.d.ts.map +0 -1
  239. package/lib/deprecation.js +0 -57
  240. package/lib/deprecation.js.map +0 -1
  241. package/lib/jess-error.d.ts +0 -343
  242. package/lib/jess-error.d.ts.map +0 -1
  243. package/lib/jess-error.js +0 -508
  244. package/lib/jess-error.js.map +0 -1
  245. package/lib/logger/deprecation-processing.d.ts +0 -41
  246. package/lib/logger/deprecation-processing.d.ts.map +0 -1
  247. package/lib/logger/deprecation-processing.js +0 -81
  248. package/lib/logger/deprecation-processing.js.map +0 -1
  249. package/lib/logger.d.ts +0 -10
  250. package/lib/logger.d.ts.map +0 -1
  251. package/lib/logger.js +0 -20
  252. package/lib/logger.js.map +0 -1
  253. package/lib/plugin.d.ts +0 -94
  254. package/lib/plugin.d.ts.map +0 -1
  255. package/lib/plugin.js +0 -174
  256. package/lib/plugin.js.map +0 -1
  257. package/lib/tree/ampersand.d.ts +0 -98
  258. package/lib/tree/ampersand.d.ts.map +0 -1
  259. package/lib/tree/ampersand.js +0 -319
  260. package/lib/tree/ampersand.js.map +0 -1
  261. package/lib/tree/any.d.ts +0 -58
  262. package/lib/tree/any.d.ts.map +0 -1
  263. package/lib/tree/any.js +0 -104
  264. package/lib/tree/any.js.map +0 -1
  265. package/lib/tree/at-rule.d.ts +0 -53
  266. package/lib/tree/at-rule.d.ts.map +0 -1
  267. package/lib/tree/at-rule.js +0 -503
  268. package/lib/tree/at-rule.js.map +0 -1
  269. package/lib/tree/block.d.ts +0 -22
  270. package/lib/tree/block.d.ts.map +0 -1
  271. package/lib/tree/block.js +0 -24
  272. package/lib/tree/block.js.map +0 -1
  273. package/lib/tree/bool.d.ts +0 -18
  274. package/lib/tree/bool.d.ts.map +0 -1
  275. package/lib/tree/bool.js +0 -28
  276. package/lib/tree/bool.js.map +0 -1
  277. package/lib/tree/call.d.ts +0 -66
  278. package/lib/tree/call.d.ts.map +0 -1
  279. package/lib/tree/call.js +0 -306
  280. package/lib/tree/call.js.map +0 -1
  281. package/lib/tree/collection.d.ts +0 -30
  282. package/lib/tree/collection.d.ts.map +0 -1
  283. package/lib/tree/collection.js +0 -37
  284. package/lib/tree/collection.js.map +0 -1
  285. package/lib/tree/color.d.ts +0 -101
  286. package/lib/tree/color.d.ts.map +0 -1
  287. package/lib/tree/color.js +0 -513
  288. package/lib/tree/color.js.map +0 -1
  289. package/lib/tree/combinator.d.ts +0 -13
  290. package/lib/tree/combinator.d.ts.map +0 -1
  291. package/lib/tree/combinator.js +0 -12
  292. package/lib/tree/combinator.js.map +0 -1
  293. package/lib/tree/comment.d.ts +0 -20
  294. package/lib/tree/comment.d.ts.map +0 -1
  295. package/lib/tree/comment.js +0 -19
  296. package/lib/tree/comment.js.map +0 -1
  297. package/lib/tree/condition.d.ts +0 -31
  298. package/lib/tree/condition.d.ts.map +0 -1
  299. package/lib/tree/condition.js +0 -103
  300. package/lib/tree/condition.js.map +0 -1
  301. package/lib/tree/control.d.ts +0 -104
  302. package/lib/tree/control.d.ts.map +0 -1
  303. package/lib/tree/control.js +0 -430
  304. package/lib/tree/control.js.map +0 -1
  305. package/lib/tree/declaration-custom.d.ts +0 -18
  306. package/lib/tree/declaration-custom.d.ts.map +0 -1
  307. package/lib/tree/declaration-custom.js +0 -24
  308. package/lib/tree/declaration-custom.js.map +0 -1
  309. package/lib/tree/declaration-var.d.ts +0 -35
  310. package/lib/tree/declaration-var.d.ts.map +0 -1
  311. package/lib/tree/declaration-var.js +0 -63
  312. package/lib/tree/declaration-var.js.map +0 -1
  313. package/lib/tree/declaration.d.ts +0 -78
  314. package/lib/tree/declaration.d.ts.map +0 -1
  315. package/lib/tree/declaration.js +0 -286
  316. package/lib/tree/declaration.js.map +0 -1
  317. package/lib/tree/default-guard.d.ts +0 -15
  318. package/lib/tree/default-guard.d.ts.map +0 -1
  319. package/lib/tree/default-guard.js +0 -19
  320. package/lib/tree/default-guard.js.map +0 -1
  321. package/lib/tree/dimension.d.ts +0 -34
  322. package/lib/tree/dimension.d.ts.map +0 -1
  323. package/lib/tree/dimension.js +0 -294
  324. package/lib/tree/dimension.js.map +0 -1
  325. package/lib/tree/expression.d.ts +0 -25
  326. package/lib/tree/expression.d.ts.map +0 -1
  327. package/lib/tree/expression.js +0 -32
  328. package/lib/tree/expression.js.map +0 -1
  329. package/lib/tree/extend-list.d.ts +0 -23
  330. package/lib/tree/extend-list.d.ts.map +0 -1
  331. package/lib/tree/extend-list.js +0 -23
  332. package/lib/tree/extend-list.js.map +0 -1
  333. package/lib/tree/extend.d.ts +0 -47
  334. package/lib/tree/extend.d.ts.map +0 -1
  335. package/lib/tree/extend.js +0 -296
  336. package/lib/tree/extend.js.map +0 -1
  337. package/lib/tree/function.d.ts +0 -48
  338. package/lib/tree/function.d.ts.map +0 -1
  339. package/lib/tree/function.js +0 -74
  340. package/lib/tree/function.js.map +0 -1
  341. package/lib/tree/import-js.d.ts +0 -35
  342. package/lib/tree/import-js.d.ts.map +0 -1
  343. package/lib/tree/import-js.js +0 -45
  344. package/lib/tree/import-js.js.map +0 -1
  345. package/lib/tree/import-style.d.ts +0 -156
  346. package/lib/tree/import-style.d.ts.map +0 -1
  347. package/lib/tree/import-style.js +0 -566
  348. package/lib/tree/import-style.js.map +0 -1
  349. package/lib/tree/index.d.ts +0 -71
  350. package/lib/tree/index.d.ts.map +0 -1
  351. package/lib/tree/index.js.map +0 -1
  352. package/lib/tree/interpolated-reference.d.ts +0 -24
  353. package/lib/tree/interpolated-reference.d.ts.map +0 -1
  354. package/lib/tree/interpolated-reference.js +0 -37
  355. package/lib/tree/interpolated-reference.js.map +0 -1
  356. package/lib/tree/interpolated.d.ts +0 -62
  357. package/lib/tree/interpolated.d.ts.map +0 -1
  358. package/lib/tree/interpolated.js +0 -204
  359. package/lib/tree/interpolated.js.map +0 -1
  360. package/lib/tree/js-array.d.ts +0 -10
  361. package/lib/tree/js-array.d.ts.map +0 -1
  362. package/lib/tree/js-array.js +0 -10
  363. package/lib/tree/js-array.js.map +0 -1
  364. package/lib/tree/js-expr.d.ts +0 -23
  365. package/lib/tree/js-expr.d.ts.map +0 -1
  366. package/lib/tree/js-expr.js +0 -28
  367. package/lib/tree/js-expr.js.map +0 -1
  368. package/lib/tree/js-function.d.ts +0 -20
  369. package/lib/tree/js-function.d.ts.map +0 -1
  370. package/lib/tree/js-function.js +0 -16
  371. package/lib/tree/js-function.js.map +0 -1
  372. package/lib/tree/js-object.d.ts +0 -10
  373. package/lib/tree/js-object.d.ts.map +0 -1
  374. package/lib/tree/js-object.js +0 -10
  375. package/lib/tree/js-object.js.map +0 -1
  376. package/lib/tree/list.d.ts +0 -38
  377. package/lib/tree/list.d.ts.map +0 -1
  378. package/lib/tree/list.js +0 -83
  379. package/lib/tree/list.js.map +0 -1
  380. package/lib/tree/log.d.ts +0 -29
  381. package/lib/tree/log.d.ts.map +0 -1
  382. package/lib/tree/log.js +0 -56
  383. package/lib/tree/log.js.map +0 -1
  384. package/lib/tree/mixin.d.ts +0 -87
  385. package/lib/tree/mixin.d.ts.map +0 -1
  386. package/lib/tree/mixin.js +0 -112
  387. package/lib/tree/mixin.js.map +0 -1
  388. package/lib/tree/negative.d.ts +0 -17
  389. package/lib/tree/negative.d.ts.map +0 -1
  390. package/lib/tree/negative.js +0 -22
  391. package/lib/tree/negative.js.map +0 -1
  392. package/lib/tree/nil.d.ts +0 -30
  393. package/lib/tree/nil.d.ts.map +0 -1
  394. package/lib/tree/nil.js +0 -35
  395. package/lib/tree/nil.js.map +0 -1
  396. package/lib/tree/node-base.d.ts +0 -361
  397. package/lib/tree/node-base.d.ts.map +0 -1
  398. package/lib/tree/node-base.js +0 -930
  399. package/lib/tree/node-base.js.map +0 -1
  400. package/lib/tree/node.d.ts +0 -10
  401. package/lib/tree/node.d.ts.map +0 -1
  402. package/lib/tree/node.js +0 -45
  403. package/lib/tree/node.js.map +0 -1
  404. package/lib/tree/number.d.ts +0 -21
  405. package/lib/tree/number.d.ts.map +0 -1
  406. package/lib/tree/number.js +0 -27
  407. package/lib/tree/number.js.map +0 -1
  408. package/lib/tree/operation.d.ts +0 -26
  409. package/lib/tree/operation.d.ts.map +0 -1
  410. package/lib/tree/operation.js +0 -103
  411. package/lib/tree/operation.js.map +0 -1
  412. package/lib/tree/paren.d.ts +0 -19
  413. package/lib/tree/paren.d.ts.map +0 -1
  414. package/lib/tree/paren.js +0 -92
  415. package/lib/tree/paren.js.map +0 -1
  416. package/lib/tree/query-condition.d.ts +0 -17
  417. package/lib/tree/query-condition.d.ts.map +0 -1
  418. package/lib/tree/query-condition.js +0 -39
  419. package/lib/tree/query-condition.js.map +0 -1
  420. package/lib/tree/quoted.d.ts +0 -28
  421. package/lib/tree/quoted.d.ts.map +0 -1
  422. package/lib/tree/quoted.js +0 -75
  423. package/lib/tree/quoted.js.map +0 -1
  424. package/lib/tree/range.d.ts +0 -33
  425. package/lib/tree/range.d.ts.map +0 -1
  426. package/lib/tree/range.js +0 -47
  427. package/lib/tree/range.js.map +0 -1
  428. package/lib/tree/reference.d.ts +0 -76
  429. package/lib/tree/reference.d.ts.map +0 -1
  430. package/lib/tree/reference.js +0 -521
  431. package/lib/tree/reference.js.map +0 -1
  432. package/lib/tree/rest.d.ts +0 -15
  433. package/lib/tree/rest.d.ts.map +0 -1
  434. package/lib/tree/rest.js +0 -32
  435. package/lib/tree/rest.js.map +0 -1
  436. package/lib/tree/rules-raw.d.ts +0 -17
  437. package/lib/tree/rules-raw.d.ts.map +0 -1
  438. package/lib/tree/rules-raw.js +0 -37
  439. package/lib/tree/rules-raw.js.map +0 -1
  440. package/lib/tree/rules.d.ts +0 -262
  441. package/lib/tree/rules.d.ts.map +0 -1
  442. package/lib/tree/rules.js +0 -2359
  443. package/lib/tree/rules.js.map +0 -1
  444. package/lib/tree/ruleset.d.ts +0 -92
  445. package/lib/tree/ruleset.d.ts.map +0 -1
  446. package/lib/tree/ruleset.js +0 -528
  447. package/lib/tree/ruleset.js.map +0 -1
  448. package/lib/tree/selector-attr.d.ts +0 -31
  449. package/lib/tree/selector-attr.d.ts.map +0 -1
  450. package/lib/tree/selector-attr.js +0 -99
  451. package/lib/tree/selector-attr.js.map +0 -1
  452. package/lib/tree/selector-basic.d.ts +0 -24
  453. package/lib/tree/selector-basic.d.ts.map +0 -1
  454. package/lib/tree/selector-basic.js +0 -38
  455. package/lib/tree/selector-basic.js.map +0 -1
  456. package/lib/tree/selector-capture.d.ts +0 -23
  457. package/lib/tree/selector-capture.d.ts.map +0 -1
  458. package/lib/tree/selector-capture.js +0 -34
  459. package/lib/tree/selector-capture.js.map +0 -1
  460. package/lib/tree/selector-complex.d.ts +0 -40
  461. package/lib/tree/selector-complex.d.ts.map +0 -1
  462. package/lib/tree/selector-complex.js +0 -143
  463. package/lib/tree/selector-complex.js.map +0 -1
  464. package/lib/tree/selector-compound.d.ts +0 -16
  465. package/lib/tree/selector-compound.d.ts.map +0 -1
  466. package/lib/tree/selector-compound.js +0 -114
  467. package/lib/tree/selector-compound.js.map +0 -1
  468. package/lib/tree/selector-interpolated.d.ts +0 -23
  469. package/lib/tree/selector-interpolated.d.ts.map +0 -1
  470. package/lib/tree/selector-interpolated.js +0 -27
  471. package/lib/tree/selector-interpolated.js.map +0 -1
  472. package/lib/tree/selector-list.d.ts +0 -17
  473. package/lib/tree/selector-list.d.ts.map +0 -1
  474. package/lib/tree/selector-list.js +0 -174
  475. package/lib/tree/selector-list.js.map +0 -1
  476. package/lib/tree/selector-pseudo.d.ts +0 -42
  477. package/lib/tree/selector-pseudo.d.ts.map +0 -1
  478. package/lib/tree/selector-pseudo.js +0 -204
  479. package/lib/tree/selector-pseudo.js.map +0 -1
  480. package/lib/tree/selector-simple.d.ts +0 -5
  481. package/lib/tree/selector-simple.d.ts.map +0 -1
  482. package/lib/tree/selector-simple.js +0 -6
  483. package/lib/tree/selector-simple.js.map +0 -1
  484. package/lib/tree/selector.d.ts +0 -43
  485. package/lib/tree/selector.d.ts.map +0 -1
  486. package/lib/tree/selector.js +0 -56
  487. package/lib/tree/selector.js.map +0 -1
  488. package/lib/tree/sequence.d.ts +0 -43
  489. package/lib/tree/sequence.d.ts.map +0 -1
  490. package/lib/tree/sequence.js +0 -151
  491. package/lib/tree/sequence.js.map +0 -1
  492. package/lib/tree/tree.d.ts +0 -87
  493. package/lib/tree/tree.d.ts.map +0 -1
  494. package/lib/tree/tree.js +0 -2
  495. package/lib/tree/tree.js.map +0 -1
  496. package/lib/tree/url.d.ts +0 -18
  497. package/lib/tree/url.d.ts.map +0 -1
  498. package/lib/tree/url.js +0 -35
  499. package/lib/tree/url.js.map +0 -1
  500. package/lib/tree/util/__tests__/debug-log.d.ts +0 -1
  501. package/lib/tree/util/__tests__/debug-log.d.ts.map +0 -1
  502. package/lib/tree/util/__tests__/debug-log.js +0 -36
  503. package/lib/tree/util/__tests__/debug-log.js.map +0 -1
  504. package/lib/tree/util/calculate.d.ts +0 -3
  505. package/lib/tree/util/calculate.d.ts.map +0 -1
  506. package/lib/tree/util/calculate.js +0 -10
  507. package/lib/tree/util/calculate.js.map +0 -1
  508. package/lib/tree/util/cast.d.ts +0 -10
  509. package/lib/tree/util/cast.d.ts.map +0 -1
  510. package/lib/tree/util/cast.js +0 -87
  511. package/lib/tree/util/cast.js.map +0 -1
  512. package/lib/tree/util/cloning.d.ts +0 -4
  513. package/lib/tree/util/cloning.d.ts.map +0 -1
  514. package/lib/tree/util/cloning.js +0 -8
  515. package/lib/tree/util/cloning.js.map +0 -1
  516. package/lib/tree/util/collections.d.ts +0 -57
  517. package/lib/tree/util/collections.d.ts.map +0 -1
  518. package/lib/tree/util/collections.js +0 -136
  519. package/lib/tree/util/collections.js.map +0 -1
  520. package/lib/tree/util/compare.d.ts +0 -11
  521. package/lib/tree/util/compare.d.ts.map +0 -1
  522. package/lib/tree/util/compare.js +0 -89
  523. package/lib/tree/util/compare.js.map +0 -1
  524. package/lib/tree/util/extend-helpers.d.ts +0 -2
  525. package/lib/tree/util/extend-helpers.d.ts.map +0 -1
  526. package/lib/tree/util/extend-helpers.js +0 -2
  527. package/lib/tree/util/extend-helpers.js.map +0 -1
  528. package/lib/tree/util/extend-roots.d.ts +0 -37
  529. package/lib/tree/util/extend-roots.d.ts.map +0 -1
  530. package/lib/tree/util/extend-roots.js +0 -700
  531. package/lib/tree/util/extend-roots.js.map +0 -1
  532. package/lib/tree/util/extend-roots.old.d.ts +0 -132
  533. package/lib/tree/util/extend-roots.old.d.ts.map +0 -1
  534. package/lib/tree/util/extend-roots.old.js +0 -2272
  535. package/lib/tree/util/extend-roots.old.js.map +0 -1
  536. package/lib/tree/util/extend-trace-debug.d.ts +0 -13
  537. package/lib/tree/util/extend-trace-debug.d.ts.map +0 -1
  538. package/lib/tree/util/extend-trace-debug.js +0 -34
  539. package/lib/tree/util/extend-trace-debug.js.map +0 -1
  540. package/lib/tree/util/extend-walk.d.ts +0 -53
  541. package/lib/tree/util/extend-walk.d.ts.map +0 -1
  542. package/lib/tree/util/extend-walk.js +0 -881
  543. package/lib/tree/util/extend-walk.js.map +0 -1
  544. package/lib/tree/util/extend.d.ts +0 -218
  545. package/lib/tree/util/extend.d.ts.map +0 -1
  546. package/lib/tree/util/extend.js +0 -3182
  547. package/lib/tree/util/extend.js.map +0 -1
  548. package/lib/tree/util/find-extendable-locations.d.ts +0 -2
  549. package/lib/tree/util/find-extendable-locations.d.ts.map +0 -1
  550. package/lib/tree/util/find-extendable-locations.js +0 -2
  551. package/lib/tree/util/find-extendable-locations.js.map +0 -1
  552. package/lib/tree/util/format.d.ts +0 -20
  553. package/lib/tree/util/format.d.ts.map +0 -1
  554. package/lib/tree/util/format.js +0 -67
  555. package/lib/tree/util/format.js.map +0 -1
  556. package/lib/tree/util/is-node.d.ts +0 -13
  557. package/lib/tree/util/is-node.d.ts.map +0 -1
  558. package/lib/tree/util/is-node.js +0 -43
  559. package/lib/tree/util/is-node.js.map +0 -1
  560. package/lib/tree/util/print.d.ts +0 -80
  561. package/lib/tree/util/print.d.ts.map +0 -1
  562. package/lib/tree/util/print.js +0 -205
  563. package/lib/tree/util/print.js.map +0 -1
  564. package/lib/tree/util/process-leading-is.d.ts +0 -25
  565. package/lib/tree/util/process-leading-is.d.ts.map +0 -1
  566. package/lib/tree/util/process-leading-is.js +0 -364
  567. package/lib/tree/util/process-leading-is.js.map +0 -1
  568. package/lib/tree/util/recursion-helper.d.ts +0 -15
  569. package/lib/tree/util/recursion-helper.d.ts.map +0 -1
  570. package/lib/tree/util/recursion-helper.js +0 -43
  571. package/lib/tree/util/recursion-helper.js.map +0 -1
  572. package/lib/tree/util/regex.d.ts +0 -4
  573. package/lib/tree/util/regex.d.ts.map +0 -1
  574. package/lib/tree/util/regex.js +0 -4
  575. package/lib/tree/util/regex.js.map +0 -1
  576. package/lib/tree/util/registry-utils.d.ts +0 -192
  577. package/lib/tree/util/registry-utils.d.ts.map +0 -1
  578. package/lib/tree/util/registry-utils.js +0 -1214
  579. package/lib/tree/util/registry-utils.js.map +0 -1
  580. package/lib/tree/util/ruleset-trace.d.ts +0 -4
  581. package/lib/tree/util/ruleset-trace.d.ts.map +0 -1
  582. package/lib/tree/util/ruleset-trace.js +0 -14
  583. package/lib/tree/util/ruleset-trace.js.map +0 -1
  584. package/lib/tree/util/selector-compare.d.ts +0 -2
  585. package/lib/tree/util/selector-compare.d.ts.map +0 -1
  586. package/lib/tree/util/selector-compare.js +0 -2
  587. package/lib/tree/util/selector-compare.js.map +0 -1
  588. package/lib/tree/util/selector-match-core.d.ts +0 -184
  589. package/lib/tree/util/selector-match-core.d.ts.map +0 -1
  590. package/lib/tree/util/selector-match-core.js +0 -1603
  591. package/lib/tree/util/selector-match-core.js.map +0 -1
  592. package/lib/tree/util/selector-utils.d.ts +0 -30
  593. package/lib/tree/util/selector-utils.d.ts.map +0 -1
  594. package/lib/tree/util/selector-utils.js +0 -100
  595. package/lib/tree/util/selector-utils.js.map +0 -1
  596. package/lib/tree/util/serialize-helper.d.ts +0 -13
  597. package/lib/tree/util/serialize-helper.d.ts.map +0 -1
  598. package/lib/tree/util/serialize-helper.js +0 -387
  599. package/lib/tree/util/serialize-helper.js.map +0 -1
  600. package/lib/tree/util/serialize-types.d.ts +0 -9
  601. package/lib/tree/util/serialize-types.d.ts.map +0 -1
  602. package/lib/tree/util/serialize-types.js +0 -216
  603. package/lib/tree/util/serialize-types.js.map +0 -1
  604. package/lib/tree/util/should-operate.d.ts +0 -23
  605. package/lib/tree/util/should-operate.d.ts.map +0 -1
  606. package/lib/tree/util/should-operate.js +0 -46
  607. package/lib/tree/util/should-operate.js.map +0 -1
  608. package/lib/tree/util/sourcemap.d.ts +0 -7
  609. package/lib/tree/util/sourcemap.d.ts.map +0 -1
  610. package/lib/tree/util/sourcemap.js +0 -25
  611. package/lib/tree/util/sourcemap.js.map +0 -1
  612. package/lib/types/config.d.ts +0 -205
  613. package/lib/types/config.d.ts.map +0 -1
  614. package/lib/types/config.js +0 -2
  615. package/lib/types/config.js.map +0 -1
  616. package/lib/types/index.d.ts +0 -15
  617. package/lib/types/index.d.ts.map +0 -1
  618. package/lib/types/index.js +0 -3
  619. package/lib/types/index.js.map +0 -1
  620. package/lib/types/modes.d.ts.map +0 -1
  621. package/lib/types/modes.js +0 -2
  622. package/lib/types/modes.js.map +0 -1
  623. package/lib/types.d.ts +0 -61
  624. package/lib/types.d.ts.map +0 -1
  625. package/lib/types.js +0 -2
  626. package/lib/types.js.map +0 -1
  627. package/lib/use-webpack-resolver.d.ts +0 -9
  628. package/lib/use-webpack-resolver.d.ts.map +0 -1
  629. package/lib/use-webpack-resolver.js +0 -41
  630. package/lib/use-webpack-resolver.js.map +0 -1
  631. package/lib/visitor/index.d.ts +0 -136
  632. package/lib/visitor/index.d.ts.map +0 -1
  633. package/lib/visitor/index.js +0 -135
  634. package/lib/visitor/index.js.map +0 -1
  635. package/lib/visitor/less-visitor.d.ts +0 -7
  636. package/lib/visitor/less-visitor.d.ts.map +0 -1
  637. package/lib/visitor/less-visitor.js.map +0 -1
@@ -0,0 +1,1427 @@
1
+ /**
2
+ * Unit tests for every exported function in selector-match-core.ts.
3
+ *
4
+ * These tests pin the CURRENT behaviour so we can safely replace
5
+ * internals with a walk-and-consume algorithm. If any test breaks
6
+ * after a refactor, the refactor changed observable semantics.
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+ import {
10
+ el, sel, sellist, compound, is, co, pseudo, amp, rules, ruleset
11
+ } from '../../../index.js';
12
+ import { Context } from '../../../context.js';
13
+ import {
14
+ selectorMatch
15
+ } from '../selector-match-core.js';
16
+
17
+ let context: Context;
18
+ beforeEach(() => {
19
+ context = new Context();
20
+ });
21
+
22
+ describe('basic selectors', () => {
23
+ it('matches identical simple selectors', () => {
24
+ let sel1 = el('.a');
25
+ let sel2 = el('.a');
26
+ sel1.eval(context);
27
+ sel2.eval(context);
28
+ let result = selectorMatch(sel1, sel2);
29
+ expect(result.fullMatch).toBe(true);
30
+ expect(result.crossesAmpersand).toBe(false);
31
+ expect(result.matches[0]!.consumedTarget).toBe(true);
32
+ });
33
+
34
+ it('rejects different simple selectors', async () => {
35
+ let sel1 = el('.a');
36
+ let sel2 = el('.b');
37
+ sel1.eval(context);
38
+ sel2.eval(context);
39
+ expect(selectorMatch(sel1, sel2).fullMatch).toBe(false);
40
+ });
41
+ });
42
+
43
+ describe('compound selectors', () => {
44
+ it('matches identical compound selectors', async () => {
45
+ let sel1 = compound([el('.a'), el('.b')]);
46
+ let sel2 = compound([el('.a'), el('.b')]);
47
+ await sel1.eval(context);
48
+ await sel2.eval(context);
49
+ expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
50
+ });
51
+
52
+ it('matches rearranged compound selectors', async () => {
53
+ let sel1 = compound([el('.a'), el('.b')]);
54
+ let sel2 = compound([el('.b'), el('.a')]);
55
+ await sel1.eval(context);
56
+ await sel2.eval(context);
57
+ expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
58
+ });
59
+
60
+ it('returns a partial match when a compound has extra members inside the matched span', async () => {
61
+ let sel1 = compound([el('.b'), el('.a'), pseudo({ name: ':hover' })]);
62
+ let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
63
+ await sel1.eval(context);
64
+ await sel2.eval(context);
65
+ let result = selectorMatch(sel2, sel1);
66
+ expect(result.fullMatch).toBe(false);
67
+ expect(result.partialMatch).toBe(true);
68
+ expect(result.matches).toHaveLength(1);
69
+ expect(result.matches[0]!.startIndex).toBe(0);
70
+ expect(result.matches[0]!.endIndex).toBe(2);
71
+ expect(result.matches[0]!.matchedIndices).toEqual([0, 2]);
72
+ expect(result.matches[0]!.consumedTarget).toBe(false);
73
+ });
74
+
75
+ it('returns a partial match for .b:hover within .b.a:hover', async () => {
76
+ let sel1 = compound([el('.b'), el('.a'), pseudo({ name: ':hover' })]);
77
+ let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
78
+ await sel1.eval(context);
79
+ await sel2.eval(context);
80
+ let result = selectorMatch(sel2, sel1);
81
+ expect(result.fullMatch).toBe(false);
82
+ expect(result.partialMatch).toBe(true);
83
+ expect(result.matches).toHaveLength(1);
84
+ expect(result.matches[0]!.startIndex).toBe(0);
85
+ expect(result.matches[0]!.endIndex).toBe(2);
86
+ expect(result.matches[0]!.matchedIndices).toEqual([0, 2]);
87
+ });
88
+
89
+ it('returns a partial match for .b.x within .b.a.x', async () => {
90
+ let sel1 = compound([el('.b'), el('.a'), el('.x')]);
91
+ let sel2 = compound([el('.b'), el('.x')]);
92
+ await sel1.eval(context);
93
+ await sel2.eval(context);
94
+ let result = selectorMatch(sel2, sel1);
95
+ expect(result.fullMatch).toBe(false);
96
+ expect(result.partialMatch).toBe(true);
97
+ expect(result.matches).toHaveLength(1);
98
+ expect(result.matches[0]!.startIndex).toBe(0);
99
+ expect(result.matches[0]!.endIndex).toBe(2);
100
+ expect(result.matches[0]!.matchedIndices).toEqual([0, 2]);
101
+ });
102
+ });
103
+
104
+ describe('pseudo-selectors', () => {
105
+ describe(':is()', () => {
106
+ it('matches identical :is() pseudo-selectors', async () => {
107
+ let sel1 = pseudo({ name: ':is', arg: el('.a') });
108
+ let sel2 = pseudo({ name: ':is', arg: el('.a') });
109
+ await sel1.eval(context);
110
+ await sel2.eval(context);
111
+ expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
112
+ });
113
+
114
+ it('normalizes simple selector in :is()', async () => {
115
+ let sel1 = pseudo({ name: ':is', arg: el('.a') });
116
+ let sel2 = el('.a');
117
+ await sel1.eval(context);
118
+ await sel2.eval(context);
119
+ expect(selectorMatch(sel1, sel2).fullMatch).toBe(true);
120
+ });
121
+
122
+ it('normalizes compound selector in :is()', async () => {
123
+ let sel1 = pseudo({ name: ':is', arg: compound([el('.a'), el('.b')]) });
124
+ let sel2 = compound([el('.b'), el('.a')]);
125
+ await sel1.eval(context);
126
+ await sel2.eval(context);
127
+ let result = selectorMatch(sel1, sel2);
128
+ expect(result.fullMatch).toBe(true);
129
+ });
130
+
131
+ it('matches a selector in :is()', async () => {
132
+ let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: compound([el('.a'), el('.b')]) })]);
133
+ let sel2 = compound([el('.b'), el('.a')]);
134
+ await sel1.eval(context);
135
+ await sel2.eval(context);
136
+ let result = selectorMatch(sel1, sel2);
137
+ expect(result.fullMatch).toBe(false);
138
+ expect(result.partialMatch).toBe(true);
139
+ });
140
+
141
+ it('matches an :is() as a full match #1', async () => {
142
+ let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: compound([el('.a'), el('.b')]) })]);
143
+ let sel2 = sel([el('foo'), co('>'), compound([el('.a'), is(el('.b'))])]);
144
+ await sel1.eval(context);
145
+ await sel2.eval(context);
146
+ let result = selectorMatch(sel1, sel2);
147
+ expect(result.fullMatch).toBe(true);
148
+ expect(result.partialMatch).toBe(true);
149
+ expect(result.matches).toHaveLength(1);
150
+ });
151
+
152
+ it('matches an :is() as a full match #2', async () => {
153
+ let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([compound([el('.a'), el('.b')]), co('>'), el('.b')]) })]);
154
+ let sel2 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([compound([el('.b'), el('.a')]), co('>'), el('.b')]) })]);
155
+ await sel1.eval(context);
156
+ await sel2.eval(context);
157
+ let result = selectorMatch(sel1, sel2);
158
+ expect(result.fullMatch).toBe(true);
159
+ expect(result.partialMatch).toBe(true);
160
+ expect(result.matches).toHaveLength(1);
161
+ });
162
+
163
+ it('matches an :is() as a full match #3', async () => {
164
+ let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sellist([sel([el('q'), co('>'), el('x')]), sel([compound([el('.a'), el('.b')]), co('>'), el('.b')])]) })]);
165
+ let sel2 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([compound([el('.b'), el('.a')]), co('>'), el('.b')]) })]);
166
+ await sel1.eval(context);
167
+ await sel2.eval(context);
168
+ let result = selectorMatch(sel1, sel2);
169
+ expect(result.fullMatch).toBe(true);
170
+ expect(result.partialMatch).toBe(true);
171
+ expect(result.matches).toHaveLength(1);
172
+ });
173
+
174
+ it('matches an :is() as a full match #4', async () => {
175
+ let sel1 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sellist([sel([el('q'), co('>'), el('x')]), sel([compound([el('.a'), el('.b')]), co('>'), el('.b')])]) })]);
176
+ let sel2 = sel([el('foo'), co('>'), pseudo({ name: ':is', arg: sel([el('q'), co('>'), el('x')]) })]);
177
+ await sel1.eval(context);
178
+ await sel2.eval(context);
179
+ let result = selectorMatch(sel1, sel2);
180
+ expect(result.fullMatch).toBe(true);
181
+ expect(result.partialMatch).toBe(true);
182
+ expect(result.matches).toHaveLength(1);
183
+ });
184
+
185
+ it('matches through deeply nested :is() alternates', async () => {
186
+ let sel1 = pseudo({
187
+ name: ':is',
188
+ arg: sellist([
189
+ pseudo({ name: ':is', arg: sellist([el('.a'), el('.b')]) }),
190
+ el('.c')
191
+ ])
192
+ });
193
+ let sel2 = el('.b');
194
+ await sel1.eval(context);
195
+ await sel2.eval(context);
196
+ let result = selectorMatch(sel2, sel1);
197
+ expect(result.fullMatch).toBe(true);
198
+ expect(result.partialMatch).toBe(true);
199
+ expect(result.matches).toHaveLength(1);
200
+ });
201
+
202
+ it('doesn\'t match branched :is() as full match #1', async () => {
203
+ let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.b')]) })]);
204
+ let sel2 = sel([el('.a'), co('>'), el('.b')]);
205
+ await sel1.eval(context);
206
+ await sel2.eval(context);
207
+ let result = selectorMatch(sel1, sel2);
208
+ expect(result.fullMatch).toBe(false);
209
+ expect(result.partialMatch).toBe(true);
210
+ expect(result.crossesAmpersand).toBe(false);
211
+ expect(result.matches).toHaveLength(0);
212
+ });
213
+
214
+ it('doesn\'t match branched :is() as full match #2', async () => {
215
+ let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
216
+ let sel2 = sel([el('.a'), co('>'), el('.b')]);
217
+ await sel1.eval(context);
218
+ await sel2.eval(context);
219
+ let result = selectorMatch(sel1, sel2);
220
+ expect(result.fullMatch).toBe(false);
221
+ expect(result.partialMatch).toBe(false);
222
+ expect(result.crossesAmpersand).toBe(false);
223
+ expect(result.matches).toHaveLength(0);
224
+ });
225
+
226
+ it('doesn\'t match branched :is() as full match #3', async () => {
227
+ let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
228
+ let sel2 = sel([el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
229
+ await sel1.eval(context);
230
+ await sel2.eval(context);
231
+ let result = selectorMatch(sel1, sel2);
232
+ expect(result.fullMatch).toBe(false);
233
+ expect(result.partialMatch).toBe(false);
234
+ expect(result.crossesAmpersand).toBe(false);
235
+ expect(result.matches).toHaveLength(0);
236
+ });
237
+
238
+ it('does match branched :is() as partial match #1', async () => {
239
+ let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
240
+ let sel2 = sel([el('.a'), co('>'), el('.c')]);
241
+ await sel1.eval(context);
242
+ await sel2.eval(context);
243
+ let result = selectorMatch(sel1, sel2);
244
+ expect(result.fullMatch).toBe(false);
245
+ expect(result.partialMatch).toBe(true);
246
+ expect(result.matches).toHaveLength(1);
247
+ });
248
+
249
+ it('does match branched :is() as partial match #2', async () => {
250
+ let sel1 = sel([el('.a'), co('>'), compound([el('.x'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })])]);
251
+ let sel2 = sel([el('.a'), co('>'), compound([el('.c'), el('.x')])]);
252
+ await sel1.eval(context);
253
+ await sel2.eval(context);
254
+ let result = selectorMatch(sel1, sel2);
255
+ expect(result.fullMatch).toBe(false);
256
+ expect(result.partialMatch).toBe(true);
257
+ expect(result.matches).toHaveLength(1);
258
+ });
259
+
260
+ it('does match branched :is() as partial match #3', async () => {
261
+ let sel1 = sel([el('.a'), co('>'), compound([pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) }), el('.x')])]);
262
+ let sel2 = sel([el('.a'), co('>'), compound([el('.c'), el('.x')])]);
263
+ await sel1.eval(context);
264
+ await sel2.eval(context);
265
+ let result = selectorMatch(sel1, sel2);
266
+ expect(result.fullMatch).toBe(false);
267
+ expect(result.partialMatch).toBe(true);
268
+ expect(result.matches).toHaveLength(1);
269
+ });
270
+
271
+ it('does match branched :is() as partial match #4', async () => {
272
+ let sel1 = sel([el('.a'), co('>'), pseudo({ name: ':is', arg: sel([el('.b'), co('>'), el('.c')]) })]);
273
+ let sel2 = sel([el('.b'), co('>'), el('.c')]);
274
+ await sel1.eval(context);
275
+ await sel2.eval(context);
276
+ let result = selectorMatch(sel1, sel2);
277
+ expect(result.fullMatch).toBe(false);
278
+ expect(result.partialMatch).toBe(true);
279
+ });
280
+ });
281
+
282
+ describe('other pseudos', () => {
283
+ it('returns a partial match when only the inner selector matches', async () => {
284
+ let sel0 = compound([el('.a'), el('.b')]);
285
+ let sel1 = pseudo({ name: ':where', arg: sel0 });
286
+ let sel2 = compound([el('.b'), el('.a')]);
287
+ await sel1.eval(context);
288
+ await sel2.eval(context);
289
+ let result = selectorMatch(sel1, sel2);
290
+ expect(result.fullMatch).toBe(false);
291
+ expect(result.partialMatch).toBe(true);
292
+ expect(result.matches[0]!.exact).toBe(false);
293
+ expect(result.matches[0]!.containingNode).toBe(sel0);
294
+ });
295
+
296
+ it('does not continue a match across a nested pseudo boundary', async () => {
297
+ let sel1 = compound([el('.a'), pseudo({ name: ':where', arg: el('.b') })]);
298
+ let sel2 = compound([el('.a'), el('.b')]);
299
+ await sel1.eval(context);
300
+ await sel2.eval(context);
301
+ let result = selectorMatch(sel1, sel2);
302
+ expect(result.fullMatch).toBe(false);
303
+ expect(result.partialMatch).toBe(false);
304
+ });
305
+
306
+ it('does not continue a match across a leading pseudo boundary', async () => {
307
+ let sel1 = compound([pseudo({ name: ':where', arg: el('.a') }), el('.b')]);
308
+ let sel2 = compound([el('.a'), el('.b')]);
309
+ await sel1.eval(context);
310
+ await sel2.eval(context);
311
+ let result = selectorMatch(sel1, sel2);
312
+ expect(result.fullMatch).toBe(false);
313
+ expect(result.partialMatch).toBe(false);
314
+ });
315
+
316
+ it('does not match non-matching pseudo names', async () => {
317
+ let sel1 = pseudo({ name: ':where', arg: el('.a') });
318
+ let sel2 = pseudo({ name: ':not', arg: el('.a') });
319
+ await sel1.eval(context);
320
+ await sel2.eval(context);
321
+ let result = selectorMatch(sel1, sel2);
322
+ expect(result.fullMatch).toBe(false);
323
+ expect(result.partialMatch).toBe(false);
324
+ });
325
+
326
+ it('matches equivalent pseudo selectors', async () => {
327
+ let sel1 = pseudo({ name: ':not', arg: compound([el('.a'), el('.b')]) });
328
+ let sel2 = pseudo({ name: ':not', arg: compound([el('.b'), el('.a')]) });
329
+ await sel1.eval(context);
330
+ await sel2.eval(context);
331
+ let result = selectorMatch(sel1, sel2);
332
+ expect(result.fullMatch).toBe(true);
333
+ expect(result.partialMatch).toBe(true);
334
+ });
335
+
336
+ it('matches equivalent pseudo selectors with selector-list alternates', async () => {
337
+ let sel1 = pseudo({ name: ':not', arg: compound([el('.a'), is(sellist([el('.b'), el('.x')]))]) });
338
+ let sel2 = pseudo({ name: ':not', arg: compound([el('.b'), el('.a')]) });
339
+ await sel1.eval(context);
340
+ await sel2.eval(context);
341
+ let result = selectorMatch(sel1, sel2);
342
+ expect(result.fullMatch).toBe(true);
343
+ expect(result.partialMatch).toBe(true);
344
+ });
345
+
346
+ it('matches equivalent pseudo selectors with complex selector args', async () => {
347
+ let sel1 = compound([
348
+ pseudo({ name: ':where', arg: sel([el('.a'), co('>'), el('.b')]) }),
349
+ el('.c')
350
+ ]);
351
+ let sel2 = compound([
352
+ el('.c'),
353
+ pseudo({ name: ':where', arg: sel([el('.a'), co('>'), el('.b')]) })
354
+ ]);
355
+ await sel1.eval(context);
356
+ await sel2.eval(context);
357
+ let result = selectorMatch(sel1, sel2);
358
+ expect(result.fullMatch).toBe(true);
359
+ expect(result.partialMatch).toBe(true);
360
+ expect(result.matches).toHaveLength(1);
361
+ });
362
+
363
+ it('matches equivalent mixed nested pseudo selectors', async () => {
364
+ let sel1 = pseudo({
365
+ name: ':not',
366
+ arg: compound([
367
+ pseudo({ name: ':is', arg: sellist([el('.a'), el('.c')]) }),
368
+ el('.d')
369
+ ])
370
+ });
371
+ let sel2 = pseudo({
372
+ name: ':not',
373
+ arg: compound([
374
+ el('.d'),
375
+ pseudo({ name: ':is', arg: sellist([el('.c'), el('.a')]) })
376
+ ])
377
+ });
378
+ await sel1.eval(context);
379
+ await sel2.eval(context);
380
+ let result = selectorMatch(sel1, sel2);
381
+ expect(result.fullMatch).toBe(true);
382
+ expect(result.partialMatch).toBe(true);
383
+ expect(result.matches).toHaveLength(1);
384
+ });
385
+
386
+ it('recursively searches through multiple nested pseudo boundaries', async () => {
387
+ let sel1 = pseudo({
388
+ name: ':not',
389
+ arg: pseudo({
390
+ name: ':where',
391
+ arg: pseudo({
392
+ name: ':is',
393
+ arg: compound([el('.a'), el('.b')])
394
+ })
395
+ })
396
+ });
397
+ let sel2 = el('.a');
398
+ await sel1.eval(context);
399
+ await sel2.eval(context);
400
+ let result = selectorMatch(sel2, sel1);
401
+ expect(result.fullMatch).toBe(false);
402
+ expect(result.partialMatch).toBe(true);
403
+ expect(result.matches.length).toBeGreaterThan(0);
404
+ expect(result.matches.every(match => !match.exact)).toBe(true);
405
+ });
406
+ });
407
+ });
408
+
409
+ describe('selector lists and branching', () => {
410
+ describe('direct matches', () => {
411
+ it('finds a selector in a selector list #1', async () => {
412
+ let sel1 = sellist([el('.a'), el('.b')]);
413
+ let sel2 = el('.b');
414
+ await sel1.eval(context);
415
+ await sel2.eval(context);
416
+ expect(selectorMatch(sel2, sel1).fullMatch).toBe(true);
417
+ });
418
+
419
+ it('finds a selector in a selector list #2', async () => {
420
+ let sel1 = sellist([el('.b'), el('.x'), el('.y')]);
421
+ let sel2 = el('.b');
422
+ await sel1.eval(context);
423
+ await sel2.eval(context);
424
+ expect(selectorMatch(sel2, sel1).fullMatch).toBe(true);
425
+ });
426
+
427
+ it('finds a compound selector in a selector list', async () => {
428
+ let sel1 = sellist([compound([el('.b'), el('.x')]), el('.y')]);
429
+ let sel2 = compound([el('.x'), el('.b')]);
430
+ await sel1.eval(context);
431
+ await sel2.eval(context);
432
+ expect(selectorMatch(sel2, sel1).fullMatch).toBe(true);
433
+ });
434
+
435
+ it('matches a compound to a compound with an :is() in it', async () => {
436
+ let sel1 = compound([el('.a'), is(sellist([el('.x'), el('.c')])), el('.d')]);
437
+ let sel2 = compound([el('.d'), el('.a'), el('.c')]);
438
+ await sel1.eval(context);
439
+ await sel2.eval(context);
440
+ let result = selectorMatch(sel2, sel1);
441
+ expect(result.fullMatch).toBe(true);
442
+ expect(result.partialMatch).toBe(true);
443
+ });
444
+
445
+ it('reports a single selector-list item match against the list container', async () => {
446
+ let sel1 = sellist([el('a'), el('b'), el('c')]);
447
+ let sel2 = el('a');
448
+ await sel1.eval(context);
449
+ await sel2.eval(context);
450
+ let result = selectorMatch(sel2, sel1);
451
+ expect(result.fullMatch).toBe(true);
452
+ expect(result.partialMatch).toBe(true);
453
+ expect(result.matches).toHaveLength(1);
454
+ expect(result.matches[0]!.containingNode).toBe(sel1);
455
+ expect(result.matches[0]!.startIndex).toBe(0);
456
+ expect(result.matches[0]!.endIndex).toBe(0);
457
+ expect(result.matches[0]!.matchedIndices).toEqual([0]);
458
+ expect(result.matches[0]!.consumedTarget).toBe(false);
459
+ });
460
+
461
+ it('reports a complex selector-list item match against the list container', async () => {
462
+ let sel1 = sellist([sel([el('.a'), co('>'), el('.b')]), el('.c')]);
463
+ let sel2 = sel([el('.a'), co('>'), el('.b')]);
464
+ await sel1.eval(context);
465
+ await sel2.eval(context);
466
+ let result = selectorMatch(sel2, sel1);
467
+ expect(result.fullMatch).toBe(true);
468
+ expect(result.partialMatch).toBe(true);
469
+ expect(result.matches).toHaveLength(1);
470
+ expect(result.matches[0]!.containingNode).toBe(sel1);
471
+ expect(result.matches[0]!.startIndex).toBe(0);
472
+ expect(result.matches[0]!.endIndex).toBe(0);
473
+ expect(result.matches[0]!.matchedIndices).toEqual([0]);
474
+ expect(result.matches[0]!.consumedTarget).toBe(false);
475
+ });
476
+
477
+ it('reports a pseudo-wrapped selector-list item match against the list container', async () => {
478
+ let sel1 = sellist([pseudo({ name: ':where', arg: compound([el('.a'), el('.b')]) }), el('.c')]);
479
+ let sel2 = compound([el('.b'), el('.a')]);
480
+ await sel1.eval(context);
481
+ await sel2.eval(context);
482
+ let result = selectorMatch(sel2, sel1);
483
+ expect(result.fullMatch).toBe(false);
484
+ expect(result.partialMatch).toBe(true);
485
+ expect(result.matches).toHaveLength(1);
486
+ expect(result.matches[0]!.containingNode).toBe(sel1);
487
+ expect(result.matches[0]!.startIndex).toBe(0);
488
+ expect(result.matches[0]!.endIndex).toBe(0);
489
+ expect(result.matches[0]!.matchedIndices).toEqual([0]);
490
+ });
491
+
492
+ it('treats a find-side selector list as alternates', async () => {
493
+ let sel1 = el('a');
494
+ let sel2 = sellist([el('a'), el('b'), el('c')]);
495
+ await sel1.eval(context);
496
+ await sel2.eval(context);
497
+ let result = selectorMatch(sel2, sel1);
498
+ expect(result.fullMatch).toBe(true);
499
+ expect(result.partialMatch).toBe(true);
500
+ expect(result.matches).toHaveLength(1);
501
+ expect(result.matches[0]!.containingNode).toBe(sel1);
502
+ });
503
+ });
504
+
505
+ describe('partial matches', () => {
506
+ it('finds (partially) a compound selector in a selector list', async () => {
507
+ let sel1 = sellist([sel([compound([el('.b'), el('.x')]), co('>'), el('.y')]), el('.z')]);
508
+ let sel2 = compound([el('.x'), el('.b')]);
509
+ await sel1.eval(context);
510
+ await sel2.eval(context);
511
+ expect(selectorMatch(sel2, sel1).fullMatch).toBe(false);
512
+ expect(selectorMatch(sel2, sel1).partialMatch).toBe(true);
513
+ });
514
+
515
+ it('returns a partial match for partial match in a compound selector', async () => {
516
+ let sel1 = compound([el('.a'), is(sellist([el('.x'), el('.c')])), el('.d')]);
517
+ let sel2 = compound([el('.d'), el('.c')]);
518
+ await sel1.eval(context);
519
+ await sel2.eval(context);
520
+ let result = selectorMatch(sel2, sel1);
521
+ expect(result.fullMatch).toBe(false);
522
+ expect(result.partialMatch).toBe(true);
523
+ });
524
+
525
+ it('returns a partial match for partial match in a complex selector', async () => {
526
+ let sel1 = sel([el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
527
+ let sel2 = sel([el('.a'), co('>'), el('.b')]);
528
+ await sel1.eval(context);
529
+ await sel2.eval(context);
530
+ let result = selectorMatch(sel2, sel1);
531
+ expect(result.fullMatch).toBe(false);
532
+ expect(result.partialMatch).toBe(true);
533
+ });
534
+
535
+ it('returns a partial match for partial compound match in a complex selector', async () => {
536
+ let sel1 = sel([el('.a'), co('>'), compound([el('.b'), el('.x')]), co('>'), el('.c')]);
537
+ let sel2 = sel([el('.a'), co('>'), el('.b')]);
538
+ await sel1.eval(context);
539
+ await sel2.eval(context);
540
+ let result = selectorMatch(sel2, sel1);
541
+ expect(result.fullMatch).toBe(false);
542
+ expect(result.partialMatch).toBe(true);
543
+ });
544
+
545
+ it('does not give up finding a match too soon', async () => {
546
+ let sel1 = sel([el('.a'), co('>'), el('.c'), co('>'), el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
547
+ let sel2 = sel([el('.a'), co('>'), el('.c')]);
548
+ await sel1.eval(context);
549
+ await sel2.eval(context);
550
+ let result = selectorMatch(sel2, sel1);
551
+ expect(result.fullMatch).toBe(false);
552
+ expect(result.partialMatch).toBe(true);
553
+ });
554
+ });
555
+
556
+ describe('misses', () => {
557
+ it('does not find a match #1', async () => {
558
+ let sel1 = sel([el('.a'), co('>'), compound([el('.b'), el('.x')]), co('>'), el('.c')]);
559
+ let sel2 = sel([el('.a'), co('>'), el('.c')]);
560
+ await sel1.eval(context);
561
+ await sel2.eval(context);
562
+ let result = selectorMatch(sel2, sel1);
563
+ expect(result.fullMatch).toBe(false);
564
+ expect(result.partialMatch).toBe(false);
565
+ });
566
+
567
+ it('does not find a match #2', async () => {
568
+ let sel1 = sel([el('.a'), co('>'), compound([el('.b'), el('.x')]), co('>'), el('.c')]);
569
+ let sel2 = sel([el('.q'), co('>'), el('.r')]);
570
+ await sel1.eval(context);
571
+ await sel2.eval(context);
572
+ let result = selectorMatch(sel2, sel1);
573
+ expect(result.fullMatch).toBe(false);
574
+ expect(result.partialMatch).toBe(false);
575
+ });
576
+
577
+ it('does not find a match #3', async () => {
578
+ let sel1 = sel([el('.a'), co('>'), el('.b'), co('>'), el('.c')]);
579
+ let sel2 = sel([el('.a'), co('>'), el('.c')]);
580
+ await sel1.eval(context);
581
+ await sel2.eval(context);
582
+ let result = selectorMatch(sel2, sel1);
583
+ expect(result.fullMatch).toBe(false);
584
+ expect(result.partialMatch).toBe(false);
585
+ });
586
+ });
587
+
588
+ describe('multiple matches', () => {
589
+ it('can find multiple matches #1', async () => {
590
+ let sel1 = sel([el('.a'), co('>'), el('.c'), co('>'), el('.a'), co('>'), el('.c')]);
591
+ let sel2 = sel([el('.a'), co('>'), el('.c')]);
592
+ await sel1.eval(context);
593
+ await sel2.eval(context);
594
+ let result = selectorMatch(sel2, sel1);
595
+ let fullMatches = result.matches.filter(match => match.exact);
596
+ let partialMatches = result.matches.filter(match => !match.exact);
597
+ expect(result.fullMatch).toBe(false);
598
+ expect(result.partialMatch).toBe(true);
599
+ expect(fullMatches).toHaveLength(0);
600
+ expect(partialMatches).toHaveLength(2);
601
+ expect(partialMatches[0]!.startIndex).toBe(0);
602
+ expect(partialMatches[0]!.endIndex).toBe(2);
603
+ expect(partialMatches[0]!.containingNode).toBe(sel1);
604
+ expect(partialMatches[1]!.startIndex).toBe(4);
605
+ expect(partialMatches[1]!.endIndex).toBe(6);
606
+ expect(partialMatches[1]!.containingNode).toBe(sel1);
607
+ });
608
+
609
+ it('can find multiple matches #2', async () => {
610
+ let sel1 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
611
+ let sel2 = compound([el('.a'), el('.b')]);
612
+ await sel1.eval(context);
613
+ await sel2.eval(context);
614
+ let result = selectorMatch(sel2, sel1);
615
+ let fullMatches = result.matches.filter(match => match.exact);
616
+ let partialMatches = result.matches.filter(match => !match.exact);
617
+ expect(result.fullMatch).toBe(false);
618
+ expect(result.partialMatch).toBe(true);
619
+ expect(fullMatches).toHaveLength(0);
620
+ expect(partialMatches).toHaveLength(2);
621
+ expect(partialMatches[0]!.startIndex).toBe(0);
622
+ expect(partialMatches[0]!.endIndex).toBe(1);
623
+ expect(partialMatches[0]!.containingNode).toBe(sel1);
624
+ expect(partialMatches[1]!.startIndex).toBe(3);
625
+ expect(partialMatches[1]!.endIndex).toBe(4);
626
+ expect(partialMatches[1]!.containingNode).toBe(sel1);
627
+ });
628
+
629
+ it('can find multiple matches #3', async () => {
630
+ let sel1 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
631
+ let sel2 = el('.a');
632
+ await sel1.eval(context);
633
+ await sel2.eval(context);
634
+ let result = selectorMatch(sel2, sel1);
635
+ let fullMatches = result.matches.filter(match => match.exact);
636
+ let partialMatches = result.matches.filter(match => !match.exact);
637
+ expect(result.fullMatch).toBe(false);
638
+ expect(result.partialMatch).toBe(true);
639
+ expect(fullMatches).toHaveLength(0);
640
+ expect(partialMatches).toHaveLength(2);
641
+ expect(partialMatches[0]!.startIndex).toBe(0);
642
+ expect(partialMatches[0]!.endIndex).toBe(0);
643
+ expect(partialMatches[0]!.containingNode).toBe(sel1);
644
+ expect(partialMatches[1]!.startIndex).toBe(3);
645
+ expect(partialMatches[1]!.endIndex).toBe(3);
646
+ expect(partialMatches[1]!.containingNode).toBe(sel1);
647
+ });
648
+
649
+ it('can find multiple matches #4', async () => {
650
+ let sel0 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
651
+ let sel1 = sel([sel0, co('>'), el('.d')]);
652
+ let sel2 = el('.a');
653
+ await sel1.eval(context);
654
+ await sel2.eval(context);
655
+ let result = selectorMatch(sel2, sel1);
656
+ let fullMatches = result.matches.filter(match => match.exact);
657
+ let partialMatches = result.matches.filter(match => !match.exact);
658
+ expect(result.fullMatch).toBe(false);
659
+ expect(result.partialMatch).toBe(true);
660
+ expect(fullMatches).toHaveLength(0);
661
+ expect(partialMatches).toHaveLength(2);
662
+ expect(partialMatches[0]!.startIndex).toBe(0);
663
+ expect(partialMatches[0]!.endIndex).toBe(0);
664
+ expect((partialMatches[0]!.containingNode.sourceNode ?? partialMatches[0]!.containingNode)).toBe(sel0);
665
+ expect(partialMatches[1]!.startIndex).toBe(3);
666
+ expect(partialMatches[1]!.endIndex).toBe(3);
667
+ expect((partialMatches[1]!.containingNode.sourceNode ?? partialMatches[1]!.containingNode)).toBe(sel0);
668
+ });
669
+
670
+ it('can find multiple matches #5', async () => {
671
+ let sel0 = compound([el('.a'), el('.b'), el('.c'), el('.a'), el('.b')]);
672
+ let sel1 = sel([sel0, co('>'), el('.d')]);
673
+ let sel2 = compound([el('.b'), el('.a')]);
674
+ await sel1.eval(context);
675
+ await sel2.eval(context);
676
+ let result = selectorMatch(sel2, sel1);
677
+ let fullMatches = result.matches.filter(match => match.exact);
678
+ let partialMatches = result.matches.filter(match => !match.exact);
679
+ expect(result.fullMatch).toBe(false);
680
+ expect(result.partialMatch).toBe(true);
681
+ expect(fullMatches).toHaveLength(0);
682
+ expect(partialMatches).toHaveLength(2);
683
+ expect(partialMatches[0]!.startIndex).toBe(0);
684
+ expect(partialMatches[0]!.endIndex).toBe(1);
685
+ expect((partialMatches[0]!.containingNode.sourceNode ?? partialMatches[0]!.containingNode)).toBe(sel0);
686
+ expect(partialMatches[1]!.startIndex).toBe(3);
687
+ expect(partialMatches[1]!.endIndex).toBe(4);
688
+ expect((partialMatches[1]!.containingNode.sourceNode ?? partialMatches[1]!.containingNode)).toBe(sel0);
689
+ });
690
+ });
691
+
692
+ describe('miscellaneous', () => {
693
+ it('can continue the search into an ampersand', async () => {
694
+ let sel1 = compound([amp({ selectorContainer: { selector: el('a') } }), pseudo({ name: ':hover' })]);
695
+ let sel2 = compound([el('a'), pseudo({ name: ':hover' })]);
696
+ let evald1 = await sel1.eval(context);
697
+ let evald2 = await sel2.eval(context);
698
+ let result = selectorMatch(sel2, sel1);
699
+ expect(`${evald1}`).toBe('&:hover');
700
+ expect(`${evald2}`).toBe('a:hover');
701
+ expect(result.fullMatch).toBe(true);
702
+ expect(result.partialMatch).toBe(true);
703
+ expect(result.crossesAmpersand).toBe(true);
704
+ expect(result.matches).toHaveLength(1);
705
+ expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
706
+ expect(result.matches[0]!.ampersandCrossings![0]!.ampersandNode).toBe(sel1.get('value')[0]);
707
+ expect(result.matches[0]!.ampersandCrossings![0]!.targetSegment.containingNode).toBe(sel1);
708
+ expect(result.matches[0]!.ampersandCrossings![0]!.parentSegment!.containingNode.valueOf()).toBe('a');
709
+ });
710
+
711
+ it('matches near an ampersand but doesn\'t cross it #1', async () => {
712
+ let inner = el('a').eval(context);
713
+ let sel1 = compound([amp({ selectorContainer: { selector: inner } }), el('.b'), pseudo({ name: ':hover' })]);
714
+ let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
715
+ let evald1 = await sel1.eval(context);
716
+ let evald2 = await sel2.eval(context);
717
+ let result = selectorMatch(sel2, sel1);
718
+ expect(`${evald1}`).toBe('&.b:hover');
719
+ expect(`${evald2}`).toBe('.b:hover');
720
+ expect(result.fullMatch).toBe(false);
721
+ expect(result.partialMatch).toBe(true);
722
+ expect(result.matches).toHaveLength(1);
723
+ expect(result.crossesAmpersand).toBe(false);
724
+ });
725
+
726
+ it('matches near an ampersand but doesn\'t cross it #2', async () => {
727
+ let inner = el('.c').eval(context);
728
+ let sel1 = compound([el('.b'), pseudo({ name: ':hover' }), amp({ selectorContainer: { selector: inner } }), el('.b')]);
729
+ let sel2 = compound([el('.b'), pseudo({ name: ':hover' })]);
730
+ let evald1 = await sel1.eval(context);
731
+ let evald2 = await sel2.eval(context);
732
+ let result = selectorMatch(sel2, sel1);
733
+ expect(`${evald1}`).toBe('.b:hover&.b');
734
+ expect(`${evald2}`).toBe('.b:hover');
735
+ expect(result.fullMatch).toBe(false);
736
+ expect(result.partialMatch).toBe(true);
737
+ expect(result.matches).toHaveLength(1);
738
+ expect(result.crossesAmpersand).toBe(false);
739
+ expect(result.matches[0]!.ampersandCrossings).toBeUndefined();
740
+ });
741
+
742
+ it('matches repeated ampersand compounds in one complex selector', async () => {
743
+ let parentSelector = compound([el('.a'), el('.b')]).eval(context);
744
+ let sel1 = sel([
745
+ compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')]),
746
+ co(' '),
747
+ compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')])
748
+ ]);
749
+ let sel2 = compound([el('.b'), el('.x')]);
750
+ await sel1.eval(context);
751
+ await sel2.eval(context);
752
+ let result = selectorMatch(sel2, sel1);
753
+ expect(result.fullMatch).toBe(false);
754
+ expect(result.partialMatch).toBe(true);
755
+ expect(result.crossesAmpersand).toBe(true);
756
+ expect(result.matches).toHaveLength(2);
757
+ expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
758
+ expect(result.matches[1]!.ampersandCrossings).toHaveLength(1);
759
+ });
760
+
761
+ it('matches one repeated ampersand compound against a fully resolved compound find', async () => {
762
+ let parentSelector = compound([el('.a'), el('.b')]).eval(context);
763
+ let sel1 = sel([
764
+ compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')]),
765
+ co(' '),
766
+ compound([amp({ selectorContainer: { selector: parentSelector } }), el('.x')])
767
+ ]);
768
+ let sel2 = compound([el('.a'), el('.b'), el('.x')]);
769
+ await sel1.eval(context);
770
+ await sel2.eval(context);
771
+ let result = selectorMatch(sel2, sel1, parentSelector);
772
+ expect(result.fullMatch).toBe(false);
773
+ expect(result.partialMatch).toBe(true);
774
+ expect(result.crossesAmpersand).toBe(true);
775
+ expect(result.matches).toHaveLength(2);
776
+ expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
777
+ expect(result.matches[1]!.ampersandCrossings).toHaveLength(1);
778
+ });
779
+
780
+ it('matches repeated :is() compounds in one complex selector', async () => {
781
+ let sel1 = sel([
782
+ compound([is(compound([el('.a'), el('.b')])), el('.x')]),
783
+ co(' '),
784
+ compound([is(compound([el('.a'), el('.b')])), el('.x')])
785
+ ]);
786
+ let sel2 = compound([el('.b'), el('.x')]);
787
+ await sel1.eval(context);
788
+ await sel2.eval(context);
789
+ let result = selectorMatch(sel2, sel1);
790
+ expect(result.fullMatch).toBe(false);
791
+ expect(result.partialMatch).toBe(true);
792
+ expect(result.crossesAmpersand).toBe(false);
793
+ expect(result.matches).toHaveLength(2);
794
+ });
795
+
796
+ it('can continue through a non-leading ampersand with a complex resolved parent', async () => {
797
+ let parentSelector = sel([el('.grand'), co('>'), el('.parent')]).eval(context);
798
+ let sel1 = sel([
799
+ amp({ selectorContainer: { selector: parentSelector } }),
800
+ co(' '),
801
+ el('.prefix'),
802
+ co(' '),
803
+ amp({ selectorContainer: { selector: parentSelector } }),
804
+ co(' '),
805
+ el('.child')
806
+ ]);
807
+ let sel2 = sel([el('.grand'), co('>'), el('.parent'), co(' '), el('.child')]);
808
+ await sel1.eval(context);
809
+ await sel2.eval(context);
810
+ let result = selectorMatch(sel2, sel1, parentSelector);
811
+ expect(result.fullMatch).toBe(false);
812
+ expect(result.partialMatch).toBe(true);
813
+ expect(result.crossesAmpersand).toBe(true);
814
+ expect(result.matches).toHaveLength(1);
815
+ expect(result.matches[0]!.startIndex).toBe(4);
816
+ expect(result.matches[0]!.endIndex).toBe(6);
817
+ expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
818
+ expect(result.matches[0]!.ampersandCrossings![0]!.ampersandNode).toBe(sel1.get('value')[4]);
819
+ expect(result.matches[0]!.ampersandCrossings![0]!.targetSegment.containingNode).toBe(sel1);
820
+ expect(
821
+ result.matches[0]!.ampersandCrossings![0]!.parentSegment!.containingNode.valueOf()
822
+ ).toBe(parentSelector.valueOf());
823
+ });
824
+
825
+ describe('parent selector context', () => {
826
+ it('uses the parent context for a plain target selector', async () => {
827
+ let parent = sellist([el('div'), el('span')]);
828
+ let sel1 = el('.a');
829
+ let sel2 = sel([el('span'), co(' '), el('.a')]);
830
+ await parent.eval(context);
831
+ await sel1.eval(context);
832
+ await sel2.eval(context);
833
+ let result = selectorMatch(sel2, sel1, parent as any);
834
+ expect(result.fullMatch).toBe(true);
835
+ expect(result.partialMatch).toBe(true);
836
+ expect(result.crossesAmpersand).toBe(true);
837
+ expect(result.matches).toHaveLength(1);
838
+ expect(result.matches[0]!.ampersandCrossings).toHaveLength(1);
839
+ expect(result.matches[0]!.ampersandCrossings![0]!.targetSegment.containingNode).toBe(sel1);
840
+ expect(result.matches[0]!.ampersandCrossings![0]!.parentSegment!.containingNode.valueOf()).toBe('span');
841
+ });
842
+
843
+ it('uses a parent selector list as alternates across a leading ampersand', async () => {
844
+ let parent = sellist([el('div'), el('span')]);
845
+ let sel1 = sel([amp(), co(' '), el('.a')]);
846
+ let sel2 = sel([el('span'), co(' '), el('.a')]);
847
+ await parent.eval(context);
848
+ await sel1.eval(context);
849
+ await sel2.eval(context);
850
+ let result = selectorMatch(sel2, sel1, parent as any);
851
+ expect(result.fullMatch).toBe(true);
852
+ expect(result.partialMatch).toBe(true);
853
+ expect(result.crossesAmpersand).toBe(true);
854
+ expect(result.matches).toHaveLength(1);
855
+ });
856
+
857
+ it('does not search the parent when the target already fully matched before the leading ampersand', async () => {
858
+ let parent = sellist([el('div'), el('span')]);
859
+ let sel1 = sel([amp(), co(' '), el('.a')]);
860
+ let sel2 = el('.a');
861
+ await parent.eval(context);
862
+ await sel1.eval(context);
863
+ await sel2.eval(context);
864
+ let result = selectorMatch(sel2, sel1, parent as any);
865
+ expect(result.fullMatch).toBe(false);
866
+ expect(result.partialMatch).toBe(true);
867
+ expect(result.crossesAmpersand).toBe(false);
868
+ expect(result.matches).toHaveLength(1);
869
+ });
870
+
871
+ it('does not search the parent when nothing matched before the leading ampersand', async () => {
872
+ let parent = sellist([el('div'), el('span')]);
873
+ let sel1 = sel([amp(), co(' '), el('.a')]);
874
+ let sel2 = el('span');
875
+ await parent.eval(context);
876
+ await sel1.eval(context);
877
+ await sel2.eval(context);
878
+ let result = selectorMatch(sel2, sel1, parent as any);
879
+ expect(result.fullMatch).toBe(false);
880
+ expect(result.partialMatch).toBe(false);
881
+ expect(result.crossesAmpersand).toBe(false);
882
+ expect(result.matches).toHaveLength(0);
883
+ });
884
+
885
+ it('does not add matches that exist only inside an explicit ampersand selector', async () => {
886
+ let inner = el('a').eval(context);
887
+ let sel1 = compound([amp({ selectorContainer: { selector: inner } }), pseudo({ name: ':hover' })]);
888
+ let sel2 = el('a');
889
+ await sel1.eval(context);
890
+ await sel2.eval(context);
891
+ let result = selectorMatch(sel2, sel1);
892
+ expect(result.fullMatch).toBe(false);
893
+ expect(result.partialMatch).toBe(false);
894
+ expect(result.crossesAmpersand).toBe(false);
895
+ expect(result.matches).toHaveLength(0);
896
+ });
897
+
898
+ it('does not match an implicit space combinator #1', async () => {
899
+ let parent = sellist([el('div'), el('span')]);
900
+ let sel1 = el('.a');
901
+ let sel2 = compound([el('span'), el('.a')]);
902
+ await parent.eval(context);
903
+ await sel1.eval(context);
904
+ await sel2.eval(context);
905
+ let result = selectorMatch(sel2, sel1, parent as any);
906
+ expect(result.fullMatch).toBe(false);
907
+ expect(result.partialMatch).toBe(false);
908
+ expect(result.crossesAmpersand).toBe(false);
909
+ expect(result.matches).toHaveLength(0);
910
+ });
911
+
912
+ it('does not match an implicit space combinator #2', async () => {
913
+ let parent = el('a');
914
+ let sel1 = el(':hover');
915
+ let sel2 = compound([el('a'), el(':hover')]);
916
+ await parent.eval(context);
917
+ await sel1.eval(context);
918
+ await sel2.eval(context);
919
+ let result = selectorMatch(sel2, sel1, parent as any);
920
+ expect(result.fullMatch).toBe(false);
921
+ expect(result.partialMatch).toBe(false);
922
+ expect(result.crossesAmpersand).toBe(false);
923
+ expect(result.matches).toHaveLength(0);
924
+ });
925
+
926
+ it('does match an implicit space combinator #1', async () => {
927
+ let parent = el('a');
928
+ let sel1 = el(':hover');
929
+ let sel2 = sel([el('a'), co(' '), el(':hover')]);
930
+ await parent.eval(context);
931
+ await sel1.eval(context);
932
+ await sel2.eval(context);
933
+ let result = selectorMatch(sel2, sel1, parent as any);
934
+ expect(result.fullMatch).toBe(true);
935
+ expect(result.partialMatch).toBe(true);
936
+ expect(result.crossesAmpersand).toBe(true);
937
+ expect(result.matches).toHaveLength(1);
938
+ });
939
+
940
+ it('does match an implicit space combinator #2', async () => {
941
+ let parent = sellist([el('div'), el('span')]);
942
+ let sel1 = el(':hover');
943
+ let sel2 = sel([el('div'), co(' '), el(':hover')]);
944
+ await parent.eval(context);
945
+ await sel1.eval(context);
946
+ await sel2.eval(context);
947
+ let result = selectorMatch(sel2, sel1, parent as any);
948
+ expect(result.fullMatch).toBe(true);
949
+ expect(result.partialMatch).toBe(true);
950
+ expect(result.crossesAmpersand).toBe(true);
951
+ expect(result.matches).toHaveLength(1);
952
+ });
953
+
954
+ it('does full-match a local nested selector against a simple parent context', async () => {
955
+ const parent = el('.aa');
956
+ const target = el('.dd');
957
+ const find = el('.dd');
958
+ await parent.eval(context);
959
+ await target.eval(context);
960
+ await find.eval(context);
961
+ const result = selectorMatch(find, target, parent as any);
962
+ expect(result.fullMatch).toBe(true);
963
+ expect(result.partialMatch).toBe(true);
964
+ expect(result.crossesAmpersand).toBe(false);
965
+ });
966
+
967
+ it('does full-match a nested selector across a parent selector list for an exact route', async () => {
968
+ const parent = sellist([
969
+ compound([el('.replace'), el('.replace')]),
970
+ sel([compound([el('.c'), el('.replace')]), co('+'), el('.replace')])
971
+ ]);
972
+ const target = sellist([el('.replace'), el('.c')]);
973
+ const find = sel([compound([el('.replace'), el('.replace')]), co(' '), el('.replace')]);
974
+ await parent.eval(context);
975
+ await target.eval(context);
976
+ await find.eval(context);
977
+ const result = selectorMatch(find, target, parent as any);
978
+ expect(result.fullMatch).toBe(true);
979
+ expect(result.partialMatch).toBe(true);
980
+ expect(result.crossesAmpersand).toBe(true);
981
+ });
982
+
983
+ it('does full-match a repeated implicit ampersand compound against a simple parent', async () => {
984
+ const parent = el('.e');
985
+ const target = compound([amp(), amp()]);
986
+ const find = compound([el('.e'), el('.e')]);
987
+ await parent.eval(context);
988
+ await target.eval(context);
989
+ await find.eval(context);
990
+ const result = selectorMatch(find, target, parent as any);
991
+ expect(result.fullMatch).toBe(true);
992
+ expect(result.partialMatch).toBe(true);
993
+ expect(result.crossesAmpersand).toBe(true);
994
+ });
995
+
996
+ it('does not continue parent matching through a non-:is() pseudo boundary', async () => {
997
+ let parent = el('div');
998
+ let sel1 = sel([amp(), co(' '), pseudo({ name: ':where', arg: el('.a') })]);
999
+ let sel2 = sel([el('div'), co(' '), el('.a')]);
1000
+ await parent.eval(context);
1001
+ await sel1.eval(context);
1002
+ await sel2.eval(context);
1003
+ let result = selectorMatch(sel2, sel1, parent as any);
1004
+ expect(result.fullMatch).toBe(false);
1005
+ expect(result.partialMatch).toBe(false);
1006
+ expect(result.crossesAmpersand).toBe(false);
1007
+ expect(result.matches).toHaveLength(0);
1008
+ });
1009
+
1010
+ it('does continue parent matching through an :is() boundary', async () => {
1011
+ let parent = el('div');
1012
+ let sel1 = sel([amp(), co(' '), pseudo({ name: ':is', arg: el('.a') })]);
1013
+ let sel2 = sel([el('div'), co(' '), el('.a')]);
1014
+ await parent.eval(context);
1015
+ await sel1.eval(context);
1016
+ await sel2.eval(context);
1017
+ let result = selectorMatch(sel2, sel1, parent as any);
1018
+ expect(result.fullMatch).toBe(true);
1019
+ expect(result.partialMatch).toBe(true);
1020
+ expect(result.crossesAmpersand).toBe(true);
1021
+ expect(result.matches).toHaveLength(1);
1022
+ });
1023
+ });
1024
+ });
1025
+
1026
+ describe('implicit parent boundary with SelectorList', () => {
1027
+ it('matches .replace.replace .replace against SelectorList inner with SelectorList outer parent', () => {
1028
+ // Models the nested structure:
1029
+ // .replace.replace, .c.replace + .replace { .replace, .c { ... } }
1030
+ // find: .replace.replace .replace
1031
+ // target: inner SelectorList .replace, .c
1032
+ // parent: outer SelectorList .replace.replace, .c.replace + .replace
1033
+ const parent = sellist([
1034
+ compound([el('.replace'), el('.replace')]),
1035
+ sel([compound([el('.c'), el('.replace')]), co('+'), compound([el('.replace')])])
1036
+ ]);
1037
+ const target = sellist([el('.replace'), el('.c')]);
1038
+ const find = sel([compound([el('.replace'), el('.replace')]), co(' '), el('.replace')]);
1039
+
1040
+ const result = selectorMatch(find, target, parent);
1041
+ expect(result.fullMatch).toBe(true);
1042
+ });
1043
+ });
1044
+ });
1045
+
1046
+ // // ─────────────────────────────────────────────────
1047
+ // // arePseudoSelectorsEquivalent
1048
+ // // ─────────────────────────────────────────────────
1049
+ // describe('arePseudoSelectorsEquivalent', () => {
1050
+ // it('returns false for non-pseudo-selectors', () => {
1051
+ // expect(arePseudoSelectorsEquivalent(el('.a'), el('.a'))).toBe(false);
1052
+ // });
1053
+
1054
+ // it('matches pseudo-selectors with same name and no args', () => {
1055
+ // const a = pseudo({ name: ':hover' });
1056
+ // const b = pseudo({ name: ':hover' });
1057
+ // expect(arePseudoSelectorsEquivalent(a, b)).toBe(true);
1058
+ // });
1059
+
1060
+ // it('rejects pseudo-selectors with different names', () => {
1061
+ // const a = pseudo({ name: ':hover' });
1062
+ // const b = pseudo({ name: ':focus' });
1063
+ // expect(arePseudoSelectorsEquivalent(a, b)).toBe(false);
1064
+ // });
1065
+
1066
+ // it('matches :where() with same args', () => {
1067
+ // const a = pseudo({ name: ':where', arg: el('.x') });
1068
+ // const b = pseudo({ name: ':where', arg: el('.x') });
1069
+ // expect(arePseudoSelectorsEquivalent(a, b)).toBe(true);
1070
+ // });
1071
+
1072
+ // it('rejects when one has arg and other does not', () => {
1073
+ // const a = pseudo({ name: ':where', arg: el('.x') });
1074
+ // const b = pseudo({ name: ':where' });
1075
+ // expect(arePseudoSelectorsEquivalent(a, b)).toBe(false);
1076
+ // });
1077
+
1078
+ // it('matches :not() with equivalent compound args in any order', () => {
1079
+ // const a = pseudo({ name: ':not', arg: compound([el('.x'), el('.y')]) });
1080
+ // const b = pseudo({ name: ':not', arg: compound([el('.y'), el('.x')]) });
1081
+ // expect(arePseudoSelectorsEquivalent(a, b)).toBe(true);
1082
+ // });
1083
+ // });
1084
+
1085
+ // // ─────────────────────────────────────────────────
1086
+ // // areSelectorArgumentsEquivalent
1087
+ // // ─────────────────────────────────────────────────
1088
+ // describe('areSelectorArgumentsEquivalent', () => {
1089
+ // it('matches identical simple selectors', () => {
1090
+ // expect(areSelectorArgumentsEquivalent(el('.a'), el('.a'))).toBe(true);
1091
+ // });
1092
+
1093
+ // it('matches selector lists in any order', () => {
1094
+ // const a = sellist([el('.x'), el('.y')]) as unknown as Selector;
1095
+ // const b = sellist([el('.y'), el('.x')]) as unknown as Selector;
1096
+ // expect(areSelectorArgumentsEquivalent(a, b)).toBe(true);
1097
+ // });
1098
+
1099
+ // it('rejects selector lists of different length', () => {
1100
+ // const a = sellist([el('.x'), el('.y')]) as unknown as Selector;
1101
+ // const b = sellist([el('.x')]) as unknown as Selector;
1102
+ // expect(areSelectorArgumentsEquivalent(a, b)).toBe(false);
1103
+ // });
1104
+
1105
+ // it('matches compound selectors', () => {
1106
+ // const a = compound([el('.a'), el('.b')]);
1107
+ // const b = compound([el('.b'), el('.a')]);
1108
+ // expect(areSelectorArgumentsEquivalent(a, b)).toBe(true);
1109
+ // });
1110
+ // });
1111
+
1112
+ // // ─────────────────────────────────────────────────
1113
+ // // areCompoundSelectorsEquivalent
1114
+ // // ─────────────────────────────────────────────────
1115
+ // describe('areCompoundSelectorsEquivalent', () => {
1116
+ // it('matches identical compounds', () => {
1117
+ // const a = compound([el('.a'), el('.b')]);
1118
+ // const b = compound([el('.a'), el('.b')]);
1119
+ // expect(areCompoundSelectorsEquivalent(a, b)).toBe(true);
1120
+ // });
1121
+
1122
+ // it('matches compounds in different order', () => {
1123
+ // const a = compound([el('.a'), el('.b')]);
1124
+ // const b = compound([el('.b'), el('.a')]);
1125
+ // expect(areCompoundSelectorsEquivalent(a, b)).toBe(true);
1126
+ // });
1127
+
1128
+ // it('rejects compounds of different length', () => {
1129
+ // const a = compound([el('.a'), el('.b')]);
1130
+ // const b = compound([el('.a')]);
1131
+ // expect(areCompoundSelectorsEquivalent(a, b)).toBe(false);
1132
+ // });
1133
+
1134
+ // it('matches compounds with :is() components via compoundComponentMatches', () => {
1135
+ // // :is(.a).b should match .a.b
1136
+ // const a = compound([is(sellist([el('.a')])), el('.b')]);
1137
+ // const b = compound([el('.a'), el('.b')]);
1138
+ // expect(areCompoundSelectorsEquivalent(a, b)).toBe(true);
1139
+ // });
1140
+ // });
1141
+
1142
+ // // ─────────────────────────────────────────────────
1143
+ // // expandCompoundWithPseudoSelectors
1144
+ // // ─────────────────────────────────────────────────
1145
+ // describe('expandCompoundWithPseudoSelectors', () => {
1146
+ // it('returns original for compound without :is()', () => {
1147
+ // const c = compound([el('.a'), el('.b')]);
1148
+ // const result = expandCompoundWithPseudoSelectors(c);
1149
+ // expect(result).toHaveLength(1);
1150
+ // });
1151
+
1152
+ // it('expands compound with one :is(2 alts) into 3 compounds', () => {
1153
+ // // .a:is(.x,.y) → [.a:is(.x,.y), .a.x, .a.y]
1154
+ // const c = compound([el('.a'), is(sellist([el('.x'), el('.y')]))]);
1155
+ // const result = expandCompoundWithPseudoSelectors(c);
1156
+ // // Original + 2 expanded = at least 3
1157
+ // expect(result.length).toBeGreaterThanOrEqual(3);
1158
+ // });
1159
+
1160
+ // it('expansion is combinatorial for multiple :is()', () => {
1161
+ // // .a:is(.x,.y):is(.p,.q) → (1+2)*(1+2) = 9
1162
+ // const c = compound([
1163
+ // el('.a'),
1164
+ // is(sellist([el('.x'), el('.y')])),
1165
+ // is(sellist([el('.p'), el('.q')]))
1166
+ // ]);
1167
+ // const result = expandCompoundWithPseudoSelectors(c);
1168
+ // expect(result.length).toBe(9);
1169
+ // });
1170
+ // });
1171
+
1172
+ // // ─────────────────────────────────────────────────
1173
+ // // expandComplexSelectorWithIs
1174
+ // // ─────────────────────────────────────────────────
1175
+ // describe('expandComplexSelectorWithIs', () => {
1176
+ // it('returns original for complex without :is()', () => {
1177
+ // const s = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
1178
+ // const result = expandComplexSelectorWithIs(s);
1179
+ // expect(result).toHaveLength(1);
1180
+ // });
1181
+
1182
+ // it('expands :is(.x,.y) .a into [.x .a, .y .a]', () => {
1183
+ // const s = sel([is(sellist([el('.x'), el('.y')])), co(' '), el('.a')]) as ComplexSelector;
1184
+ // const result = expandComplexSelectorWithIs(s);
1185
+ // expect(result).toHaveLength(2);
1186
+ // const values = result.map(r => r.valueOf());
1187
+ // expect(values).toContain('.x .a');
1188
+ // expect(values).toContain('.y .a');
1189
+ // });
1190
+ // });
1191
+
1192
+ // // ─────────────────────────────────────────────────
1193
+ // // expandSelectorWithIs
1194
+ // // ─────────────────────────────────────────────────
1195
+ // describe('expandSelectorWithIs', () => {
1196
+ // it('passes through simple selectors unchanged', () => {
1197
+ // const s = el('.a');
1198
+ // expect(expandSelectorWithIs(s)).toHaveLength(1);
1199
+ // });
1200
+
1201
+ // it('delegates to expandComplexSelectorWithIs for complex selectors', () => {
1202
+ // const s = sel([is(sellist([el('.x'), el('.y')])), co(' '), el('.a')]) as ComplexSelector;
1203
+ // const result = expandSelectorWithIs(s as any);
1204
+ // expect(result).toHaveLength(2);
1205
+ // });
1206
+
1207
+ // it('delegates to expandCompoundWithPseudoSelectors for compound selectors', () => {
1208
+ // const c = compound([el('.a'), is(sellist([el('.x'), el('.y')]))]);
1209
+ // const result = expandSelectorWithIs(c);
1210
+ // expect(result.length).toBeGreaterThanOrEqual(3);
1211
+ // });
1212
+ // });
1213
+
1214
+ // // ─────────────────────────────────────────────────
1215
+ // // areComplexSelectorsEquivalent
1216
+ // // ─────────────────────────────────────────────────
1217
+ // describe('areComplexSelectorsEquivalent', () => {
1218
+ // it('matches identical complex selectors', () => {
1219
+ // const a = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
1220
+ // const b = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
1221
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
1222
+ // });
1223
+
1224
+ // it('rejects complex selectors of different length', () => {
1225
+ // const a = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
1226
+ // const b = sel([el('.a')]) as ComplexSelector;
1227
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(false);
1228
+ // });
1229
+
1230
+ // it('rejects complex selectors with different combinators', () => {
1231
+ // const a = sel([el('.a'), co('>'), el('.b')]) as ComplexSelector;
1232
+ // const b = sel([el('.a'), co('+'), el('.b')]) as ComplexSelector;
1233
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(false);
1234
+ // });
1235
+
1236
+ // it('matches complex selectors with compound components in any order', () => {
1237
+ // const a = sel([compound([el('.x'), el('.y')]), co('>'), el('.b')]) as ComplexSelector;
1238
+ // const b = sel([compound([el('.y'), el('.x')]), co('>'), el('.b')]) as ComplexSelector;
1239
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
1240
+ // });
1241
+
1242
+ // it('matches when one component is :is(.a) and other is .a', () => {
1243
+ // const a = sel([is(el('.a')), co(' '), el('.b')]) as ComplexSelector;
1244
+ // const b = sel([el('.a'), co(' '), el('.b')]) as ComplexSelector;
1245
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
1246
+ // });
1247
+
1248
+ // it('matches :is(.a,.b) against .a (picks one alternative)', () => {
1249
+ // const a = sel([is(sellist([el('.a'), el('.b')])), co(' '), el('.c')]) as ComplexSelector;
1250
+ // const b = sel([el('.a'), co(' '), el('.c')]) as ComplexSelector;
1251
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(true);
1252
+ // });
1253
+
1254
+ // it('rejects :is(.a,.b) against .c (no alternative matches)', () => {
1255
+ // const a = sel([is(sellist([el('.a'), el('.b')])), co(' '), el('.c')]) as ComplexSelector;
1256
+ // const b = sel([el('.c'), co(' '), el('.c')]) as ComplexSelector;
1257
+ // expect(areComplexSelectorsEquivalent(a, b)).toBe(false);
1258
+ // });
1259
+ // });
1260
+
1261
+ // // ─────────────────────────────────────────────────
1262
+ // // isStructurallyEqual
1263
+ // // ─────────────────────────────────────────────────
1264
+ // describe('isStructurallyEqual', () => {
1265
+ // it('matches identical simple selectors', () => {
1266
+ // expect(isStructurallyEqual(el('.a'), el('.a'))).toBe(true);
1267
+ // });
1268
+
1269
+ // it('rejects different simple selectors', () => {
1270
+ // expect(isStructurallyEqual(el('.a'), el('.b'))).toBe(false);
1271
+ // });
1272
+
1273
+ // it('matches equivalent pseudo-selectors', () => {
1274
+ // const a = pseudo({ name: ':hover' });
1275
+ // const b = pseudo({ name: ':hover' });
1276
+ // expect(isStructurallyEqual(a, b)).toBe(true);
1277
+ // });
1278
+
1279
+ // it('rejects pseudo-selectors with different names', () => {
1280
+ // const a = pseudo({ name: ':hover' });
1281
+ // const b = pseudo({ name: ':focus' });
1282
+ // expect(isStructurallyEqual(a, b)).toBe(false);
1283
+ // });
1284
+
1285
+ // it('matches complex selectors component-by-component', () => {
1286
+ // const a = sel([el('.a'), co('>'), el('.b')]);
1287
+ // const b = sel([el('.a'), co('>'), el('.b')]);
1288
+ // expect(isStructurallyEqual(a as any, b as any)).toBe(true);
1289
+ // });
1290
+
1291
+ // it('rejects complex selectors with different components', () => {
1292
+ // const a = sel([el('.a'), co('>'), el('.b')]);
1293
+ // const b = sel([el('.a'), co('>'), el('.c')]);
1294
+ // expect(isStructurallyEqual(a as any, b as any)).toBe(false);
1295
+ // });
1296
+
1297
+ // it('matches compound selectors with same components in same order', () => {
1298
+ // const a = compound([el('.x'), el('.y')]);
1299
+ // const b = compound([el('.x'), el('.y')]);
1300
+ // expect(isStructurallyEqual(a, b)).toBe(true);
1301
+ // });
1302
+ // });
1303
+
1304
+ // // ─────────────────────────────────────────────────
1305
+ // // selectorMatchesExtendTarget
1306
+ // // ─────────────────────────────────────────────────
1307
+ // describe('selectorMatchesExtendTarget', () => {
1308
+ // it('matches identical simple selectors (non-partial)', () => {
1309
+ // expect(selectorMatchesExtendTarget(el('.a'), el('.a'), false)).toBe(true);
1310
+ // });
1311
+
1312
+ // it('rejects different simple selectors (non-partial)', () => {
1313
+ // expect(selectorMatchesExtendTarget(el('.a'), el('.b'), false)).toBe(false);
1314
+ // });
1315
+
1316
+ // it('matches when target is inside a SelectorList (non-partial)', () => {
1317
+ // const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
1318
+ // expect(selectorMatchesExtendTarget(target, el('.a'), false)).toBe(true);
1319
+ // });
1320
+
1321
+ // it('matches complex selectors (non-partial)', () => {
1322
+ // const a = sel([el('.a'), co('>'), el('.b')]);
1323
+ // const b = sel([el('.a'), co('>'), el('.b')]);
1324
+ // expect(selectorMatchesExtendTarget(a as any, b as any, false)).toBe(true);
1325
+ // });
1326
+
1327
+ // it('rejects when find is not in target (non-partial)', () => {
1328
+ // const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
1329
+ // expect(selectorMatchesExtendTarget(target, el('.c'), false)).toBe(false);
1330
+ // });
1331
+ // });
1332
+
1333
+ // // ─────────────────────────────────────────────────
1334
+ // // normalizeSelectorForExtend
1335
+ // // ─────────────────────────────────────────────────
1336
+ // describe('normalizeSelectorForExtend', () => {
1337
+ // it('passes through simple selectors unchanged', () => {
1338
+ // const s = el('.a');
1339
+ // const result = normalizeSelectorForExtend(s);
1340
+ // expect(result.valueOf()).toBe('.a');
1341
+ // });
1342
+
1343
+ // it('normalizes selector for extend matching', () => {
1344
+ // // Basic sanity — should not crash and should return a selector
1345
+ // const s = sel([el('.a'), co(' '), el('.b')]);
1346
+ // const result = normalizeSelectorForExtend(s as any);
1347
+ // expect(result).toBeTruthy();
1348
+ // expect(typeof result.valueOf()).toBe('string');
1349
+ // });
1350
+ // });
1351
+
1352
+ // // ─────────────────────────────────────────────────
1353
+ // // selectorCompare — additional edge cases
1354
+ // // ─────────────────────────────────────────────────
1355
+ // describe('selectorCompare edge cases', () => {
1356
+ // it('reports whole match for identical simple selectors', () => {
1357
+ // const result = selectorCompare(el('.a'), el('.a'));
1358
+ // expect(result.isEquivalent).toBe(true);
1359
+ // expect(result.hasWholeMatch).toBe(true);
1360
+ // });
1361
+
1362
+ // it('reports no match for different simple selectors', () => {
1363
+ // const result = selectorCompare(el('.a'), el('.b'));
1364
+ // expect(result.isEquivalent).toBe(false);
1365
+ // expect(result.hasWholeMatch).toBe(false);
1366
+ // });
1367
+
1368
+ // it('reports match for SelectorList containing the find', () => {
1369
+ // const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
1370
+ // const result = selectorCompare(target, el('.a'));
1371
+ // // selectorCompare uses findExtendableLocations which detects presence
1372
+ // expect(result.hasWholeMatch || result.hasPartialMatch).toBe(true);
1373
+ // });
1374
+
1375
+ // it('uses Set-based O(N) comparison for two SelectorLists', () => {
1376
+ // const a = sellist([el('.x'), el('.y'), el('.z')]) as unknown as Selector;
1377
+ // const b = sellist([el('.z'), el('.x'), el('.y')]) as unknown as Selector;
1378
+ // const result = selectorCompare(a, b);
1379
+ // expect(result.isEquivalent).toBe(true);
1380
+ // });
1381
+
1382
+ // it('rejects SelectorLists with different items', () => {
1383
+ // const a = sellist([el('.x'), el('.y')]) as unknown as Selector;
1384
+ // const b = sellist([el('.x'), el('.z')]) as unknown as Selector;
1385
+ // const result = selectorCompare(a, b);
1386
+ // expect(result.isEquivalent).toBe(false);
1387
+ // });
1388
+ // });
1389
+
1390
+ // // ─────────────────────────────────────────────────
1391
+ // // findExtendableLocations — additional unit tests
1392
+ // // ─────────────────────────────────────────────────
1393
+ // describe('findExtendableLocations unit tests', () => {
1394
+ // it('finds simple selector in SelectorList', () => {
1395
+ // const target = sellist([el('.a'), el('.b'), el('.c')]) as unknown as Selector;
1396
+ // const result = findExtendableLocations(target, el('.b'));
1397
+ // expect(result.hasMatches).toBe(true);
1398
+ // });
1399
+
1400
+ // it('returns no match when target does not contain find', () => {
1401
+ // const target = sellist([el('.a'), el('.b')]) as unknown as Selector;
1402
+ // const result = findExtendableLocations(target, el('.z'));
1403
+ // expect(result.hasMatches).toBe(false);
1404
+ // });
1405
+
1406
+ // it('finds selector inside :is() argument', () => {
1407
+ // const target = is(sellist([el('.a'), el('.b')]));
1408
+ // const result = findExtendableLocations(target, el('.a'));
1409
+ // expect(result.hasMatches).toBe(true);
1410
+ // });
1411
+
1412
+ // it('finds compound subsequence as partial match', () => {
1413
+ // // .a.b.c target, .a.c find → partial match
1414
+ // const target = compound([el('.a'), el('.b'), el('.c')]);
1415
+ // const result = findExtendableLocations(target, compound([el('.a'), el('.c')]));
1416
+ // expect(result.hasMatches).toBe(true);
1417
+ // });
1418
+
1419
+ // it('caches results for identical selector+find pairs', () => {
1420
+ // const target = el('.cached');
1421
+ // const find = el('.cached');
1422
+ // const r1 = findExtendableLocations(target, find);
1423
+ // const r2 = findExtendableLocations(target, find);
1424
+ // // Should return the same cached object
1425
+ // expect(r1).toBe(r2);
1426
+ // });
1427
+ // });