@jesscss/core 2.0.0-alpha.4 → 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 -94
  258. package/lib/tree/ampersand.d.ts.map +0 -1
  259. package/lib/tree/ampersand.js +0 -269
  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,2005 @@
1
+ import { Selector } from '../selector.js';
2
+ import { SelectorList } from '../selector-list.js';
3
+ import { F_AMPERSAND, type Node } from '../node.js';
4
+ import type { Context as EvalContext } from '../../context.js';
5
+ import { isNode } from './is-node.js';
6
+ import { N } from '../node-type.js';
7
+ import { isDisjoint, type BitSet } from './bitset.js';
8
+ import { type PseudoSelector } from '../selector-pseudo.js';
9
+ import { type CompoundSelector } from '../selector-compound.js';
10
+ import { type ComplexSelector } from '../selector-complex.js';
11
+
12
+ /**
13
+ * A single located selector match.
14
+ *
15
+ * `startIndex` / `endIndex` are measured in the `containingNode`'s local
16
+ * ordered data when the containing node is ordered.
17
+ *
18
+ * `exact` means the matched route consumed its span end-to-end without extra
19
+ * unmatched basic selectors on that route.
20
+ *
21
+ * `crossesAmpersand` is set on synthetic cross-boundary matches completed
22
+ * through a parent selector context.
23
+ *
24
+ * `consumedTarget` means this location matched everything it could possibly
25
+ * match in its target container. This is distinct from route-level
26
+ * `fullMatch`, which only means one exact match route succeeded.
27
+ */
28
+ interface SelectorMatchLocation {
29
+ startIndex?: number;
30
+ endIndex?: number;
31
+ matchedIndices?: number[];
32
+ containingNode: Node;
33
+ exact?: boolean;
34
+ crossesAmpersand?: boolean;
35
+ consumedTarget?: boolean;
36
+ ampersandCrossings?: SelectorMatchAmpersandCrossing[];
37
+ }
38
+
39
+ interface SelectorMatchSegment {
40
+ containingNode: Node;
41
+ startIndex?: number;
42
+ endIndex?: number;
43
+ matchedIndices?: number[];
44
+ }
45
+
46
+ interface SelectorMatchAmpersandCrossing {
47
+ ampersandNode?: Node;
48
+ targetSegment: SelectorMatchSegment;
49
+ parentSegment?: SelectorMatchSegment;
50
+ }
51
+
52
+ /**
53
+ * Aggregate selector-match result.
54
+ *
55
+ * `fullMatch` means at least one exact end-to-end route matched.
56
+ * `partialMatch` means at least one match of any kind was found.
57
+ * `crossesAmpersand` means at least one recorded match crossed an ampersand
58
+ * boundary instead of matching entirely on one side of it.
59
+ */
60
+ interface SelectorMatchState {
61
+ fullMatch: boolean;
62
+ partialMatch: boolean;
63
+ crossesAmpersand: boolean;
64
+ matches: SelectorMatchLocation[];
65
+ }
66
+
67
+ /** The set of basic selectors that one unordered position can satisfy. */
68
+ type MatchGroupRequirement = {
69
+ basicSelectorIndex: Map<string, number>;
70
+ basicSelectorCounts: number[];
71
+ basicSelectorTotal: number;
72
+ hasComplexBranch: boolean;
73
+ branchTailAmbiguous: boolean;
74
+ };
75
+
76
+ /** One position can satisfy any one of its alternatives, such as `:is(...)`. */
77
+ type MatchGroup = {
78
+ alternatives: MatchGroupRequirement[];
79
+ };
80
+
81
+ /** One ordered unit in a route-level match plan. */
82
+ type MatchPlanUnit =
83
+ | {
84
+ kind: 'group';
85
+ index: number;
86
+ node: Node;
87
+ group: MatchGroup;
88
+ }
89
+ | {
90
+ kind: 'combinator';
91
+ index: number;
92
+ node: Node;
93
+ value: string;
94
+ };
95
+
96
+ type RouteMatchPlan = {
97
+ kind: 'route';
98
+ selector: Selector;
99
+ units: MatchPlanUnit[];
100
+ hasAmbiguousBranchTail: boolean;
101
+ };
102
+
103
+ type SelectorListMatchPlan = {
104
+ kind: 'list';
105
+ selector: SelectorList;
106
+ alternates: MatchPlan[];
107
+ };
108
+
109
+ type MatchPlan = RouteMatchPlan | SelectorListMatchPlan;
110
+
111
+ type MatchGroupState = {
112
+ remainingCounts: number[];
113
+ remainingTotal: number;
114
+ exact: boolean;
115
+ matchedOutsideAmpersand: boolean;
116
+ };
117
+
118
+ type MatchWindowResult = {
119
+ matched: boolean;
120
+ exact: boolean;
121
+ };
122
+
123
+ type GroupMatchCache = WeakMap<Node, WeakMap<MatchGroup, MatchWindowResult>>;
124
+ type SelectorMatchPairCache = WeakMap<Selector, WeakMap<Selector, SelectorMatchState>>;
125
+ type SelectorMatchContext = {
126
+ pairCache: SelectorMatchPairCache;
127
+ evalContext?: EvalContext;
128
+ };
129
+
130
+ const selectorMatchPlanCache = new WeakMap<Selector, {
131
+ value: string;
132
+ plan: MatchPlan;
133
+ }>();
134
+
135
+ function selectorValueOf(node: Node | Selector, context?: EvalContext): string {
136
+ return String((node as unknown as { valueOf(context?: EvalContext): string }).valueOf(context));
137
+ }
138
+
139
+ function bitSetValues(bitSet: BitSet<string> | undefined): string[] | undefined {
140
+ return bitSet?._library?.valuesOf(bitSet);
141
+ }
142
+
143
+ function safeIsDisjoint(
144
+ left: BitSet<string> | undefined,
145
+ right: BitSet<string> | undefined
146
+ ): boolean {
147
+ if (!left || !right) {
148
+ return false;
149
+ }
150
+ if (left._library && right._library && left._library === right._library) {
151
+ return isDisjoint(left, right);
152
+ }
153
+ const leftValues = bitSetValues(left);
154
+ const rightValues = bitSetValues(right);
155
+ if (!leftValues || !rightValues) {
156
+ return false;
157
+ }
158
+ const rightSet = new Set(rightValues);
159
+ for (const value of leftValues) {
160
+ if (rightSet.has(value)) {
161
+ return false;
162
+ }
163
+ }
164
+ return true;
165
+ }
166
+
167
+ /**
168
+ * Returns true for pseudos whose selector arguments can be searched recursively
169
+ * but cannot be consumed as part of a continuing outer match, unlike `:is()`.
170
+ */
171
+ function isSearchablePseudoBoundary(node: Node): node is PseudoSelector {
172
+ return (
173
+ isNode(node, N.PseudoSelector)
174
+ && node.get('name') !== ':is'
175
+ && isNode(node.get('arg'), N.Selector)
176
+ );
177
+ }
178
+
179
+ function createRequirement(): MatchGroupRequirement {
180
+ const basicSelectorIndex = new Map<string, number>();
181
+ const basicSelectorCounts: number[] = [];
182
+
183
+ return {
184
+ basicSelectorIndex,
185
+ basicSelectorCounts,
186
+ basicSelectorTotal: 0,
187
+ hasComplexBranch: false,
188
+ branchTailAmbiguous: false
189
+ };
190
+ }
191
+
192
+ function addRequirementValue(requirement: MatchGroupRequirement, value: string): MatchGroupRequirement {
193
+ const existingIndex = requirement.basicSelectorIndex.get(value);
194
+ if (existingIndex !== undefined) {
195
+ requirement.basicSelectorCounts[existingIndex]!++;
196
+ requirement.basicSelectorTotal++;
197
+ return requirement;
198
+ }
199
+
200
+ requirement.basicSelectorIndex.set(value, requirement.basicSelectorCounts.length);
201
+ requirement.basicSelectorCounts.push(1);
202
+ requirement.basicSelectorTotal++;
203
+ return requirement;
204
+ }
205
+
206
+ function cloneRequirement(requirement: MatchGroupRequirement): MatchGroupRequirement {
207
+ return {
208
+ basicSelectorIndex: new Map(requirement.basicSelectorIndex),
209
+ basicSelectorCounts: [...requirement.basicSelectorCounts],
210
+ basicSelectorTotal: requirement.basicSelectorTotal,
211
+ hasComplexBranch: requirement.hasComplexBranch,
212
+ branchTailAmbiguous: requirement.branchTailAmbiguous
213
+ };
214
+ }
215
+
216
+ function mergeRequirements(
217
+ left: MatchGroupRequirement,
218
+ right: MatchGroupRequirement
219
+ ): MatchGroupRequirement {
220
+ const merged = cloneRequirement(left);
221
+ for (const [value, index] of right.basicSelectorIndex.entries()) {
222
+ const count = right.basicSelectorCounts[index] ?? 0;
223
+ for (let i = 0; i < count; i++) {
224
+ addRequirementValue(merged, value);
225
+ }
226
+ }
227
+ merged.hasComplexBranch ||= right.hasComplexBranch;
228
+ merged.branchTailAmbiguous ||= right.branchTailAmbiguous;
229
+ return merged;
230
+ }
231
+
232
+ function markComplexBranchRequirements(
233
+ requirements: MatchGroupRequirement[],
234
+ branch: Selector & { value?: readonly Node[] },
235
+ parent?: Selector,
236
+ context?: EvalContext
237
+ ): MatchGroupRequirement[] {
238
+ const earlierValues = new Set<string>();
239
+
240
+ if (isNode(branch, N.ComplexSelector)) {
241
+ for (let i = 0; i < (branch as ComplexSelector).get('value').length - 1; i++) {
242
+ const component = (branch as ComplexSelector).get('value')[i]!;
243
+ const nested = buildGroupRequirements(component, parent, context);
244
+ for (let j = 0; j < nested.length; j++) {
245
+ for (const value of nested[j]!.basicSelectorIndex.keys()) {
246
+ earlierValues.add(value);
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ for (let i = 0; i < requirements.length; i++) {
253
+ const requirement = requirements[i]!;
254
+ requirement.hasComplexBranch = true;
255
+ for (const value of requirement.basicSelectorIndex.keys()) {
256
+ if (earlierValues.has(value)) {
257
+ requirement.branchTailAmbiguous = true;
258
+ break;
259
+ }
260
+ }
261
+ }
262
+
263
+ return requirements;
264
+ }
265
+
266
+ function buildGroupRequirements(
267
+ node: Node,
268
+ parent?: Selector,
269
+ context?: EvalContext,
270
+ activePath: Set<Node> = new Set()
271
+ ): MatchGroupRequirement[] {
272
+ if (activePath.has(node)) {
273
+ return [createRequirement()];
274
+ }
275
+ activePath.add(node);
276
+
277
+ if (isNode(node, N.BasicSelector)) {
278
+ const requirement = createRequirement();
279
+ addRequirementValue(requirement, selectorValueOf(node, context));
280
+ activePath.delete(node);
281
+ return [requirement];
282
+ }
283
+
284
+ if (isNode(node, N.Ampersand)) {
285
+ const resolved = node.getResolvedSelector(context) ?? parent;
286
+ if (resolved && !isNode(resolved, N.Nil)) {
287
+ if (activePath.has(resolved)) {
288
+ activePath.delete(node);
289
+ return [createRequirement()];
290
+ }
291
+ const result = buildGroupRequirements(resolved, parent, context, activePath);
292
+ activePath.delete(node);
293
+ return result;
294
+ }
295
+
296
+ activePath.delete(node);
297
+ return [createRequirement()];
298
+ }
299
+
300
+ if (isNode(node, N.ComplexSelector)) {
301
+ for (let i = (node as ComplexSelector).get('value').length - 1; i >= 0; i--) {
302
+ const component = (node as ComplexSelector).get('value')[i]!;
303
+ if (!isNode(component, N.Combinator)) {
304
+ return markComplexBranchRequirements(
305
+ buildGroupRequirements(component, parent, context, activePath),
306
+ node as unknown as Selector & { value?: readonly Node[] },
307
+ parent,
308
+ context
309
+ );
310
+ }
311
+ }
312
+
313
+ activePath.delete(node);
314
+ return [createRequirement()];
315
+ }
316
+
317
+ if (isNode(node, N.PseudoSelector) && node.get('name') !== ':is') {
318
+ const requirement = createRequirement();
319
+ addRequirementValue(requirement, selectorValueOf(node, context));
320
+ activePath.delete(node);
321
+ return [requirement];
322
+ }
323
+
324
+ if (isSearchablePseudoBoundary(node)) {
325
+ activePath.delete(node);
326
+ return [createRequirement()];
327
+ }
328
+
329
+ if (isNode(node, N.SelectorList)) {
330
+ const alternatives: MatchGroupRequirement[] = [];
331
+ for (let i = 0; i < (node as SelectorList).get('value').length; i++) {
332
+ const nested = buildGroupRequirements((node as SelectorList).get('value')[i]!, parent, context, activePath);
333
+ for (let j = 0; j < nested.length; j++) {
334
+ alternatives.push(nested[j]!);
335
+ }
336
+ }
337
+ activePath.delete(node);
338
+ return alternatives;
339
+ }
340
+
341
+ let requirements: MatchGroupRequirement[] = [createRequirement()];
342
+ const children = node.children();
343
+ let child = children.next();
344
+
345
+ while (!child.done) {
346
+ const childRequirements = buildGroupRequirements(child.value, parent, context, activePath);
347
+ const nextRequirements: MatchGroupRequirement[] = [];
348
+ for (let i = 0; i < requirements.length; i++) {
349
+ for (let j = 0; j < childRequirements.length; j++) {
350
+ nextRequirements.push(
351
+ mergeRequirements(requirements[i]!, childRequirements[j]!)
352
+ );
353
+ }
354
+ }
355
+ requirements = nextRequirements;
356
+ child = children.next();
357
+ }
358
+
359
+ activePath.delete(node);
360
+ return requirements;
361
+ }
362
+
363
+ function buildMatchGroup(node: Node, parent?: Selector, context?: EvalContext): MatchGroup {
364
+ return {
365
+ alternatives: buildGroupRequirements(node, parent, context)
366
+ };
367
+ }
368
+
369
+ function buildRouteMatchPlan(selector: Selector, parent?: Selector, context?: EvalContext): RouteMatchPlan {
370
+ if (isNode(selector, N.ComplexSelector)) {
371
+ const units: MatchPlanUnit[] = [];
372
+ let hasAmbiguousBranchTail = false;
373
+
374
+ for (let i = 0; i < (selector as ComplexSelector).get('value').length; i++) {
375
+ const component = (selector as ComplexSelector).get('value')[i]!;
376
+ if (isNode(component, N.Combinator)) {
377
+ units.push({
378
+ kind: 'combinator',
379
+ index: i,
380
+ node: component,
381
+ value: selectorValueOf(component, context)
382
+ });
383
+ continue;
384
+ }
385
+
386
+ const group = buildMatchGroup(
387
+ component,
388
+ isNode(component, N.Ampersand) && i === 0 ? undefined : parent,
389
+ context
390
+ );
391
+ const hasAlternatives = group.alternatives.some(alternate => alternate.basicSelectorTotal > 0);
392
+ if (hasAlternatives) {
393
+ hasAmbiguousBranchTail ||= group.alternatives.some(alternate => alternate.branchTailAmbiguous);
394
+ units.push({
395
+ kind: 'group',
396
+ index: i,
397
+ node: component,
398
+ group
399
+ });
400
+ }
401
+ }
402
+
403
+ return {
404
+ kind: 'route',
405
+ selector,
406
+ units,
407
+ hasAmbiguousBranchTail
408
+ };
409
+ }
410
+
411
+ if (isNode(selector, N.Combinator)) {
412
+ return {
413
+ kind: 'route',
414
+ selector,
415
+ hasAmbiguousBranchTail: false,
416
+ units: [{
417
+ kind: 'combinator',
418
+ index: 0,
419
+ node: selector,
420
+ value: selectorValueOf(selector, context)
421
+ }]
422
+ };
423
+ }
424
+
425
+ const group = buildMatchGroup(selector, parent, context);
426
+ return {
427
+ kind: 'route',
428
+ selector,
429
+ hasAmbiguousBranchTail: group.alternatives.some(alternate => alternate.branchTailAmbiguous),
430
+ units: group.alternatives.some(alternate => alternate.basicSelectorTotal > 0)
431
+ ? [{
432
+ kind: 'group',
433
+ index: 0,
434
+ node: selector,
435
+ group
436
+ }]
437
+ : []
438
+ };
439
+ }
440
+
441
+ function buildMatchPlan(selector: Selector, parent?: Selector, context?: EvalContext): MatchPlan {
442
+ if (isNode(selector, N.SelectorList)) {
443
+ return {
444
+ kind: 'list',
445
+ selector,
446
+ alternates: (selector as SelectorList).get('value').map(item => getSelectorMatchPlan(item, parent, context))
447
+ };
448
+ }
449
+
450
+ return buildRouteMatchPlan(selector, parent, context);
451
+ }
452
+
453
+ function getSelectorMatchPlan(selector: Selector, parent?: Selector, context?: EvalContext): MatchPlan {
454
+ if (parent || context) {
455
+ return buildMatchPlan(selector, parent, context);
456
+ }
457
+
458
+ const value = selectorValueOf(selector);
459
+ const cached = selectorMatchPlanCache.get(selector);
460
+ if (cached && cached.value === value) {
461
+ return cached.plan;
462
+ }
463
+
464
+ const plan = buildMatchPlan(selector);
465
+ selectorMatchPlanCache.set(selector, { value, plan });
466
+ return plan;
467
+ }
468
+
469
+ function cloneGroupStates(states: MatchGroupState[]): MatchGroupState[] {
470
+ const nextStates = new Array<MatchGroupState>(states.length);
471
+ for (let i = 0; i < states.length; i++) {
472
+ const state = states[i]!;
473
+ nextStates[i] = {
474
+ remainingCounts: [...state.remainingCounts],
475
+ remainingTotal: state.remainingTotal,
476
+ exact: state.exact,
477
+ matchedOutsideAmpersand: state.matchedOutsideAmpersand
478
+ };
479
+ }
480
+ return nextStates;
481
+ }
482
+
483
+ function allStatesAreTerminalPartial(states: MatchGroupState[]): boolean {
484
+ for (let i = 0; i < states.length; i++) {
485
+ const state = states[i]!;
486
+ if (state.remainingTotal > 0 || state.exact) {
487
+ return false;
488
+ }
489
+ }
490
+ return true;
491
+ }
492
+
493
+ function consumeGroupBasics(
494
+ node: Node,
495
+ group: MatchGroupRequirement,
496
+ states: MatchGroupState[],
497
+ insideAmpersand = false,
498
+ parent?: Selector,
499
+ context?: EvalContext,
500
+ activePath: Set<Node> = new Set()
501
+ ): MatchGroupState[] {
502
+ if (states.length === 0) {
503
+ return states;
504
+ }
505
+
506
+ if (activePath.has(node)) {
507
+ return states;
508
+ }
509
+ activePath.add(node);
510
+
511
+ if (isNode(node, N.BasicSelector) || (isNode(node, N.PseudoSelector) && node.get('name') !== ':is')) {
512
+ const idx = group.basicSelectorIndex.get(selectorValueOf(node, context));
513
+
514
+ for (let i = 0; i < states.length; i++) {
515
+ const state = states[i]!;
516
+
517
+ if (state.remainingTotal === 0 && !state.exact) {
518
+ continue;
519
+ }
520
+
521
+ if (idx === undefined || state.remainingCounts[idx] === 0) {
522
+ state.exact = false;
523
+ continue;
524
+ }
525
+
526
+ state.remainingCounts[idx]!--;
527
+ state.remainingTotal--;
528
+ if (!insideAmpersand) {
529
+ state.matchedOutsideAmpersand = true;
530
+ }
531
+ }
532
+
533
+ activePath.delete(node);
534
+ return states;
535
+ }
536
+
537
+ if (isNode(node, N.Ampersand)) {
538
+ const resolved = node.getResolvedSelector(context) ?? parent;
539
+ if (resolved && !isNode(resolved, N.Nil)) {
540
+ if (activePath.has(resolved)) {
541
+ activePath.delete(node);
542
+ return states;
543
+ }
544
+ const result = consumeGroupBasics(resolved, group, states, true, parent, context, activePath);
545
+ activePath.delete(node);
546
+ return result;
547
+ }
548
+
549
+ activePath.delete(node);
550
+ return states;
551
+ }
552
+
553
+ if (isSearchablePseudoBoundary(node)) {
554
+ activePath.delete(node);
555
+ return states;
556
+ }
557
+
558
+ if (isNode(node, N.SelectorList)) {
559
+ const alternates = node.children();
560
+ const routes = [alternates.mark()];
561
+ const nextStates: MatchGroupState[] = [];
562
+
563
+ while (routes.length > 0) {
564
+ alternates.restore(routes.pop()!);
565
+ const alternate = alternates.next();
566
+
567
+ if (alternate.done) {
568
+ continue;
569
+ }
570
+
571
+ routes.push(alternates.mark());
572
+
573
+ nextStates.push(
574
+ ...consumeGroupBasics(
575
+ alternate.value,
576
+ group,
577
+ cloneGroupStates(states),
578
+ insideAmpersand,
579
+ parent,
580
+ context,
581
+ activePath
582
+ )
583
+ );
584
+ }
585
+
586
+ activePath.delete(node);
587
+ return nextStates;
588
+ }
589
+
590
+ const children = node.children();
591
+ let nextStates = states;
592
+ let child = children.next();
593
+
594
+ while (!child.done && nextStates.length > 0 && !allStatesAreTerminalPartial(nextStates)) {
595
+ nextStates = consumeGroupBasics(child.value, group, nextStates, insideAmpersand, parent, context, activePath);
596
+ child = children.next();
597
+ }
598
+
599
+ activePath.delete(node);
600
+ return nextStates;
601
+ }
602
+
603
+ /** Summarizes a set of consume-states with a single pass. */
604
+ function summarizeStates(states: MatchGroupState[]): MatchWindowResult {
605
+ let matched = false;
606
+ let exact = false;
607
+
608
+ for (let i = 0; i < states.length; i++) {
609
+ const state = states[i]!;
610
+ if (state.remainingTotal !== 0 || !state.matchedOutsideAmpersand) {
611
+ continue;
612
+ }
613
+
614
+ matched = true;
615
+ if (state.exact) {
616
+ exact = true;
617
+ break;
618
+ }
619
+ }
620
+
621
+ return { matched, exact };
622
+ }
623
+
624
+ /**
625
+ * Matches a single unordered target position against one match group.
626
+ *
627
+ * This is the core "consume basics within a position" operation used by
628
+ * route-level matching.
629
+ */
630
+ function matchTargetGroup(
631
+ targetGroup: Node,
632
+ findGroup: MatchGroup,
633
+ parent?: Selector,
634
+ allowAmpersandOnlyMatch = false,
635
+ context?: EvalContext
636
+ ): MatchWindowResult {
637
+ let matched = false;
638
+ let exact = false;
639
+
640
+ for (let i = 0; i < findGroup.alternatives.length; i++) {
641
+ const requirement = findGroup.alternatives[i]!;
642
+ const states = consumeGroupBasics(targetGroup, requirement, [{
643
+ remainingCounts: [...requirement.basicSelectorCounts],
644
+ remainingTotal: requirement.basicSelectorTotal,
645
+ exact: true,
646
+ matchedOutsideAmpersand: false
647
+ }], false, parent, context);
648
+ const summary = summarizeStates(states);
649
+ if (!summary.matched && allowAmpersandOnlyMatch) {
650
+ for (let j = 0; j < states.length; j++) {
651
+ const state = states[j]!;
652
+ if (state.remainingTotal !== 0) {
653
+ continue;
654
+ }
655
+
656
+ summary.matched = true;
657
+ if (state.exact) {
658
+ summary.exact = true;
659
+ break;
660
+ }
661
+ }
662
+ }
663
+
664
+ matched ||= summary.matched;
665
+ exact ||= summary.exact && !requirement.hasComplexBranch;
666
+
667
+ if (exact) {
668
+ break;
669
+ }
670
+ }
671
+
672
+ return { matched, exact };
673
+ }
674
+
675
+ function matchCompoundWindow(
676
+ targetCompound: Selector & { value: readonly Node[] },
677
+ start: number,
678
+ end: number,
679
+ requirement: MatchGroupRequirement,
680
+ parent?: Selector,
681
+ context?: EvalContext
682
+ ): MatchWindowResult {
683
+ let states = [{
684
+ remainingCounts: [...requirement.basicSelectorCounts],
685
+ remainingTotal: requirement.basicSelectorTotal,
686
+ exact: true,
687
+ matchedOutsideAmpersand: false
688
+ }];
689
+
690
+ for (let i = start; i <= end && states.length > 0 && !allStatesAreTerminalPartial(states); i++) {
691
+ states = consumeGroupBasics(targetCompound.value[i]!, requirement, states, false, parent, context);
692
+ }
693
+
694
+ const summary = summarizeStates(states);
695
+ if (!summary.matched && parent) {
696
+ let allAmpersands = true;
697
+ for (let i = start; i <= end; i++) {
698
+ if (!isNode(targetCompound.value[i]!, N.Ampersand)) {
699
+ allAmpersands = false;
700
+ break;
701
+ }
702
+ }
703
+
704
+ if (allAmpersands) {
705
+ for (let i = 0; i < states.length; i++) {
706
+ const state = states[i]!;
707
+ if (state.remainingTotal !== 0) {
708
+ continue;
709
+ }
710
+ summary.matched = true;
711
+ if (state.exact) {
712
+ summary.exact = true;
713
+ break;
714
+ }
715
+ }
716
+ }
717
+ }
718
+ if (requirement.hasComplexBranch) {
719
+ summary.exact = false;
720
+ }
721
+ return summary;
722
+ }
723
+
724
+ function collectMatchedIndicesForWindow(
725
+ targetCompound: Selector & { value: readonly Node[] },
726
+ start: number,
727
+ end: number,
728
+ requirement: MatchGroupRequirement,
729
+ context?: EvalContext
730
+ ): number[] | undefined {
731
+ const remainingCounts = [...requirement.basicSelectorCounts];
732
+ const matchedIndices: number[] = [];
733
+
734
+ for (let i = start; i <= end; i++) {
735
+ const node = targetCompound.value[i]!;
736
+ if (!isNode(node, N.BasicSelector) && !(isNode(node, N.PseudoSelector) && node.get('name') !== ':is')) {
737
+ continue;
738
+ }
739
+
740
+ const idx = requirement.basicSelectorIndex.get(selectorValueOf(node, context));
741
+ if (idx === undefined || remainingCounts[idx] === 0) {
742
+ continue;
743
+ }
744
+
745
+ remainingCounts[idx]!--;
746
+ matchedIndices.push(i);
747
+ }
748
+
749
+ if (matchedIndices.length === 0) {
750
+ return undefined;
751
+ }
752
+
753
+ const spanLength = end - start + 1;
754
+ if (matchedIndices.length === spanLength) {
755
+ return undefined;
756
+ }
757
+
758
+ return matchedIndices;
759
+ }
760
+
761
+ /**
762
+ * Collects every group-local span match for a target node.
763
+ *
764
+ * For compounds this scans contiguous windows so repeated matches in the same
765
+ * compound are reported independently.
766
+ *
767
+ * @todo This is still the hottest path in selector matching. It currently
768
+ * scans O(n^2) spans and may re-run `matchCompoundWindow()` for the same
769
+ * requirement to prove minimality (`withoutStartMatches` / `withoutEndMatches`).
770
+ * If this becomes a measurable bottleneck, replace the brute-force span scan
771
+ * with a requirement-aware sliding window or prefix-count index that can:
772
+ * 1. detect whether a span satisfies the requirement,
773
+ * 2. prove minimality without rescanning adjacent subspans, and
774
+ * 3. still preserve current semantics for extras-inside-span, matchedIndices,
775
+ * branch-tail ambiguity, and repeated independent matches in one compound.
776
+ */
777
+ function collectGroupMatchLocations(
778
+ targetGroup: Node,
779
+ findGroup: MatchGroup,
780
+ parent?: Selector,
781
+ context?: EvalContext
782
+ ): SelectorMatchLocation[] {
783
+ if (!isNode(targetGroup, N.CompoundSelector)) {
784
+ const groupMatch = matchTargetGroup(
785
+ targetGroup,
786
+ findGroup,
787
+ parent,
788
+ !!parent && isNode(targetGroup, N.Ampersand),
789
+ context
790
+ );
791
+ return groupMatch.matched
792
+ ? [{
793
+ startIndex: 0,
794
+ endIndex: 0,
795
+ containingNode: targetGroup,
796
+ exact: groupMatch.exact,
797
+ consumedTarget: groupMatch.exact
798
+ }]
799
+ : [];
800
+ }
801
+
802
+ const matches: SelectorMatchLocation[] = [];
803
+ const seen = new Set<number>();
804
+ const targetCompound = targetGroup as unknown as Selector & { value: readonly Node[] };
805
+ const targetLength = (targetGroup as CompoundSelector).get('value').length;
806
+
807
+ for (let i = 0; i < findGroup.alternatives.length; i++) {
808
+ const requirement = findGroup.alternatives[i]!;
809
+ for (let start = 0; start < targetLength; start++) {
810
+ for (let end = start; end < targetLength; end++) {
811
+ const windowMatch = matchCompoundWindow(
812
+ targetCompound,
813
+ start,
814
+ end,
815
+ requirement,
816
+ parent,
817
+ context
818
+ );
819
+
820
+ if (!windowMatch.matched) {
821
+ continue;
822
+ }
823
+
824
+ const withoutStartMatches = start < end
825
+ && matchCompoundWindow(targetCompound, start + 1, end, requirement, parent, context).matched;
826
+ if (withoutStartMatches) {
827
+ continue;
828
+ }
829
+
830
+ const withoutEndMatches = start < end
831
+ && matchCompoundWindow(targetCompound, start, end - 1, requirement, parent, context).matched;
832
+ if (withoutEndMatches) {
833
+ continue;
834
+ }
835
+
836
+ const key = start * targetLength + end;
837
+ if (seen.has(key)) {
838
+ continue;
839
+ }
840
+
841
+ seen.add(key);
842
+
843
+ matches.push({
844
+ startIndex: start,
845
+ endIndex: end,
846
+ matchedIndices: collectMatchedIndicesForWindow(targetCompound, start, end, requirement, context),
847
+ containingNode: targetGroup,
848
+ exact: windowMatch.exact && start === 0 && end === targetLength - 1,
849
+ consumedTarget: windowMatch.exact && start === 0 && end === targetLength - 1
850
+ });
851
+ }
852
+ }
853
+ }
854
+
855
+ matches.sort((left, right) => {
856
+ const leftStart = left.startIndex ?? 0;
857
+ const rightStart = right.startIndex ?? 0;
858
+ if (leftStart !== rightStart) {
859
+ return leftStart - rightStart;
860
+ }
861
+
862
+ const leftEnd = left.endIndex ?? leftStart;
863
+ const rightEnd = right.endIndex ?? rightStart;
864
+ return leftEnd - rightEnd;
865
+ });
866
+
867
+ const filtered: SelectorMatchLocation[] = [];
868
+ let lastEnd = -1;
869
+ for (let i = 0; i < matches.length; i++) {
870
+ const match = matches[i]!;
871
+ const start = match.startIndex ?? 0;
872
+ const end = match.endIndex ?? start;
873
+
874
+ if (start <= lastEnd) {
875
+ continue;
876
+ }
877
+
878
+ filtered.push(match);
879
+ lastEnd = end;
880
+ }
881
+
882
+ return filtered;
883
+ }
884
+
885
+ /** Fast constructor for the no-match result shape. */
886
+ function emptySelectorMatchState(): SelectorMatchState {
887
+ return {
888
+ fullMatch: false,
889
+ partialMatch: false,
890
+ crossesAmpersand: false,
891
+ matches: []
892
+ };
893
+ }
894
+
895
+ /** Appends one result list into another without allocating a combined array. */
896
+ function pushMatches(
897
+ target: SelectorMatchLocation[],
898
+ source: SelectorMatchLocation[]
899
+ ): void {
900
+ for (let i = 0; i < source.length; i++) {
901
+ target.push(source[i]!);
902
+ }
903
+ }
904
+
905
+ function cloneMatchSegment(
906
+ segment: SelectorMatchSegment | undefined
907
+ ): SelectorMatchSegment | undefined {
908
+ if (!segment) {
909
+ return undefined;
910
+ }
911
+
912
+ return {
913
+ containingNode: segment.containingNode,
914
+ startIndex: segment.startIndex,
915
+ endIndex: segment.endIndex,
916
+ matchedIndices: segment.matchedIndices ? [...segment.matchedIndices] : undefined
917
+ };
918
+ }
919
+
920
+ function cloneAmpersandCrossings(
921
+ crossings: SelectorMatchAmpersandCrossing[] | undefined
922
+ ): SelectorMatchAmpersandCrossing[] | undefined {
923
+ if (!crossings || crossings.length === 0) {
924
+ return undefined;
925
+ }
926
+
927
+ const next = new Array<SelectorMatchAmpersandCrossing>(crossings.length);
928
+ for (let i = 0; i < crossings.length; i++) {
929
+ const crossing = crossings[i]!;
930
+ next[i] = {
931
+ ampersandNode: crossing.ampersandNode,
932
+ targetSegment: cloneMatchSegment(crossing.targetSegment)!,
933
+ parentSegment: cloneMatchSegment(crossing.parentSegment)
934
+ };
935
+ }
936
+
937
+ return next;
938
+ }
939
+
940
+ function getCachedGroupMatch(
941
+ cache: GroupMatchCache,
942
+ targetGroup: Node,
943
+ findGroup: MatchGroup,
944
+ parent?: Selector,
945
+ evalContext?: EvalContext
946
+ ): MatchWindowResult {
947
+ let nodeCache = cache.get(targetGroup);
948
+ if (!nodeCache) {
949
+ nodeCache = new WeakMap<MatchGroup, MatchWindowResult>();
950
+ cache.set(targetGroup, nodeCache);
951
+ }
952
+
953
+ const cached = nodeCache.get(findGroup);
954
+ if (cached) {
955
+ return cached;
956
+ }
957
+
958
+ let result: MatchWindowResult;
959
+ if (isNode(targetGroup, N.CompoundSelector)) {
960
+ let matched = false;
961
+ let exact = false;
962
+
963
+ for (let i = 0; i < findGroup.alternatives.length; i++) {
964
+ const windowMatch = matchCompoundWindow(
965
+ targetGroup as unknown as Selector & { value: readonly Node[] },
966
+ 0,
967
+ (targetGroup as CompoundSelector).get('value').length - 1,
968
+ findGroup.alternatives[i]!,
969
+ parent,
970
+ evalContext
971
+ );
972
+ matched ||= windowMatch.matched;
973
+ exact ||= windowMatch.exact;
974
+
975
+ if (exact) {
976
+ break;
977
+ }
978
+ }
979
+
980
+ result = { matched, exact };
981
+ } else {
982
+ result = matchTargetGroup(
983
+ targetGroup,
984
+ findGroup,
985
+ parent,
986
+ !!parent && isNode(targetGroup, N.Ampersand),
987
+ evalContext
988
+ );
989
+ }
990
+ nodeCache.set(findGroup, result);
991
+ return result;
992
+ }
993
+
994
+ function getBranchAlternatives(node: Node): readonly Selector[] | undefined {
995
+ if (isNode(node, N.SelectorList)) {
996
+ return (node as SelectorList).get('value');
997
+ }
998
+
999
+ if (isNode(node, N.PseudoSelector) && node.get('name') === ':is' && isNode(node.get('arg'), N.Selector)) {
1000
+ if (isNode(node.get('arg'), N.SelectorList)) {
1001
+ return (node.get('arg') as SelectorList).get('value');
1002
+ }
1003
+
1004
+ return [node.get('arg') as Selector];
1005
+ }
1006
+
1007
+ return undefined;
1008
+ }
1009
+
1010
+ function matchGroupNodes(
1011
+ findNode: Node,
1012
+ targetNode: Node,
1013
+ findGroup: MatchGroup,
1014
+ groupMatchCache: GroupMatchCache,
1015
+ context: SelectorMatchContext,
1016
+ parent?: Selector
1017
+ ): MatchWindowResult {
1018
+ const targetBranches = getBranchAlternatives(targetNode);
1019
+ const findBranches = getBranchAlternatives(findNode);
1020
+ if (targetBranches && findBranches) {
1021
+ let matched = false;
1022
+ let exact = false;
1023
+
1024
+ for (let i = 0; i < findBranches.length; i++) {
1025
+ for (let j = 0; j < targetBranches.length; j++) {
1026
+ const nested = selectorMatchInternal(findBranches[i]!, targetBranches[j]!, parent, context);
1027
+ matched ||= nested.partialMatch;
1028
+ exact ||= nested.fullMatch;
1029
+
1030
+ if (exact) {
1031
+ break;
1032
+ }
1033
+ }
1034
+
1035
+ if (exact) {
1036
+ break;
1037
+ }
1038
+ }
1039
+
1040
+ return { matched, exact };
1041
+ }
1042
+
1043
+ if (targetBranches) {
1044
+ let matched = false;
1045
+ let exact = false;
1046
+
1047
+ for (let i = 0; i < targetBranches.length; i++) {
1048
+ const nested = selectorMatchInternal(findNode as Selector, targetBranches[i]!, parent, context);
1049
+ matched ||= nested.partialMatch;
1050
+ exact ||= nested.fullMatch;
1051
+
1052
+ if (exact) {
1053
+ break;
1054
+ }
1055
+ }
1056
+
1057
+ return { matched, exact };
1058
+ }
1059
+
1060
+ return getCachedGroupMatch(groupMatchCache, targetNode, findGroup, parent, context.evalContext);
1061
+ }
1062
+
1063
+ function pushNestedBranchMatches(
1064
+ findNode: Node,
1065
+ target: Selector,
1066
+ matches: SelectorMatchLocation[],
1067
+ context: SelectorMatchContext,
1068
+ parent?: Selector
1069
+ ): void {
1070
+ const branches = getBranchAlternatives(findNode);
1071
+ if (branches) {
1072
+ for (let i = 0; i < branches.length; i++) {
1073
+ const nested = selectorMatchInternal(target, branches[i]!, parent, context);
1074
+ if (!nested.fullMatch) {
1075
+ continue;
1076
+ }
1077
+ for (let j = 0; j < nested.matches.length; j++) {
1078
+ const match = nested.matches[j]!;
1079
+ matches.push({
1080
+ startIndex: match.startIndex,
1081
+ endIndex: match.endIndex,
1082
+ matchedIndices: match.matchedIndices,
1083
+ containingNode: match.containingNode,
1084
+ exact: false,
1085
+ consumedTarget: match.consumedTarget,
1086
+ ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
1087
+ });
1088
+ }
1089
+ }
1090
+ return;
1091
+ }
1092
+
1093
+ const children = findNode.children();
1094
+ let child = children.next();
1095
+ while (!child.done) {
1096
+ pushNestedBranchMatches(child.value, target, matches, context, parent);
1097
+ child = children.next();
1098
+ }
1099
+ }
1100
+
1101
+ /**
1102
+ * Compares a contiguous slice of ordered units.
1103
+ *
1104
+ * Groups compare via unordered basic-selector consumption, while combinators
1105
+ * must match exactly in place.
1106
+ */
1107
+ function matchUnitWindow(
1108
+ findUnits: MatchPlanUnit[],
1109
+ findStart: number,
1110
+ targetUnits: MatchPlanUnit[],
1111
+ targetStart: number,
1112
+ length: number,
1113
+ groupMatchCache: GroupMatchCache,
1114
+ context: SelectorMatchContext,
1115
+ parent?: Selector
1116
+ ): MatchWindowResult {
1117
+ let exact = true;
1118
+
1119
+ for (let offset = 0; offset < length; offset++) {
1120
+ const findUnit = findUnits[findStart + offset]!;
1121
+ const targetUnit = targetUnits[targetStart + offset]!;
1122
+
1123
+ if (findUnit.kind !== targetUnit.kind) {
1124
+ return { matched: false, exact: false };
1125
+ }
1126
+
1127
+ if (findUnit.kind === 'combinator') {
1128
+ if (targetUnit.kind !== 'combinator' || findUnit.value !== targetUnit.value) {
1129
+ return { matched: false, exact: false };
1130
+ }
1131
+ continue;
1132
+ }
1133
+
1134
+ const groupMatch = matchGroupNodes(
1135
+ findUnit.node,
1136
+ targetUnit.node,
1137
+ findUnit.group,
1138
+ groupMatchCache,
1139
+ context,
1140
+ parent
1141
+ );
1142
+ if (!groupMatch.matched) {
1143
+ return { matched: false, exact: false };
1144
+ }
1145
+
1146
+ exact &&= groupMatch.exact;
1147
+ }
1148
+
1149
+ return { matched: true, exact };
1150
+ }
1151
+
1152
+ /** True when a complex selector begins with a visible ampersand boundary. */
1153
+ function hasLeadingAmpersandBoundary(selector: Selector): boolean {
1154
+ return (
1155
+ isNode(selector, N.ComplexSelector)
1156
+ && (selector as ComplexSelector).get('value').length > 0
1157
+ && isNode((selector as ComplexSelector).get('value')[0]!, N.Ampersand)
1158
+ );
1159
+ }
1160
+
1161
+ function getBoundaryTailUnits(routePlan: RouteMatchPlan): MatchPlanUnit[] {
1162
+ if (hasLeadingAmpersandBoundary(routePlan.selector)) {
1163
+ return routePlan.units;
1164
+ }
1165
+
1166
+ return [{
1167
+ kind: 'combinator',
1168
+ index: -1,
1169
+ node: routePlan.selector,
1170
+ value: ' '
1171
+ }, ...routePlan.units];
1172
+ }
1173
+
1174
+ function locationCrossesAmpersand(location: SelectorMatchLocation): boolean {
1175
+ if (location.ampersandCrossings && location.ampersandCrossings.length > 0) {
1176
+ return true;
1177
+ }
1178
+
1179
+ if (location.crossesAmpersand) {
1180
+ return true;
1181
+ }
1182
+
1183
+ const { containingNode, startIndex, endIndex } = location;
1184
+
1185
+ if (!containingNode.hasFlag(F_AMPERSAND)) {
1186
+ return false;
1187
+ }
1188
+
1189
+ if (startIndex === undefined || endIndex === undefined) {
1190
+ return true;
1191
+ }
1192
+
1193
+ if (isNode(containingNode, N.CompoundSelector) || isNode(containingNode, N.ComplexSelector)) {
1194
+ for (let i = startIndex; i <= endIndex; i++) {
1195
+ if (containingNode.get('value')[i]?.hasFlag(F_AMPERSAND)) {
1196
+ return true;
1197
+ }
1198
+ }
1199
+ return false;
1200
+ }
1201
+
1202
+ if (isNode(containingNode, N.SelectorList)) {
1203
+ return !!(containingNode as SelectorList).get('value')[startIndex]?.hasFlag(F_AMPERSAND);
1204
+ }
1205
+
1206
+ return true;
1207
+ }
1208
+
1209
+ function getLocationAmpersandCrossings(
1210
+ location: SelectorMatchLocation,
1211
+ context?: EvalContext
1212
+ ): SelectorMatchAmpersandCrossing[] | undefined {
1213
+ if (location.ampersandCrossings && location.ampersandCrossings.length > 0) {
1214
+ return location.ampersandCrossings;
1215
+ }
1216
+
1217
+ const { containingNode } = location;
1218
+ if (!containingNode.hasFlag(F_AMPERSAND)) {
1219
+ return undefined;
1220
+ }
1221
+
1222
+ const indices = location.matchedIndices && location.matchedIndices.length > 0
1223
+ ? location.matchedIndices
1224
+ : undefined;
1225
+ const start = location.startIndex ?? indices?.[0] ?? 0;
1226
+ const end = location.endIndex ?? indices?.[indices.length - 1] ?? start;
1227
+ const targetSegment: SelectorMatchSegment = {
1228
+ containingNode,
1229
+ startIndex: location.startIndex,
1230
+ endIndex: location.endIndex,
1231
+ matchedIndices: location.matchedIndices ? [...location.matchedIndices] : undefined
1232
+ };
1233
+
1234
+ const crossings: SelectorMatchAmpersandCrossing[] = [];
1235
+ const seenAmpersands = new Set<Node>();
1236
+ const pushCrossing = (ampersandNode: Node): void => {
1237
+ if (seenAmpersands.has(ampersandNode)) {
1238
+ return;
1239
+ }
1240
+
1241
+ seenAmpersands.add(ampersandNode);
1242
+
1243
+ let parentSegment: SelectorMatchSegment | undefined;
1244
+ if (isNode(ampersandNode, N.Ampersand)) {
1245
+ const resolved = ampersandNode.getResolvedSelector(context);
1246
+ if (resolved && !isNode(resolved, N.Nil)) {
1247
+ parentSegment = {
1248
+ containingNode: resolved
1249
+ };
1250
+ }
1251
+ }
1252
+
1253
+ crossings.push({
1254
+ ampersandNode,
1255
+ targetSegment,
1256
+ parentSegment
1257
+ });
1258
+ };
1259
+
1260
+ if (isNode(containingNode, N.Ampersand)) {
1261
+ pushCrossing(containingNode);
1262
+ return crossings;
1263
+ }
1264
+
1265
+ if (isNode(containingNode, N.CompoundSelector) || isNode(containingNode, N.ComplexSelector)) {
1266
+ if (indices) {
1267
+ for (let i = 0; i < indices.length; i++) {
1268
+ const idx = indices[i]!;
1269
+ const node = containingNode.get('value')[idx];
1270
+ if (node && isNode(node, N.Ampersand)) {
1271
+ pushCrossing(node);
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ for (let i = start; i <= end; i++) {
1277
+ const node = containingNode.get('value')[i];
1278
+ if (node && isNode(node, N.Ampersand)) {
1279
+ pushCrossing(node);
1280
+ }
1281
+ }
1282
+ }
1283
+
1284
+ if (isNode(containingNode, N.SelectorList) && start === end) {
1285
+ const node = (containingNode as SelectorList).get('value')[start];
1286
+ if (node && isNode(node, N.Ampersand)) {
1287
+ pushCrossing(node);
1288
+ } else if (node && (isNode(node, N.CompoundSelector) || isNode(node, N.ComplexSelector))) {
1289
+ for (let i = 0; i < node.get('value').length; i++) {
1290
+ const child = node.get('value')[i];
1291
+ if (child && isNode(child, N.Ampersand)) {
1292
+ const resolved = child.getResolvedSelector(context);
1293
+ crossings.push({
1294
+ ampersandNode: child,
1295
+ targetSegment,
1296
+ parentSegment: resolved && !isNode(resolved, N.Nil)
1297
+ ? { containingNode: resolved as Selector }
1298
+ : undefined
1299
+ });
1300
+ }
1301
+ }
1302
+ }
1303
+ }
1304
+
1305
+ return crossings.length > 0 ? crossings : undefined;
1306
+ }
1307
+
1308
+ /**
1309
+ * Finalizes aggregate booleans from the collected match list.
1310
+ *
1311
+ * This intentionally uses a single scan to avoid repeated array passes on a
1312
+ * hot path.
1313
+ */
1314
+ function finalizeMatchState(result: SelectorMatchState, context?: EvalContext): SelectorMatchState {
1315
+ let fullMatch = false;
1316
+ let crossesAmpersand = false;
1317
+
1318
+ for (let i = 0; i < result.matches.length; i++) {
1319
+ const match = result.matches[i]!;
1320
+ if (!match.ampersandCrossings) {
1321
+ match.ampersandCrossings = getLocationAmpersandCrossings(match, context);
1322
+ }
1323
+ fullMatch ||= !!match.exact;
1324
+ crossesAmpersand ||= locationCrossesAmpersand(match);
1325
+
1326
+ if (fullMatch && crossesAmpersand) {
1327
+ break;
1328
+ }
1329
+ }
1330
+
1331
+ result.fullMatch ||= fullMatch;
1332
+ result.partialMatch ||= result.matches.length > 0;
1333
+ result.crossesAmpersand = crossesAmpersand;
1334
+ return result;
1335
+ }
1336
+
1337
+ /**
1338
+ * Recursively searches inside searchable pseudo-selector arguments.
1339
+ *
1340
+ * These nested matches are root-like searches; they do not allow an outer
1341
+ * match route to continue through the pseudo boundary.
1342
+ */
1343
+ function pushNestedPseudoMatches(
1344
+ find: Selector,
1345
+ targetNode: Node,
1346
+ matches: SelectorMatchLocation[],
1347
+ context: SelectorMatchContext
1348
+ ): void {
1349
+ if (isSearchablePseudoBoundary(targetNode)) {
1350
+ if (isSearchablePseudoBoundary(find)) {
1351
+ if (find.get('name') !== targetNode.get('name')) {
1352
+ return;
1353
+ }
1354
+
1355
+ const nested = selectorMatchInternal(find.get('arg') as Selector, targetNode.get('arg') as Selector, undefined, context);
1356
+ for (let i = 0; i < nested.matches.length; i++) {
1357
+ const match = nested.matches[i]!;
1358
+ matches.push({
1359
+ startIndex: match.startIndex,
1360
+ endIndex: match.endIndex,
1361
+ containingNode: match.containingNode,
1362
+ exact: match.exact,
1363
+ consumedTarget: match.consumedTarget,
1364
+ ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
1365
+ });
1366
+ }
1367
+ return;
1368
+ }
1369
+
1370
+ const nested = selectorMatchInternal(find, targetNode.get('arg') as Selector, undefined, context);
1371
+ for (let i = 0; i < nested.matches.length; i++) {
1372
+ const match = nested.matches[i]!;
1373
+ matches.push({
1374
+ startIndex: match.startIndex,
1375
+ endIndex: match.endIndex,
1376
+ containingNode: match.containingNode,
1377
+ exact: false,
1378
+ consumedTarget: match.consumedTarget,
1379
+ ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
1380
+ });
1381
+ }
1382
+ return;
1383
+ }
1384
+
1385
+ const children = targetNode.children();
1386
+ let child = children.next();
1387
+ while (!child.done) {
1388
+ pushNestedPseudoMatches(find, child.value, matches, context);
1389
+ child = children.next();
1390
+ }
1391
+ }
1392
+
1393
+ /**
1394
+ * Finds all occurrences of `find` inside `target`.
1395
+ *
1396
+ * Matching is ordered at the route level, unordered only inside a single
1397
+ * compound-like position, and branches only at `SelectorList` alternatives.
1398
+ * A selector list on the find side is treated as alternate find routes: any
1399
+ * one alternate may match, and each matching alternate contributes its own
1400
+ * recorded locations.
1401
+ *
1402
+ * When `parent` is provided, it acts like an implicit prefix context joined to
1403
+ * `target` by a descendant combinator. Parent traversal is attempted only when
1404
+ * a match reaches a left-side ampersand boundary with partial-but-incomplete
1405
+ * progress; matches that exist only inside the parent do not get added on
1406
+ * their own.
1407
+ *
1408
+ * That same `parent` context is preserved when matching through nested
1409
+ * selector-list and `:is(...)` alternatives, because those are still the same
1410
+ * authored match route. It is not preserved for root-like searches inside
1411
+ * non-`:is()` pseudo-selector boundaries, because those searches must not
1412
+ * continue the outer route through that boundary.
1413
+ *
1414
+ * Complex selector branches inside `:is(...)` or selector lists are treated as
1415
+ * alternate branch routes. Matching may succeed inside one branch, but the
1416
+ * outer route cannot continue leftward through that branch unless that branch
1417
+ * itself was consumed end-to-end.
1418
+ */
1419
+ function selectorMatchUncached(
1420
+ find: Selector,
1421
+ target: Selector,
1422
+ parent: Selector | undefined,
1423
+ context: SelectorMatchContext
1424
+ ): SelectorMatchState {
1425
+ const evalContext = context.evalContext;
1426
+
1427
+ if (isNode(find, N.SelectorList)) {
1428
+ const result = emptySelectorMatchState();
1429
+
1430
+ for (let i = 0; i < (find as SelectorList).get('value').length; i++) {
1431
+ const nested = selectorMatchInternal((find as SelectorList).get('value')[i]!, target, parent, context);
1432
+ result.fullMatch ||= nested.fullMatch;
1433
+ result.partialMatch ||= nested.partialMatch;
1434
+ result.crossesAmpersand ||= nested.crossesAmpersand;
1435
+ pushMatches(result.matches, nested.matches);
1436
+ }
1437
+
1438
+ return finalizeMatchState(result, context.evalContext);
1439
+ }
1440
+
1441
+ if (
1442
+ isNode(find, N.PseudoSelector)
1443
+ && find.get('name') !== ':is'
1444
+ && isNode(find.get('arg'), N.Selector)
1445
+ ) {
1446
+ if (isSearchablePseudoBoundary(target)) {
1447
+ if (find.get('name') !== target.get('name')) {
1448
+ return emptySelectorMatchState();
1449
+ }
1450
+
1451
+ return selectorMatchInternal(find.get('arg') as Selector, target.get('arg') as Selector, parent, context);
1452
+ }
1453
+
1454
+ const nested = selectorMatchInternal(find.get('arg') as Selector, target, parent, context);
1455
+ if (!nested.partialMatch) {
1456
+ return nested;
1457
+ }
1458
+
1459
+ const matches = new Array<SelectorMatchLocation>(nested.matches.length);
1460
+ for (let i = 0; i < nested.matches.length; i++) {
1461
+ const match = nested.matches[i]!;
1462
+ matches[i] = {
1463
+ startIndex: match.startIndex,
1464
+ endIndex: match.endIndex,
1465
+ containingNode: find.get('arg') as Node,
1466
+ exact: false,
1467
+ consumedTarget: false,
1468
+ ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
1469
+ };
1470
+ }
1471
+
1472
+ return {
1473
+ fullMatch: false,
1474
+ partialMatch: true,
1475
+ crossesAmpersand: matches.some(locationCrossesAmpersand),
1476
+ matches
1477
+ };
1478
+ }
1479
+
1480
+ const findValue = selectorValueOf(find, evalContext);
1481
+ if (isNode(target, N.SelectorList)) {
1482
+ for (let i = 0; i < (target as SelectorList).get('value').length; i++) {
1483
+ const sel = (target as SelectorList).get('value')[i]!;
1484
+ if (findValue === selectorValueOf(sel, evalContext)) {
1485
+ return {
1486
+ fullMatch: true,
1487
+ partialMatch: true,
1488
+ crossesAmpersand: sel.hasFlag(F_AMPERSAND),
1489
+ matches: [{
1490
+ startIndex: i,
1491
+ endIndex: i,
1492
+ matchedIndices: [i],
1493
+ containingNode: target,
1494
+ exact: true,
1495
+ consumedTarget: (target as SelectorList).get('value').length === 1,
1496
+ ampersandCrossings: getLocationAmpersandCrossings({
1497
+ startIndex: i,
1498
+ endIndex: i,
1499
+ matchedIndices: [i],
1500
+ containingNode: target,
1501
+ exact: true,
1502
+ consumedTarget: (target as SelectorList).get('value').length === 1
1503
+ }, evalContext)
1504
+ }]
1505
+ };
1506
+ }
1507
+
1508
+ const nested = selectorMatchInternal(find, sel, parent, context);
1509
+ if (nested.partialMatch) {
1510
+ return {
1511
+ fullMatch: nested.fullMatch,
1512
+ partialMatch: true,
1513
+ crossesAmpersand: nested.crossesAmpersand || sel.hasFlag(F_AMPERSAND),
1514
+ matches: [{
1515
+ startIndex: i,
1516
+ endIndex: i,
1517
+ matchedIndices: [i],
1518
+ containingNode: target,
1519
+ exact: nested.fullMatch,
1520
+ consumedTarget: nested.fullMatch && (target as SelectorList).get('value').length === 1,
1521
+ ampersandCrossings: cloneAmpersandCrossings(nested.matches[0]?.ampersandCrossings)
1522
+ }]
1523
+ };
1524
+ }
1525
+ }
1526
+ return emptySelectorMatchState();
1527
+ } else {
1528
+ if (findValue === selectorValueOf(target, evalContext)) {
1529
+ return {
1530
+ fullMatch: true,
1531
+ partialMatch: true,
1532
+ crossesAmpersand: target.hasFlag(F_AMPERSAND),
1533
+ matches: [{
1534
+ containingNode: target,
1535
+ exact: true,
1536
+ consumedTarget: true,
1537
+ ampersandCrossings: getLocationAmpersandCrossings({
1538
+ containingNode: target,
1539
+ exact: true,
1540
+ consumedTarget: true
1541
+ }, evalContext)
1542
+ }]
1543
+ };
1544
+ }
1545
+ }
1546
+
1547
+ if (
1548
+ !parent
1549
+ && find.keySetLibrary
1550
+ && target.keySetLibrary
1551
+ ) {
1552
+ if (evalContext) {
1553
+ if (safeIsDisjoint(find.getKeySet(evalContext), target.getKeySet(evalContext))) {
1554
+ return emptySelectorMatchState();
1555
+ }
1556
+ } else {
1557
+ if (
1558
+ !find.hasFlag(F_AMPERSAND)
1559
+ && target.hasFlag(F_AMPERSAND)
1560
+ && safeIsDisjoint(find.visibleKeySet, target.visibleKeySet)
1561
+ ) {
1562
+ return emptySelectorMatchState();
1563
+ }
1564
+ if (
1565
+ !safeIsDisjoint(find.requiredKeySet, find.keySet)
1566
+ && safeIsDisjoint(find.requiredKeySet, target.keySet)
1567
+ ) {
1568
+ return emptySelectorMatchState();
1569
+ }
1570
+ }
1571
+ }
1572
+
1573
+ const findPlan = getSelectorMatchPlan(find, undefined, evalContext);
1574
+ if (findPlan.kind !== 'route' || findPlan.units.length === 0) {
1575
+ return emptySelectorMatchState();
1576
+ }
1577
+ const groupMatchCache: GroupMatchCache = new WeakMap();
1578
+
1579
+ const matchParentRoute = (
1580
+ routePlan: RouteMatchPlan,
1581
+ parentPlan: MatchPlan
1582
+ ): SelectorMatchState => {
1583
+ const result = emptySelectorMatchState();
1584
+ const routeUnits = routePlan.units;
1585
+ const findUnits = findPlan.units;
1586
+
1587
+ if (routeUnits.length === 0) {
1588
+ return result;
1589
+ }
1590
+
1591
+ const boundaryTailUnits = getBoundaryTailUnits(routePlan);
1592
+ const boundaryTailLength = boundaryTailUnits.length;
1593
+ const findLength = findUnits.length;
1594
+ const matchedAll = (
1595
+ findLength <= boundaryTailLength
1596
+ && matchUnitWindow(
1597
+ findUnits,
1598
+ 0,
1599
+ boundaryTailUnits,
1600
+ boundaryTailLength - findLength,
1601
+ findLength,
1602
+ groupMatchCache,
1603
+ context,
1604
+ parent
1605
+ ).matched
1606
+ );
1607
+
1608
+ if (matchedAll || boundaryTailLength > findLength) {
1609
+ return result;
1610
+ }
1611
+
1612
+ const targetBoundaryMatch = matchUnitWindow(
1613
+ findUnits,
1614
+ findLength - boundaryTailLength,
1615
+ boundaryTailUnits,
1616
+ 0,
1617
+ boundaryTailLength,
1618
+ groupMatchCache,
1619
+ context,
1620
+ parent
1621
+ );
1622
+
1623
+ if (!targetBoundaryMatch.matched) {
1624
+ return result;
1625
+ }
1626
+
1627
+ const remainingFindLength = findLength - boundaryTailLength;
1628
+ if (remainingFindLength === 0) {
1629
+ return result;
1630
+ }
1631
+
1632
+ const matchParentPlan = (plan: MatchPlan): void => {
1633
+ if (plan.kind === 'list') {
1634
+ for (let i = 0; i < plan.alternates.length; i++) {
1635
+ matchParentPlan(plan.alternates[i]!);
1636
+ }
1637
+ return;
1638
+ }
1639
+
1640
+ const parentUnits = plan.units;
1641
+ if (parentUnits.length < remainingFindLength) {
1642
+ return;
1643
+ }
1644
+
1645
+ const parentMatch = matchUnitWindow(
1646
+ findUnits,
1647
+ 0,
1648
+ parentUnits,
1649
+ parentUnits.length - remainingFindLength,
1650
+ remainingFindLength,
1651
+ groupMatchCache,
1652
+ context
1653
+ );
1654
+
1655
+ if (!parentMatch.matched) {
1656
+ return;
1657
+ }
1658
+
1659
+ const exact = (
1660
+ parentMatch.exact
1661
+ && targetBoundaryMatch.exact
1662
+ && remainingFindLength === parentUnits.length
1663
+ );
1664
+ const firstTargetUnit = boundaryTailUnits[0]!;
1665
+ const lastTargetUnit = boundaryTailUnits[boundaryTailLength - 1]!;
1666
+ const firstParentUnit = parentUnits[parentUnits.length - remainingFindLength]!;
1667
+ const lastParentUnit = parentUnits[parentUnits.length - 1]!;
1668
+ const leadingAmpersand = hasLeadingAmpersandBoundary(routePlan.selector)
1669
+ ? (routePlan.selector as ComplexSelector).value[0]
1670
+ : undefined;
1671
+ const ampersandCrossings: SelectorMatchAmpersandCrossing[] = [{
1672
+ ampersandNode: leadingAmpersand,
1673
+ targetSegment: {
1674
+ containingNode: routePlan.selector,
1675
+ startIndex: firstTargetUnit.index,
1676
+ endIndex: lastTargetUnit.index
1677
+ },
1678
+ parentSegment: {
1679
+ containingNode: plan.selector,
1680
+ startIndex: firstParentUnit.index,
1681
+ endIndex: lastParentUnit.index
1682
+ }
1683
+ }];
1684
+
1685
+ if (isNode(routePlan.selector, N.CompoundSelector) || isNode(routePlan.selector, N.ComplexSelector)) {
1686
+ result.matches.push({
1687
+ startIndex: 0,
1688
+ endIndex: (routePlan.selector as CompoundSelector | ComplexSelector).get('value').length - 1,
1689
+ containingNode: routePlan.selector,
1690
+ exact,
1691
+ crossesAmpersand: true,
1692
+ consumedTarget: !!exact,
1693
+ ampersandCrossings
1694
+ });
1695
+ return;
1696
+ }
1697
+
1698
+ result.matches.push({
1699
+ containingNode: routePlan.selector,
1700
+ exact,
1701
+ crossesAmpersand: true,
1702
+ consumedTarget: !!exact,
1703
+ ampersandCrossings
1704
+ });
1705
+ };
1706
+
1707
+ matchParentPlan(parentPlan);
1708
+ return finalizeMatchState(result, context.evalContext);
1709
+ };
1710
+
1711
+ const pushMidRouteAmpersandMatches = (
1712
+ matches: SelectorMatchLocation[],
1713
+ routePlan: RouteMatchPlan
1714
+ ): void => {
1715
+ const routeUnits = routePlan.units;
1716
+ const findUnits = findPlan.units;
1717
+
1718
+ const pushMatchesForResolvedPlan = (
1719
+ plan: MatchPlan,
1720
+ ampStart: number,
1721
+ ampUnit: MatchPlanUnit & { kind: 'group' },
1722
+ tailLength: number,
1723
+ tailMatch: MatchWindowResult
1724
+ ): void => {
1725
+ if (plan.kind === 'list') {
1726
+ for (let i = 0; i < plan.alternates.length; i++) {
1727
+ pushMatchesForResolvedPlan(plan.alternates[i]!, ampStart, ampUnit, tailLength, tailMatch);
1728
+ }
1729
+ return;
1730
+ }
1731
+
1732
+ const remainingFindLength = findUnits.length - tailLength;
1733
+ if (remainingFindLength <= 0) {
1734
+ return;
1735
+ }
1736
+
1737
+ const parentUnits = plan.units;
1738
+ if (parentUnits.length < remainingFindLength) {
1739
+ return;
1740
+ }
1741
+
1742
+ const parentMatch = matchUnitWindow(
1743
+ findUnits,
1744
+ 0,
1745
+ parentUnits,
1746
+ parentUnits.length - remainingFindLength,
1747
+ remainingFindLength,
1748
+ groupMatchCache,
1749
+ context
1750
+ );
1751
+ if (!parentMatch.matched) {
1752
+ return;
1753
+ }
1754
+
1755
+ const tailEndUnit = routeUnits[ampStart + tailLength] ?? routeUnits[routeUnits.length - 1]!;
1756
+ const firstParentUnit = parentUnits[parentUnits.length - remainingFindLength]!;
1757
+ const lastParentUnit = parentUnits[parentUnits.length - 1]!;
1758
+ const exact = (
1759
+ ampStart === 0
1760
+ && ampStart + tailLength === routeUnits.length - 1
1761
+ && remainingFindLength === parentUnits.length
1762
+ && parentMatch.exact
1763
+ && tailMatch.exact
1764
+ );
1765
+
1766
+ matches.push({
1767
+ startIndex: ampUnit.index,
1768
+ endIndex: tailEndUnit.index,
1769
+ containingNode: routePlan.selector,
1770
+ exact,
1771
+ crossesAmpersand: true,
1772
+ consumedTarget: !!exact,
1773
+ ampersandCrossings: [{
1774
+ ampersandNode: ampUnit.node,
1775
+ targetSegment: {
1776
+ containingNode: routePlan.selector,
1777
+ startIndex: ampUnit.index,
1778
+ endIndex: tailEndUnit.index
1779
+ },
1780
+ parentSegment: {
1781
+ containingNode: plan.selector,
1782
+ startIndex: firstParentUnit.index,
1783
+ endIndex: lastParentUnit.index
1784
+ }
1785
+ }]
1786
+ });
1787
+ };
1788
+
1789
+ for (let ampStart = 0; ampStart < routeUnits.length - 1; ampStart++) {
1790
+ const ampUnit = routeUnits[ampStart]!;
1791
+ if (!(ampUnit.kind === 'group' && isNode(ampUnit.node, N.Ampersand))) {
1792
+ continue;
1793
+ }
1794
+
1795
+ const resolved = ampUnit.node.getResolvedSelector(evalContext) ?? parent;
1796
+ if (!resolved || isNode(resolved, N.Nil)) {
1797
+ continue;
1798
+ }
1799
+
1800
+ const resolvedPlan = getSelectorMatchPlan(resolved as Selector, undefined, evalContext);
1801
+ const maxTailLength = Math.min(
1802
+ routeUnits.length - (ampStart + 1),
1803
+ findUnits.length - 1
1804
+ );
1805
+
1806
+ for (let tailLength = 1; tailLength <= maxTailLength; tailLength++) {
1807
+ const tailMatch = matchUnitWindow(
1808
+ findUnits,
1809
+ findUnits.length - tailLength,
1810
+ routeUnits,
1811
+ ampStart + 1,
1812
+ tailLength,
1813
+ groupMatchCache,
1814
+ context,
1815
+ parent
1816
+ );
1817
+ if (!tailMatch.matched) {
1818
+ continue;
1819
+ }
1820
+
1821
+ pushMatchesForResolvedPlan(resolvedPlan, ampStart, {
1822
+ ...ampUnit,
1823
+ kind: 'group'
1824
+ }, tailLength, tailMatch);
1825
+ }
1826
+ }
1827
+ };
1828
+
1829
+ const matchTargetRoute = (
1830
+ routePlan: RouteMatchPlan,
1831
+ parentPlan?: MatchPlan
1832
+ ): SelectorMatchState => {
1833
+ const result = emptySelectorMatchState();
1834
+ const routeUnits = routePlan.units;
1835
+ const findUnits = findPlan.units;
1836
+ const singleFindGroup = findUnits.length === 1 && findUnits[0]!.kind === 'group'
1837
+ ? findUnits[0]!
1838
+ : undefined;
1839
+ const suppressAmbiguousBranchLocations = findUnits.length > 0 && routePlan.selector !== find
1840
+ ? findPlan.hasAmbiguousBranchTail
1841
+ : findPlan.hasAmbiguousBranchTail;
1842
+
1843
+ if (routeUnits.length >= findUnits.length) {
1844
+ const lastStart = routeUnits.length - findUnits.length;
1845
+
1846
+ for (let start = 0; start <= lastStart; start++) {
1847
+ let exact = start === 0 && routeUnits.length === findUnits.length;
1848
+ const windowMatch = matchUnitWindow(
1849
+ findUnits,
1850
+ 0,
1851
+ routeUnits,
1852
+ start,
1853
+ findUnits.length,
1854
+ groupMatchCache,
1855
+ context,
1856
+ parent
1857
+ );
1858
+ let matched = windowMatch.matched;
1859
+ exact &&= windowMatch.exact;
1860
+
1861
+ if (!matched) {
1862
+ continue;
1863
+ }
1864
+
1865
+ if (
1866
+ parent
1867
+ && findUnits.length === 1
1868
+ && routeUnits.length > 1
1869
+ && routeUnits[start]?.kind === 'group'
1870
+ && isNode(routeUnits[start]!.node, N.Ampersand)
1871
+ ) {
1872
+ continue;
1873
+ }
1874
+
1875
+ if (singleFindGroup) {
1876
+ const targetUnit = routeUnits[start]!;
1877
+ if (targetUnit.kind === 'group') {
1878
+ const groupLocations = collectGroupMatchLocations(
1879
+ targetUnit.node,
1880
+ singleFindGroup.group,
1881
+ parent,
1882
+ evalContext
1883
+ );
1884
+ for (let i = 0; i < groupLocations.length; i++) {
1885
+ const location = groupLocations[i]!;
1886
+ result.matches.push({
1887
+ ...location,
1888
+ exact: exact && location.exact,
1889
+ consumedTarget: !!location.consumedTarget,
1890
+ ampersandCrossings: cloneAmpersandCrossings(location.ampersandCrossings)
1891
+ });
1892
+ }
1893
+ continue;
1894
+ }
1895
+ }
1896
+
1897
+ if (!windowMatch.exact && suppressAmbiguousBranchLocations) {
1898
+ result.partialMatch = true;
1899
+ continue;
1900
+ }
1901
+
1902
+ result.matches.push({
1903
+ startIndex: routeUnits[start]!.index,
1904
+ endIndex: routeUnits[start + findUnits.length - 1]!.index,
1905
+ containingNode: routePlan.selector as Node,
1906
+ exact,
1907
+ consumedTarget: !!exact
1908
+ });
1909
+ }
1910
+ }
1911
+
1912
+ pushMidRouteAmpersandMatches(result.matches, routePlan);
1913
+
1914
+ if (parentPlan) {
1915
+ pushMatches(result.matches, matchParentRoute(routePlan, parentPlan).matches);
1916
+ }
1917
+
1918
+ return finalizeMatchState(result, context.evalContext);
1919
+ };
1920
+
1921
+ const matchTargetPlan = (
1922
+ plan: MatchPlan,
1923
+ parentPlan?: MatchPlan
1924
+ ): SelectorMatchState => {
1925
+ if (plan.kind === 'route') {
1926
+ return matchTargetRoute(plan, parentPlan);
1927
+ }
1928
+
1929
+ const result = emptySelectorMatchState();
1930
+ for (let i = 0; i < plan.alternates.length; i++) {
1931
+ const match = matchTargetPlan(plan.alternates[i]!, parentPlan);
1932
+ pushMatches(result.matches, match.matches);
1933
+ }
1934
+
1935
+ return finalizeMatchState(result, context.evalContext);
1936
+ };
1937
+
1938
+ const parentPlan = parent ? getSelectorMatchPlan(parent, undefined, evalContext) : undefined;
1939
+ const result = matchTargetPlan(getSelectorMatchPlan(target, parent, evalContext), parentPlan);
1940
+ pushNestedPseudoMatches(find, target, result.matches, context);
1941
+ if (!result.partialMatch && result.matches.length === 0) {
1942
+ pushNestedBranchMatches(find, target, result.matches, context, parent);
1943
+ }
1944
+ return finalizeMatchState(result, context.evalContext);
1945
+ }
1946
+
1947
+ function selectorMatchInternal(
1948
+ find: Selector,
1949
+ target: Selector,
1950
+ parent: Selector | undefined,
1951
+ context: SelectorMatchContext
1952
+ ): SelectorMatchState {
1953
+ if (parent) {
1954
+ return selectorMatchUncached(find, target, parent, context);
1955
+ }
1956
+
1957
+ let findCache = context.pairCache.get(find);
1958
+ if (!findCache) {
1959
+ findCache = new WeakMap<Selector, SelectorMatchState>();
1960
+ context.pairCache.set(find, findCache);
1961
+ }
1962
+
1963
+ const cached = findCache.get(target);
1964
+ if (cached) {
1965
+ return cached;
1966
+ }
1967
+
1968
+ const result = selectorMatchUncached(find, target, undefined, context);
1969
+ findCache.set(target, result);
1970
+ return result;
1971
+ }
1972
+
1973
+ /**
1974
+ * Finds all occurrences of `find` inside `target`.
1975
+ *
1976
+ * Matching is ordered at the route level, unordered only inside a single
1977
+ * compound-like position, and branches only at `SelectorList` alternatives.
1978
+ *
1979
+ * When `parent` is provided, it acts like an implicit prefix context joined to
1980
+ * `target` by a descendant combinator. Parent traversal is attempted only when
1981
+ * a match reaches a left-side ampersand boundary with partial-but-incomplete
1982
+ * progress; matches that exist only inside the parent do not get added on
1983
+ * their own.
1984
+ *
1985
+ * Complex selector branches inside `:is(...)` or selector lists are treated as
1986
+ * alternate branch routes. Matching may succeed inside one branch, but the
1987
+ * outer route cannot continue leftward through that branch unless that branch
1988
+ * itself was consumed end-to-end.
1989
+ *
1990
+ * The matcher uses normalized `valueOf()` and selector key-set fast paths only
1991
+ * as cheap equality / rejection signals. They are not shape-preserving and
1992
+ * should not be used by callers to infer the structural rewrite shape of a
1993
+ * successful match.
1994
+ */
1995
+ export function selectorMatch(
1996
+ find: Selector,
1997
+ target: Selector,
1998
+ parent?: Selector,
1999
+ evalContext?: EvalContext
2000
+ ): SelectorMatchState {
2001
+ return selectorMatchInternal(find, target, parent, {
2002
+ pairCache: new WeakMap(),
2003
+ evalContext
2004
+ });
2005
+ }