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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/lib/index.cjs +20159 -0
  2. package/lib/index.d.cts +5993 -0
  3. package/lib/index.d.cts.map +1 -0
  4. package/lib/index.d.ts +5992 -21
  5. package/lib/index.d.ts.map +1 -1
  6. package/lib/index.js +19926 -22
  7. package/lib/index.js.map +1 -1
  8. package/package.json +15 -14
  9. package/src/__tests__/define-function-record.test.ts +58 -0
  10. package/src/__tests__/define-function-simple.test.ts +55 -0
  11. package/src/__tests__/define-function-split-sequence.test.ts +547 -0
  12. package/src/__tests__/define-function-type-parity.test.ts +9 -0
  13. package/src/__tests__/define-function.test.ts +763 -0
  14. package/src/__tests__/num-operations.test.ts +91 -0
  15. package/src/__tests__/safe-parse.test.ts +374 -0
  16. package/src/context.ts +896 -0
  17. package/src/conversions.ts +282 -0
  18. package/src/debug-log.ts +29 -0
  19. package/src/define-function.ts +1006 -0
  20. package/src/deprecation.ts +67 -0
  21. package/src/globals.d.ts +26 -0
  22. package/src/index.ts +31 -0
  23. package/src/jess-error.ts +773 -0
  24. package/src/logger/deprecation-processing.ts +109 -0
  25. package/src/logger.ts +31 -0
  26. package/src/plugin.ts +292 -0
  27. package/src/tree/LOOKUP_CHAINS.md +35 -0
  28. package/src/tree/README.md +18 -0
  29. package/src/tree/__tests__/__snapshots__/extend-eval-integration.test.ts.snap +1455 -0
  30. package/src/tree/__tests__/ampersand.test.ts +382 -0
  31. package/src/tree/__tests__/at-rule.test.ts +2047 -0
  32. package/src/tree/__tests__/basic-render.test.ts +212 -0
  33. package/src/tree/__tests__/block.test.ts +40 -0
  34. package/src/tree/__tests__/call.test.ts +346 -0
  35. package/src/tree/__tests__/color.test.ts +537 -0
  36. package/src/tree/__tests__/condition.test.ts +186 -0
  37. package/src/tree/__tests__/control.test.ts +564 -0
  38. package/src/tree/__tests__/declaration.test.ts +253 -0
  39. package/src/tree/__tests__/dependency-graph.test.ts +177 -0
  40. package/src/tree/__tests__/detached-rulesets.test.ts +213 -0
  41. package/src/tree/__tests__/dimension.test.ts +236 -0
  42. package/src/tree/__tests__/expression.test.ts +73 -0
  43. package/src/tree/__tests__/ext-node.test.ts +31 -0
  44. package/src/tree/__tests__/extend-eval-integration.test.ts +1033 -0
  45. package/src/tree/__tests__/extend-import-style.test.ts +929 -0
  46. package/src/tree/__tests__/extend-less-fixtures.test.ts +851 -0
  47. package/src/tree/__tests__/extend-list.test.ts +31 -0
  48. package/src/tree/__tests__/extend-roots.test.ts +1045 -0
  49. package/src/tree/__tests__/extend-rules.test.ts +740 -0
  50. package/src/tree/__tests__/func.test.ts +171 -0
  51. package/src/tree/__tests__/import-js.test.ts +33 -0
  52. package/src/tree/__tests__/import-style-test-helpers.ts +56 -0
  53. package/src/tree/__tests__/import-style.test.ts +1967 -0
  54. package/src/tree/__tests__/interpolated-reference.test.ts +44 -0
  55. package/src/tree/__tests__/interpolated.test.ts +41 -0
  56. package/src/tree/__tests__/list.test.ts +177 -0
  57. package/src/tree/__tests__/log.test.ts +83 -0
  58. package/src/tree/__tests__/mixin-recursion.test.ts +639 -0
  59. package/src/tree/__tests__/mixin.test.ts +2171 -0
  60. package/src/tree/__tests__/negative.test.ts +45 -0
  61. package/src/tree/__tests__/nesting-collapse.test.ts +519 -0
  62. package/src/tree/__tests__/node-flags-perf.test.ts +195 -0
  63. package/src/tree/__tests__/node-flags.test.ts +410 -0
  64. package/src/tree/__tests__/node-graph.test.ts +598 -0
  65. package/src/tree/__tests__/node-mutation.test.ts +182 -0
  66. package/src/tree/__tests__/operation.test.ts +18 -0
  67. package/src/tree/__tests__/paren.test.ts +90 -0
  68. package/src/tree/__tests__/preserve-mode-output.test.ts +50 -0
  69. package/src/tree/__tests__/quoted.test.ts +72 -0
  70. package/src/tree/__tests__/range.test.ts +59 -0
  71. package/src/tree/__tests__/reference.test.ts +743 -0
  72. package/src/tree/__tests__/rest.test.ts +29 -0
  73. package/src/tree/__tests__/rules-raw.test.ts +14 -0
  74. package/src/tree/__tests__/rules.test.ts +1271 -0
  75. package/src/tree/__tests__/ruleset.test.ts +597 -0
  76. package/src/tree/__tests__/selector-attr.test.ts +50 -0
  77. package/src/tree/__tests__/selector-basic.test.ts +44 -0
  78. package/src/tree/__tests__/selector-capture.test.ts +22 -0
  79. package/src/tree/__tests__/selector-complex.test.ts +120 -0
  80. package/src/tree/__tests__/selector-compound.test.ts +74 -0
  81. package/src/tree/__tests__/selector-interpolated.test.ts +50 -0
  82. package/src/tree/__tests__/selector-list.test.ts +59 -0
  83. package/src/tree/__tests__/selector-pseudo.test.ts +23 -0
  84. package/src/tree/__tests__/selector.test.ts +182 -0
  85. package/src/tree/__tests__/sequence.test.ts +226 -0
  86. package/src/tree/__tests__/serialize-types.test.ts +529 -0
  87. package/src/tree/__tests__/spaced.test.ts +8 -0
  88. package/src/tree/__tests__/url.test.ts +72 -0
  89. package/src/tree/__tests__/var-declaration.test.ts +90 -0
  90. package/src/tree/ampersand.ts +538 -0
  91. package/src/tree/any.ts +169 -0
  92. package/src/tree/at-rule.ts +760 -0
  93. package/src/tree/block.ts +72 -0
  94. package/src/tree/bool.ts +46 -0
  95. package/src/tree/call.ts +593 -0
  96. package/src/tree/collection.ts +52 -0
  97. package/src/tree/color.ts +629 -0
  98. package/src/tree/combinator.ts +30 -0
  99. package/src/tree/comment.ts +36 -0
  100. package/src/tree/condition.ts +194 -0
  101. package/src/tree/control.ts +452 -0
  102. package/src/tree/declaration-custom.ts +56 -0
  103. package/src/tree/declaration-var.ts +87 -0
  104. package/src/tree/declaration.ts +742 -0
  105. package/src/tree/default-guard.ts +35 -0
  106. package/src/tree/dimension.ts +392 -0
  107. package/src/tree/expression.ts +97 -0
  108. package/src/tree/extend-list.ts +51 -0
  109. package/src/tree/extend.ts +391 -0
  110. package/src/tree/function.ts +254 -0
  111. package/src/tree/import-js.ts +130 -0
  112. package/src/tree/import-style.ts +875 -0
  113. package/{lib/tree/index.js → src/tree/index.ts} +49 -22
  114. package/src/tree/interpolated.ts +346 -0
  115. package/src/tree/js-array.ts +21 -0
  116. package/src/tree/js-expr.ts +50 -0
  117. package/src/tree/js-function.ts +31 -0
  118. package/src/tree/js-object.ts +22 -0
  119. package/src/tree/list.ts +415 -0
  120. package/src/tree/log.ts +89 -0
  121. package/src/tree/mixin.ts +331 -0
  122. package/src/tree/negative.ts +58 -0
  123. package/src/tree/nil.ts +57 -0
  124. package/src/tree/node-base.ts +1716 -0
  125. package/src/tree/node-type.ts +122 -0
  126. package/src/tree/node.ts +118 -0
  127. package/src/tree/number.ts +54 -0
  128. package/src/tree/operation.ts +187 -0
  129. package/src/tree/paren.ts +132 -0
  130. package/src/tree/query-condition.ts +47 -0
  131. package/src/tree/quoted.ts +119 -0
  132. package/src/tree/range.ts +101 -0
  133. package/src/tree/reference.ts +1099 -0
  134. package/src/tree/rest.ts +55 -0
  135. package/src/tree/rules-raw.ts +52 -0
  136. package/src/tree/rules.ts +2896 -0
  137. package/src/tree/ruleset.ts +1217 -0
  138. package/src/tree/selector-attr.ts +172 -0
  139. package/src/tree/selector-basic.ts +75 -0
  140. package/src/tree/selector-capture.ts +85 -0
  141. package/src/tree/selector-complex.ts +189 -0
  142. package/src/tree/selector-compound.ts +205 -0
  143. package/src/tree/selector-interpolated.ts +95 -0
  144. package/src/tree/selector-list.ts +245 -0
  145. package/src/tree/selector-pseudo.ts +173 -0
  146. package/src/tree/selector-simple.ts +10 -0
  147. package/src/tree/selector.ts +152 -0
  148. package/src/tree/sequence.ts +463 -0
  149. package/src/tree/tree.ts +130 -0
  150. package/src/tree/url.ts +95 -0
  151. package/src/tree/util/EXTEND_ARCHITECTURE_ANALYSIS.md +215 -0
  152. package/src/tree/util/EXTEND_AUDIT.md +233 -0
  153. package/src/tree/util/EXTEND_BASELINE.md +64 -0
  154. package/src/tree/util/EXTEND_CALL_GRAPH_ANALYSIS.md +244 -0
  155. package/src/tree/util/EXTEND_DOCS.md +24 -0
  156. package/src/tree/util/EXTEND_FINAL_SUMMARY.md +95 -0
  157. package/src/tree/util/EXTEND_FUNCTION_AUDIT.md +1433 -0
  158. package/src/tree/util/EXTEND_OPTIMIZATION_PLAN.md +114 -0
  159. package/src/tree/util/EXTEND_REFACTORING_SUMMARY.md +152 -0
  160. package/src/tree/util/EXTEND_RULES.md +74 -0
  161. package/src/tree/util/EXTEND_UNUSED_FUNCTIONS.md +127 -0
  162. package/src/tree/util/EXTEND_UNUSED_FUNCTIONS_ANALYSIS.md +227 -0
  163. package/src/tree/util/NODE_COPY_REDUCTION_PLAN.md +12 -0
  164. package/src/tree/util/__tests__/EXTEND_TEST_INDEX.md +59 -0
  165. package/src/tree/util/__tests__/OPTIMIZATION-ANALYSIS.md +130 -0
  166. package/src/tree/util/__tests__/WALK_AND_CONSUME_DESIGN.md +138 -0
  167. package/src/tree/util/__tests__/_archive/2026-02-09__OPTIMIZATION-ANALYSIS.md +9 -0
  168. package/src/tree/util/__tests__/_archive/README.md +4 -0
  169. package/src/tree/util/__tests__/bitset.test.ts +142 -0
  170. package/src/tree/util/__tests__/debug-log.ts +50 -0
  171. package/src/tree/util/__tests__/extend-comment-handling.test.ts +187 -0
  172. package/src/tree/util/__tests__/extend-core-unit.test.ts +941 -0
  173. package/src/tree/util/__tests__/extend-pipeline-bench.test.ts +154 -0
  174. package/src/tree/util/__tests__/extend-pipeline-bench.ts +190 -0
  175. package/src/tree/util/__tests__/fast-reject.test.ts +377 -0
  176. package/src/tree/util/__tests__/is-node.test.ts +63 -0
  177. package/src/tree/util/__tests__/list-like.test.ts +63 -0
  178. package/src/tree/util/__tests__/outputwriter.test.ts +523 -0
  179. package/src/tree/util/__tests__/print.test.ts +183 -0
  180. package/src/tree/util/__tests__/process-extends.test.ts +226 -0
  181. package/src/tree/util/__tests__/process-leading-is.test.ts +205 -0
  182. package/src/tree/util/__tests__/recursion-helper.test.ts +184 -0
  183. package/src/tree/util/__tests__/selector-match-unit.test.ts +1427 -0
  184. package/src/tree/util/__tests__/sourcemap.test.ts +117 -0
  185. package/src/tree/util/ampersand-template.ts +9 -0
  186. package/src/tree/util/bitset.ts +194 -0
  187. package/src/tree/util/calculate.ts +11 -0
  188. package/src/tree/util/cast.ts +89 -0
  189. package/src/tree/util/cloning.ts +8 -0
  190. package/src/tree/util/collections.ts +299 -0
  191. package/src/tree/util/compare.ts +90 -0
  192. package/src/tree/util/cursor.ts +171 -0
  193. package/src/tree/util/extend-core.ts +2139 -0
  194. package/src/tree/util/extend-roots.ts +1108 -0
  195. package/src/tree/util/field-helpers.ts +354 -0
  196. package/src/tree/util/is-node.ts +43 -0
  197. package/src/tree/util/list-like.ts +93 -0
  198. package/src/tree/util/mixin-instance-primitives.ts +2020 -0
  199. package/src/tree/util/print.ts +303 -0
  200. package/src/tree/util/process-leading-is.ts +421 -0
  201. package/src/tree/util/recursion-helper.ts +54 -0
  202. package/src/tree/util/regex.ts +2 -0
  203. package/src/tree/util/registry-utils.ts +1953 -0
  204. package/src/tree/util/ruleset-trace.ts +17 -0
  205. package/src/tree/util/scoped-body-eval.ts +320 -0
  206. package/src/tree/util/selector-match-core.ts +2005 -0
  207. package/src/tree/util/selector-utils.ts +757 -0
  208. package/src/tree/util/serialize-helper.ts +535 -0
  209. package/src/tree/util/serialize-types.ts +318 -0
  210. package/src/tree/util/should-operate.ts +78 -0
  211. package/src/tree/util/sourcemap.ts +37 -0
  212. package/src/types/config.ts +247 -0
  213. package/src/types/index.ts +12 -0
  214. package/{lib/types/modes.d.ts → src/types/modes.ts} +2 -1
  215. package/src/types.d.ts +9 -0
  216. package/src/types.ts +68 -0
  217. package/src/use-webpack-resolver.ts +56 -0
  218. package/src/visitor/__tests__/visitor.test.ts +136 -0
  219. package/src/visitor/index.ts +263 -0
  220. package/{lib/visitor/less-visitor.js → src/visitor/less-visitor.ts} +3 -2
  221. package/lib/context.d.ts +0 -352
  222. package/lib/context.d.ts.map +0 -1
  223. package/lib/context.js +0 -636
  224. package/lib/context.js.map +0 -1
  225. package/lib/conversions.d.ts +0 -73
  226. package/lib/conversions.d.ts.map +0 -1
  227. package/lib/conversions.js +0 -253
  228. package/lib/conversions.js.map +0 -1
  229. package/lib/debug-log.d.ts +0 -2
  230. package/lib/debug-log.d.ts.map +0 -1
  231. package/lib/debug-log.js +0 -27
  232. package/lib/debug-log.js.map +0 -1
  233. package/lib/define-function.d.ts +0 -587
  234. package/lib/define-function.d.ts.map +0 -1
  235. package/lib/define-function.js +0 -726
  236. package/lib/define-function.js.map +0 -1
  237. package/lib/deprecation.d.ts +0 -34
  238. package/lib/deprecation.d.ts.map +0 -1
  239. package/lib/deprecation.js +0 -57
  240. package/lib/deprecation.js.map +0 -1
  241. package/lib/jess-error.d.ts +0 -343
  242. package/lib/jess-error.d.ts.map +0 -1
  243. package/lib/jess-error.js +0 -508
  244. package/lib/jess-error.js.map +0 -1
  245. package/lib/logger/deprecation-processing.d.ts +0 -41
  246. package/lib/logger/deprecation-processing.d.ts.map +0 -1
  247. package/lib/logger/deprecation-processing.js +0 -81
  248. package/lib/logger/deprecation-processing.js.map +0 -1
  249. package/lib/logger.d.ts +0 -10
  250. package/lib/logger.d.ts.map +0 -1
  251. package/lib/logger.js +0 -20
  252. package/lib/logger.js.map +0 -1
  253. package/lib/plugin.d.ts +0 -94
  254. package/lib/plugin.d.ts.map +0 -1
  255. package/lib/plugin.js +0 -174
  256. package/lib/plugin.js.map +0 -1
  257. package/lib/tree/ampersand.d.ts +0 -98
  258. package/lib/tree/ampersand.d.ts.map +0 -1
  259. package/lib/tree/ampersand.js +0 -319
  260. package/lib/tree/ampersand.js.map +0 -1
  261. package/lib/tree/any.d.ts +0 -58
  262. package/lib/tree/any.d.ts.map +0 -1
  263. package/lib/tree/any.js +0 -104
  264. package/lib/tree/any.js.map +0 -1
  265. package/lib/tree/at-rule.d.ts +0 -53
  266. package/lib/tree/at-rule.d.ts.map +0 -1
  267. package/lib/tree/at-rule.js +0 -503
  268. package/lib/tree/at-rule.js.map +0 -1
  269. package/lib/tree/block.d.ts +0 -22
  270. package/lib/tree/block.d.ts.map +0 -1
  271. package/lib/tree/block.js +0 -24
  272. package/lib/tree/block.js.map +0 -1
  273. package/lib/tree/bool.d.ts +0 -18
  274. package/lib/tree/bool.d.ts.map +0 -1
  275. package/lib/tree/bool.js +0 -28
  276. package/lib/tree/bool.js.map +0 -1
  277. package/lib/tree/call.d.ts +0 -66
  278. package/lib/tree/call.d.ts.map +0 -1
  279. package/lib/tree/call.js +0 -306
  280. package/lib/tree/call.js.map +0 -1
  281. package/lib/tree/collection.d.ts +0 -30
  282. package/lib/tree/collection.d.ts.map +0 -1
  283. package/lib/tree/collection.js +0 -37
  284. package/lib/tree/collection.js.map +0 -1
  285. package/lib/tree/color.d.ts +0 -101
  286. package/lib/tree/color.d.ts.map +0 -1
  287. package/lib/tree/color.js +0 -513
  288. package/lib/tree/color.js.map +0 -1
  289. package/lib/tree/combinator.d.ts +0 -13
  290. package/lib/tree/combinator.d.ts.map +0 -1
  291. package/lib/tree/combinator.js +0 -12
  292. package/lib/tree/combinator.js.map +0 -1
  293. package/lib/tree/comment.d.ts +0 -20
  294. package/lib/tree/comment.d.ts.map +0 -1
  295. package/lib/tree/comment.js +0 -19
  296. package/lib/tree/comment.js.map +0 -1
  297. package/lib/tree/condition.d.ts +0 -31
  298. package/lib/tree/condition.d.ts.map +0 -1
  299. package/lib/tree/condition.js +0 -103
  300. package/lib/tree/condition.js.map +0 -1
  301. package/lib/tree/control.d.ts +0 -104
  302. package/lib/tree/control.d.ts.map +0 -1
  303. package/lib/tree/control.js +0 -430
  304. package/lib/tree/control.js.map +0 -1
  305. package/lib/tree/declaration-custom.d.ts +0 -18
  306. package/lib/tree/declaration-custom.d.ts.map +0 -1
  307. package/lib/tree/declaration-custom.js +0 -24
  308. package/lib/tree/declaration-custom.js.map +0 -1
  309. package/lib/tree/declaration-var.d.ts +0 -35
  310. package/lib/tree/declaration-var.d.ts.map +0 -1
  311. package/lib/tree/declaration-var.js +0 -63
  312. package/lib/tree/declaration-var.js.map +0 -1
  313. package/lib/tree/declaration.d.ts +0 -78
  314. package/lib/tree/declaration.d.ts.map +0 -1
  315. package/lib/tree/declaration.js +0 -286
  316. package/lib/tree/declaration.js.map +0 -1
  317. package/lib/tree/default-guard.d.ts +0 -15
  318. package/lib/tree/default-guard.d.ts.map +0 -1
  319. package/lib/tree/default-guard.js +0 -19
  320. package/lib/tree/default-guard.js.map +0 -1
  321. package/lib/tree/dimension.d.ts +0 -34
  322. package/lib/tree/dimension.d.ts.map +0 -1
  323. package/lib/tree/dimension.js +0 -294
  324. package/lib/tree/dimension.js.map +0 -1
  325. package/lib/tree/expression.d.ts +0 -25
  326. package/lib/tree/expression.d.ts.map +0 -1
  327. package/lib/tree/expression.js +0 -32
  328. package/lib/tree/expression.js.map +0 -1
  329. package/lib/tree/extend-list.d.ts +0 -23
  330. package/lib/tree/extend-list.d.ts.map +0 -1
  331. package/lib/tree/extend-list.js +0 -23
  332. package/lib/tree/extend-list.js.map +0 -1
  333. package/lib/tree/extend.d.ts +0 -47
  334. package/lib/tree/extend.d.ts.map +0 -1
  335. package/lib/tree/extend.js +0 -296
  336. package/lib/tree/extend.js.map +0 -1
  337. package/lib/tree/function.d.ts +0 -48
  338. package/lib/tree/function.d.ts.map +0 -1
  339. package/lib/tree/function.js +0 -74
  340. package/lib/tree/function.js.map +0 -1
  341. package/lib/tree/import-js.d.ts +0 -35
  342. package/lib/tree/import-js.d.ts.map +0 -1
  343. package/lib/tree/import-js.js +0 -45
  344. package/lib/tree/import-js.js.map +0 -1
  345. package/lib/tree/import-style.d.ts +0 -156
  346. package/lib/tree/import-style.d.ts.map +0 -1
  347. package/lib/tree/import-style.js +0 -566
  348. package/lib/tree/import-style.js.map +0 -1
  349. package/lib/tree/index.d.ts +0 -71
  350. package/lib/tree/index.d.ts.map +0 -1
  351. package/lib/tree/index.js.map +0 -1
  352. package/lib/tree/interpolated-reference.d.ts +0 -24
  353. package/lib/tree/interpolated-reference.d.ts.map +0 -1
  354. package/lib/tree/interpolated-reference.js +0 -37
  355. package/lib/tree/interpolated-reference.js.map +0 -1
  356. package/lib/tree/interpolated.d.ts +0 -62
  357. package/lib/tree/interpolated.d.ts.map +0 -1
  358. package/lib/tree/interpolated.js +0 -204
  359. package/lib/tree/interpolated.js.map +0 -1
  360. package/lib/tree/js-array.d.ts +0 -10
  361. package/lib/tree/js-array.d.ts.map +0 -1
  362. package/lib/tree/js-array.js +0 -10
  363. package/lib/tree/js-array.js.map +0 -1
  364. package/lib/tree/js-expr.d.ts +0 -23
  365. package/lib/tree/js-expr.d.ts.map +0 -1
  366. package/lib/tree/js-expr.js +0 -28
  367. package/lib/tree/js-expr.js.map +0 -1
  368. package/lib/tree/js-function.d.ts +0 -20
  369. package/lib/tree/js-function.d.ts.map +0 -1
  370. package/lib/tree/js-function.js +0 -16
  371. package/lib/tree/js-function.js.map +0 -1
  372. package/lib/tree/js-object.d.ts +0 -10
  373. package/lib/tree/js-object.d.ts.map +0 -1
  374. package/lib/tree/js-object.js +0 -10
  375. package/lib/tree/js-object.js.map +0 -1
  376. package/lib/tree/list.d.ts +0 -38
  377. package/lib/tree/list.d.ts.map +0 -1
  378. package/lib/tree/list.js +0 -83
  379. package/lib/tree/list.js.map +0 -1
  380. package/lib/tree/log.d.ts +0 -29
  381. package/lib/tree/log.d.ts.map +0 -1
  382. package/lib/tree/log.js +0 -56
  383. package/lib/tree/log.js.map +0 -1
  384. package/lib/tree/mixin.d.ts +0 -87
  385. package/lib/tree/mixin.d.ts.map +0 -1
  386. package/lib/tree/mixin.js +0 -112
  387. package/lib/tree/mixin.js.map +0 -1
  388. package/lib/tree/negative.d.ts +0 -17
  389. package/lib/tree/negative.d.ts.map +0 -1
  390. package/lib/tree/negative.js +0 -22
  391. package/lib/tree/negative.js.map +0 -1
  392. package/lib/tree/nil.d.ts +0 -30
  393. package/lib/tree/nil.d.ts.map +0 -1
  394. package/lib/tree/nil.js +0 -35
  395. package/lib/tree/nil.js.map +0 -1
  396. package/lib/tree/node-base.d.ts +0 -361
  397. package/lib/tree/node-base.d.ts.map +0 -1
  398. package/lib/tree/node-base.js +0 -930
  399. package/lib/tree/node-base.js.map +0 -1
  400. package/lib/tree/node.d.ts +0 -10
  401. package/lib/tree/node.d.ts.map +0 -1
  402. package/lib/tree/node.js +0 -45
  403. package/lib/tree/node.js.map +0 -1
  404. package/lib/tree/number.d.ts +0 -21
  405. package/lib/tree/number.d.ts.map +0 -1
  406. package/lib/tree/number.js +0 -27
  407. package/lib/tree/number.js.map +0 -1
  408. package/lib/tree/operation.d.ts +0 -26
  409. package/lib/tree/operation.d.ts.map +0 -1
  410. package/lib/tree/operation.js +0 -103
  411. package/lib/tree/operation.js.map +0 -1
  412. package/lib/tree/paren.d.ts +0 -19
  413. package/lib/tree/paren.d.ts.map +0 -1
  414. package/lib/tree/paren.js +0 -92
  415. package/lib/tree/paren.js.map +0 -1
  416. package/lib/tree/query-condition.d.ts +0 -17
  417. package/lib/tree/query-condition.d.ts.map +0 -1
  418. package/lib/tree/query-condition.js +0 -39
  419. package/lib/tree/query-condition.js.map +0 -1
  420. package/lib/tree/quoted.d.ts +0 -28
  421. package/lib/tree/quoted.d.ts.map +0 -1
  422. package/lib/tree/quoted.js +0 -75
  423. package/lib/tree/quoted.js.map +0 -1
  424. package/lib/tree/range.d.ts +0 -33
  425. package/lib/tree/range.d.ts.map +0 -1
  426. package/lib/tree/range.js +0 -47
  427. package/lib/tree/range.js.map +0 -1
  428. package/lib/tree/reference.d.ts +0 -76
  429. package/lib/tree/reference.d.ts.map +0 -1
  430. package/lib/tree/reference.js +0 -521
  431. package/lib/tree/reference.js.map +0 -1
  432. package/lib/tree/rest.d.ts +0 -15
  433. package/lib/tree/rest.d.ts.map +0 -1
  434. package/lib/tree/rest.js +0 -32
  435. package/lib/tree/rest.js.map +0 -1
  436. package/lib/tree/rules-raw.d.ts +0 -17
  437. package/lib/tree/rules-raw.d.ts.map +0 -1
  438. package/lib/tree/rules-raw.js +0 -37
  439. package/lib/tree/rules-raw.js.map +0 -1
  440. package/lib/tree/rules.d.ts +0 -262
  441. package/lib/tree/rules.d.ts.map +0 -1
  442. package/lib/tree/rules.js +0 -2359
  443. package/lib/tree/rules.js.map +0 -1
  444. package/lib/tree/ruleset.d.ts +0 -92
  445. package/lib/tree/ruleset.d.ts.map +0 -1
  446. package/lib/tree/ruleset.js +0 -528
  447. package/lib/tree/ruleset.js.map +0 -1
  448. package/lib/tree/selector-attr.d.ts +0 -31
  449. package/lib/tree/selector-attr.d.ts.map +0 -1
  450. package/lib/tree/selector-attr.js +0 -99
  451. package/lib/tree/selector-attr.js.map +0 -1
  452. package/lib/tree/selector-basic.d.ts +0 -24
  453. package/lib/tree/selector-basic.d.ts.map +0 -1
  454. package/lib/tree/selector-basic.js +0 -38
  455. package/lib/tree/selector-basic.js.map +0 -1
  456. package/lib/tree/selector-capture.d.ts +0 -23
  457. package/lib/tree/selector-capture.d.ts.map +0 -1
  458. package/lib/tree/selector-capture.js +0 -34
  459. package/lib/tree/selector-capture.js.map +0 -1
  460. package/lib/tree/selector-complex.d.ts +0 -40
  461. package/lib/tree/selector-complex.d.ts.map +0 -1
  462. package/lib/tree/selector-complex.js +0 -143
  463. package/lib/tree/selector-complex.js.map +0 -1
  464. package/lib/tree/selector-compound.d.ts +0 -16
  465. package/lib/tree/selector-compound.d.ts.map +0 -1
  466. package/lib/tree/selector-compound.js +0 -114
  467. package/lib/tree/selector-compound.js.map +0 -1
  468. package/lib/tree/selector-interpolated.d.ts +0 -23
  469. package/lib/tree/selector-interpolated.d.ts.map +0 -1
  470. package/lib/tree/selector-interpolated.js +0 -27
  471. package/lib/tree/selector-interpolated.js.map +0 -1
  472. package/lib/tree/selector-list.d.ts +0 -17
  473. package/lib/tree/selector-list.d.ts.map +0 -1
  474. package/lib/tree/selector-list.js +0 -174
  475. package/lib/tree/selector-list.js.map +0 -1
  476. package/lib/tree/selector-pseudo.d.ts +0 -42
  477. package/lib/tree/selector-pseudo.d.ts.map +0 -1
  478. package/lib/tree/selector-pseudo.js +0 -204
  479. package/lib/tree/selector-pseudo.js.map +0 -1
  480. package/lib/tree/selector-simple.d.ts +0 -5
  481. package/lib/tree/selector-simple.d.ts.map +0 -1
  482. package/lib/tree/selector-simple.js +0 -6
  483. package/lib/tree/selector-simple.js.map +0 -1
  484. package/lib/tree/selector.d.ts +0 -43
  485. package/lib/tree/selector.d.ts.map +0 -1
  486. package/lib/tree/selector.js +0 -56
  487. package/lib/tree/selector.js.map +0 -1
  488. package/lib/tree/sequence.d.ts +0 -43
  489. package/lib/tree/sequence.d.ts.map +0 -1
  490. package/lib/tree/sequence.js +0 -151
  491. package/lib/tree/sequence.js.map +0 -1
  492. package/lib/tree/tree.d.ts +0 -87
  493. package/lib/tree/tree.d.ts.map +0 -1
  494. package/lib/tree/tree.js +0 -2
  495. package/lib/tree/tree.js.map +0 -1
  496. package/lib/tree/url.d.ts +0 -18
  497. package/lib/tree/url.d.ts.map +0 -1
  498. package/lib/tree/url.js +0 -35
  499. package/lib/tree/url.js.map +0 -1
  500. package/lib/tree/util/__tests__/debug-log.d.ts +0 -1
  501. package/lib/tree/util/__tests__/debug-log.d.ts.map +0 -1
  502. package/lib/tree/util/__tests__/debug-log.js +0 -36
  503. package/lib/tree/util/__tests__/debug-log.js.map +0 -1
  504. package/lib/tree/util/calculate.d.ts +0 -3
  505. package/lib/tree/util/calculate.d.ts.map +0 -1
  506. package/lib/tree/util/calculate.js +0 -10
  507. package/lib/tree/util/calculate.js.map +0 -1
  508. package/lib/tree/util/cast.d.ts +0 -10
  509. package/lib/tree/util/cast.d.ts.map +0 -1
  510. package/lib/tree/util/cast.js +0 -87
  511. package/lib/tree/util/cast.js.map +0 -1
  512. package/lib/tree/util/cloning.d.ts +0 -4
  513. package/lib/tree/util/cloning.d.ts.map +0 -1
  514. package/lib/tree/util/cloning.js +0 -8
  515. package/lib/tree/util/cloning.js.map +0 -1
  516. package/lib/tree/util/collections.d.ts +0 -57
  517. package/lib/tree/util/collections.d.ts.map +0 -1
  518. package/lib/tree/util/collections.js +0 -136
  519. package/lib/tree/util/collections.js.map +0 -1
  520. package/lib/tree/util/compare.d.ts +0 -11
  521. package/lib/tree/util/compare.d.ts.map +0 -1
  522. package/lib/tree/util/compare.js +0 -89
  523. package/lib/tree/util/compare.js.map +0 -1
  524. package/lib/tree/util/extend-helpers.d.ts +0 -2
  525. package/lib/tree/util/extend-helpers.d.ts.map +0 -1
  526. package/lib/tree/util/extend-helpers.js +0 -2
  527. package/lib/tree/util/extend-helpers.js.map +0 -1
  528. package/lib/tree/util/extend-roots.d.ts +0 -37
  529. package/lib/tree/util/extend-roots.d.ts.map +0 -1
  530. package/lib/tree/util/extend-roots.js +0 -700
  531. package/lib/tree/util/extend-roots.js.map +0 -1
  532. package/lib/tree/util/extend-roots.old.d.ts +0 -132
  533. package/lib/tree/util/extend-roots.old.d.ts.map +0 -1
  534. package/lib/tree/util/extend-roots.old.js +0 -2272
  535. package/lib/tree/util/extend-roots.old.js.map +0 -1
  536. package/lib/tree/util/extend-trace-debug.d.ts +0 -13
  537. package/lib/tree/util/extend-trace-debug.d.ts.map +0 -1
  538. package/lib/tree/util/extend-trace-debug.js +0 -34
  539. package/lib/tree/util/extend-trace-debug.js.map +0 -1
  540. package/lib/tree/util/extend-walk.d.ts +0 -53
  541. package/lib/tree/util/extend-walk.d.ts.map +0 -1
  542. package/lib/tree/util/extend-walk.js +0 -881
  543. package/lib/tree/util/extend-walk.js.map +0 -1
  544. package/lib/tree/util/extend.d.ts +0 -218
  545. package/lib/tree/util/extend.d.ts.map +0 -1
  546. package/lib/tree/util/extend.js +0 -3182
  547. package/lib/tree/util/extend.js.map +0 -1
  548. package/lib/tree/util/find-extendable-locations.d.ts +0 -2
  549. package/lib/tree/util/find-extendable-locations.d.ts.map +0 -1
  550. package/lib/tree/util/find-extendable-locations.js +0 -2
  551. package/lib/tree/util/find-extendable-locations.js.map +0 -1
  552. package/lib/tree/util/format.d.ts +0 -20
  553. package/lib/tree/util/format.d.ts.map +0 -1
  554. package/lib/tree/util/format.js +0 -67
  555. package/lib/tree/util/format.js.map +0 -1
  556. package/lib/tree/util/is-node.d.ts +0 -13
  557. package/lib/tree/util/is-node.d.ts.map +0 -1
  558. package/lib/tree/util/is-node.js +0 -43
  559. package/lib/tree/util/is-node.js.map +0 -1
  560. package/lib/tree/util/print.d.ts +0 -80
  561. package/lib/tree/util/print.d.ts.map +0 -1
  562. package/lib/tree/util/print.js +0 -205
  563. package/lib/tree/util/print.js.map +0 -1
  564. package/lib/tree/util/process-leading-is.d.ts +0 -25
  565. package/lib/tree/util/process-leading-is.d.ts.map +0 -1
  566. package/lib/tree/util/process-leading-is.js +0 -364
  567. package/lib/tree/util/process-leading-is.js.map +0 -1
  568. package/lib/tree/util/recursion-helper.d.ts +0 -15
  569. package/lib/tree/util/recursion-helper.d.ts.map +0 -1
  570. package/lib/tree/util/recursion-helper.js +0 -43
  571. package/lib/tree/util/recursion-helper.js.map +0 -1
  572. package/lib/tree/util/regex.d.ts +0 -4
  573. package/lib/tree/util/regex.d.ts.map +0 -1
  574. package/lib/tree/util/regex.js +0 -4
  575. package/lib/tree/util/regex.js.map +0 -1
  576. package/lib/tree/util/registry-utils.d.ts +0 -192
  577. package/lib/tree/util/registry-utils.d.ts.map +0 -1
  578. package/lib/tree/util/registry-utils.js +0 -1214
  579. package/lib/tree/util/registry-utils.js.map +0 -1
  580. package/lib/tree/util/ruleset-trace.d.ts +0 -4
  581. package/lib/tree/util/ruleset-trace.d.ts.map +0 -1
  582. package/lib/tree/util/ruleset-trace.js +0 -14
  583. package/lib/tree/util/ruleset-trace.js.map +0 -1
  584. package/lib/tree/util/selector-compare.d.ts +0 -2
  585. package/lib/tree/util/selector-compare.d.ts.map +0 -1
  586. package/lib/tree/util/selector-compare.js +0 -2
  587. package/lib/tree/util/selector-compare.js.map +0 -1
  588. package/lib/tree/util/selector-match-core.d.ts +0 -184
  589. package/lib/tree/util/selector-match-core.d.ts.map +0 -1
  590. package/lib/tree/util/selector-match-core.js +0 -1603
  591. package/lib/tree/util/selector-match-core.js.map +0 -1
  592. package/lib/tree/util/selector-utils.d.ts +0 -30
  593. package/lib/tree/util/selector-utils.d.ts.map +0 -1
  594. package/lib/tree/util/selector-utils.js +0 -100
  595. package/lib/tree/util/selector-utils.js.map +0 -1
  596. package/lib/tree/util/serialize-helper.d.ts +0 -13
  597. package/lib/tree/util/serialize-helper.d.ts.map +0 -1
  598. package/lib/tree/util/serialize-helper.js +0 -387
  599. package/lib/tree/util/serialize-helper.js.map +0 -1
  600. package/lib/tree/util/serialize-types.d.ts +0 -9
  601. package/lib/tree/util/serialize-types.d.ts.map +0 -1
  602. package/lib/tree/util/serialize-types.js +0 -216
  603. package/lib/tree/util/serialize-types.js.map +0 -1
  604. package/lib/tree/util/should-operate.d.ts +0 -23
  605. package/lib/tree/util/should-operate.d.ts.map +0 -1
  606. package/lib/tree/util/should-operate.js +0 -46
  607. package/lib/tree/util/should-operate.js.map +0 -1
  608. package/lib/tree/util/sourcemap.d.ts +0 -7
  609. package/lib/tree/util/sourcemap.d.ts.map +0 -1
  610. package/lib/tree/util/sourcemap.js +0 -25
  611. package/lib/tree/util/sourcemap.js.map +0 -1
  612. package/lib/types/config.d.ts +0 -205
  613. package/lib/types/config.d.ts.map +0 -1
  614. package/lib/types/config.js +0 -2
  615. package/lib/types/config.js.map +0 -1
  616. package/lib/types/index.d.ts +0 -15
  617. package/lib/types/index.d.ts.map +0 -1
  618. package/lib/types/index.js +0 -3
  619. package/lib/types/index.js.map +0 -1
  620. package/lib/types/modes.d.ts.map +0 -1
  621. package/lib/types/modes.js +0 -2
  622. package/lib/types/modes.js.map +0 -1
  623. package/lib/types.d.ts +0 -61
  624. package/lib/types.d.ts.map +0 -1
  625. package/lib/types.js +0 -2
  626. package/lib/types.js.map +0 -1
  627. package/lib/use-webpack-resolver.d.ts +0 -9
  628. package/lib/use-webpack-resolver.d.ts.map +0 -1
  629. package/lib/use-webpack-resolver.js +0 -41
  630. package/lib/use-webpack-resolver.js.map +0 -1
  631. package/lib/visitor/index.d.ts +0 -136
  632. package/lib/visitor/index.d.ts.map +0 -1
  633. package/lib/visitor/index.js +0 -135
  634. package/lib/visitor/index.js.map +0 -1
  635. package/lib/visitor/less-visitor.d.ts +0 -7
  636. package/lib/visitor/less-visitor.d.ts.map +0 -1
  637. package/lib/visitor/less-visitor.js.map +0 -1
@@ -1,1603 +0,0 @@
1
- import { SelectorList } from '../selector-list.js';
2
- import { ComplexSelector } from '../selector-complex.js';
3
- import { CompoundSelector } from '../selector-compound.js';
4
- import { isNode } from './is-node.js';
5
- /**
6
- * Helper functions for extend operations that eliminate genuine code duplication
7
- * These preserve all original logic while extracting commonly repeated patterns
8
- */
9
- /**
10
- * Determines the extension type based on selector type and location context
11
- * Extracted from multiple places in extend.ts with preserved original logic
12
- */
13
- export function determineExtensionType(selector, basePath) {
14
- // If we're inside a pseudo-selector argument (like :where() or :is())
15
- if (basePath.some(segment => segment === 'arg')) {
16
- // Check if we're matching a component within a compound selector inside the argument
17
- // Path format: ['arg', selectorListIndex, compoundIndex, ...]
18
- // If we have at least 3 segments and the last numeric segment is a compound index,
19
- // we should wrap to preserve compound selector structure
20
- const numericSegments = basePath.filter((s) => typeof s === 'number');
21
- if (numericSegments.length >= 2) {
22
- // We're inside a compound selector - use 'wrap' to create :is() wrapper
23
- return 'wrap';
24
- }
25
- return 'append'; // Can append to pseudo-selector argument lists
26
- }
27
- // Check if we're matching a component within a compound selector
28
- // Path format: [compoundIndex, ...] where compoundIndex is a number
29
- // If the path starts with a number and we're in a compound selector context, use 'wrap'
30
- if (basePath.length > 0 && typeof basePath[0] === 'number') {
31
- // This could be a compound selector component match - check if selector is CompoundSelector
32
- // Actually, we can't check the selector type here, so we'll rely on the caller to set 'wrap'
33
- // For now, default to 'replace' for numeric paths
34
- }
35
- // If we're in a SelectorList context (not just any numeric path)
36
- // We need to check the context more carefully
37
- // Numeric paths can mean: SelectorList index, CompoundSelector index, or ComplexSelector index
38
- // Only SelectorList contexts should use 'append' - others should use 'replace'
39
- // For now, default to replace for all direct matches
40
- // The 'append' behavior should be handled by specialized logic in pseudo-selector handling
41
- return 'replace';
42
- }
43
- /**
44
- * Checks if a value can be treated as a selector
45
- * Extracted from multiple pseudo-selector checks
46
- */
47
- export function isSelector(value) {
48
- // Avoid `instanceof` (module identity can diverge under Vite/Vitest).
49
- // All selector nodes set `isSelector = true` on the base Selector class.
50
- return !!value && typeof value === 'object' && value.isSelector === true;
51
- }
52
- /**
53
- * Filters components to get only non-combinator selectors
54
- * This pattern appears in many complex selector algorithms
55
- */
56
- export function getNonCombinatorComponents(selector) {
57
- return selector.value.filter(c => !isNode(c, 'Combinator'));
58
- }
59
- /**
60
- * Filters components to get only combinators
61
- * Used in complex selector matching algorithms
62
- */
63
- export function getCombinatorComponents(selector) {
64
- return selector.value.filter(c => isNode(c, 'Combinator'));
65
- }
66
- /**
67
- * Checks if two selectors match using component-level logic
68
- * Preserves the exact original matching semantics from multiple locations
69
- */
70
- export function componentsMatch(a, b) {
71
- // Exact string match first (fast path)
72
- if (a.valueOf() === b.valueOf()) {
73
- return true;
74
- }
75
- // Handle compound selector equivalence (order-independent)
76
- if (isNode(a, 'CompoundSelector') && isNode(b, 'CompoundSelector')) {
77
- return areCompoundSelectorsEquivalent(a, b);
78
- }
79
- // Handle compound vs simple: compound contains simple (improved structural matching)
80
- if (isNode(a, 'CompoundSelector') && isNode(b, 'SimpleSelector')) {
81
- return a.value.some(comp => comp.valueOf() === b.valueOf());
82
- }
83
- // Handle simple vs compound: compound contains simple (improved structural matching)
84
- if (isNode(a, 'SimpleSelector') && isNode(b, 'CompoundSelector')) {
85
- return b.value.some(comp => comp.valueOf() === a.valueOf());
86
- }
87
- // Handle pseudo-selector equivalence
88
- if (isNode(a, 'PseudoSelector') && isNode(b, 'PseudoSelector')) {
89
- return a.value.name === b.value.name
90
- && areSelectorArgumentsEquivalent(a.value.arg, b.value.arg);
91
- }
92
- return false;
93
- }
94
- /**
95
- * Compound component semantic equivalence — pointer-based, no object creation.
96
- *
97
- * Walks the existing AST structure to answer "can `find` occupy this position in `target`?"
98
- * without expanding or rewriting either selector.
99
- *
100
- * Rules (mirrors the walk-and-consume algorithm):
101
- * - Direct match: find.valueOf() === target.valueOf()
102
- * - find is :is(...): any ONE alternative of find matches target (find provides alternatives)
103
- * - target is :is(...): any ONE alternative of target matches find (target provides alternatives)
104
- * - Both are non-:is() pseudo-selectors: delegate to arePseudoSelectorsEquivalent
105
- */
106
- export function compoundComponentMatches(find, target) {
107
- // Fast path
108
- if (find.valueOf() === target.valueOf()) {
109
- return true;
110
- }
111
- // find is :is(...) — walk its alternatives without creating new structures
112
- if (isNode(find, 'PseudoSelector') && find.value.name === ':is' && find.value.arg && isSelector(find.value.arg)) {
113
- const arg = find.value.arg;
114
- if (isNode(arg, 'SelectorList')) {
115
- return arg.value.some((alt) => compoundComponentMatches(alt, target));
116
- }
117
- return compoundComponentMatches(arg, target);
118
- }
119
- // target is :is(...) — walk its alternatives
120
- if (isNode(target, 'PseudoSelector') && target.value.name === ':is' && target.value.arg && isSelector(target.value.arg)) {
121
- const arg = target.value.arg;
122
- if (isNode(arg, 'SelectorList')) {
123
- return arg.value.some((alt) => compoundComponentMatches(find, alt));
124
- }
125
- return compoundComponentMatches(find, arg);
126
- }
127
- // Both are non-:is() pseudo-selectors
128
- if (isNode(find, 'PseudoSelector') && find.value.arg && isSelector(find.value.arg) && isNode(target, 'PseudoSelector')) {
129
- return arePseudoSelectorsEquivalent(find, target);
130
- }
131
- return false;
132
- }
133
- /**
134
- * Checks pseudo-selector equivalence including argument matching
135
- * Handles all pseudo-selectors with selector arguments, not just specific ones
136
- * Extracted from find-extendable-locations.ts with preserved original logic
137
- */
138
- export function arePseudoSelectorsEquivalent(a, b) {
139
- if (!isNode(a, 'PseudoSelector') || !isNode(b, 'PseudoSelector')) {
140
- return false;
141
- }
142
- if (a.value.name !== b.value.name) {
143
- return false;
144
- }
145
- const aArg = a.value.arg;
146
- const bArg = b.value.arg;
147
- if (!aArg && !bArg) {
148
- return true;
149
- }
150
- if (!aArg || !bArg) {
151
- return false;
152
- }
153
- // If both have selector arguments, check equivalence
154
- if (isSelector(aArg) && isSelector(bArg)) {
155
- return areSelectorArgumentsEquivalent(aArg, bArg);
156
- }
157
- // For non-selector arguments, use string comparison
158
- return String(aArg) === String(bArg);
159
- }
160
- /**
161
- * Checks equivalence of selector arguments in pseudo-selectors
162
- * Preserves complex original logic for :is(), :where(), etc.
163
- */
164
- export function areSelectorArgumentsEquivalent(a, b) {
165
- // Handle selector lists (order-independent)
166
- if (isNode(a, 'SelectorList') && isNode(b, 'SelectorList')) {
167
- if (a.value.length !== b.value.length) {
168
- return false;
169
- }
170
- return a.value.every(aItem => b.value.some(bItem => componentsMatch(aItem, bItem)));
171
- }
172
- // Handle compound selectors
173
- if (isNode(a, 'CompoundSelector') && isNode(b, 'CompoundSelector')) {
174
- return areCompoundSelectorsEquivalent(a, b);
175
- }
176
- // Default comparison
177
- return componentsMatch(a, b);
178
- }
179
- /**
180
- * Efficient compound selector equivalence check (order-independent)
181
- * Preserves exact original algorithm from find-extendable-locations.ts
182
- */
183
- /**
184
- * True when find's components appear in target in order (subsequence). Enables .a.c.b to match .a.b.
185
- */
186
- function compoundContainsCompoundSubsequence(target, find) {
187
- if (find.value.length > target.value.length) {
188
- return false;
189
- }
190
- const eq = (t, f) => compoundComponentMatches(f, t);
191
- let tIdx = 0;
192
- for (const fComp of find.value) {
193
- let found = false;
194
- while (tIdx < target.value.length) {
195
- if (eq(target.value[tIdx], fComp)) {
196
- tIdx++;
197
- found = true;
198
- break;
199
- }
200
- tIdx++;
201
- }
202
- if (!found) {
203
- return false;
204
- }
205
- }
206
- return true;
207
- }
208
- export function areCompoundSelectorsEquivalent(a, b) {
209
- if (a.value.length !== b.value.length) {
210
- return false;
211
- }
212
- // Order-independent component matching: two compounds are equivalent if they have the same
213
- // multiset of components. Components are small (typically 2-5), so O(N²) is fine.
214
- // Uses compoundComponentMatches for :is()-aware pointer walk — no object creation.
215
- return a.value.every(aComp => b.value.some(bComp => compoundComponentMatches(aComp, bComp)));
216
- }
217
- /**
218
- * Expands compound selectors by handling :is() pseudo-selectors
219
- * Preserves exact original expansion algorithm - only handles :is() specially
220
- */
221
- export function expandCompoundWithPseudoSelectors(compound) {
222
- const expansions = [compound];
223
- // Only expand :is() pseudo-selectors (preserving original logic)
224
- compound.value.forEach((component, index) => {
225
- if (isNode(component, 'PseudoSelector') && component.value.name === ':is' && component.value.arg && isSelector(component.value.arg)) {
226
- const arg = component.value.arg;
227
- // Handle :is() with compound selector argument
228
- if (isNode(arg, 'CompoundSelector')) {
229
- // Create new expansions by replacing :is() with its contents
230
- const newExpansions = [];
231
- expansions.forEach((expansion) => {
232
- const newComponents = [...expansion.value];
233
- newComponents.splice(index, 1, ...arg.value); // Replace :is() with its contents
234
- newExpansions.push(new CompoundSelector(newComponents));
235
- });
236
- expansions.push(...newExpansions);
237
- }
238
- else if (isNode(arg, 'SimpleSelector')) {
239
- // Handle :is() with simple selector argument
240
- const newExpansions = [];
241
- expansions.forEach((expansion) => {
242
- const newComponents = [...expansion.value];
243
- newComponents.splice(index, 1, arg); // Replace :is() with the simple selector
244
- newExpansions.push(new CompoundSelector(newComponents));
245
- });
246
- expansions.push(...newExpansions);
247
- }
248
- else if (isNode(arg, 'SelectorList')) {
249
- // Handle :is() with selector list argument
250
- const newExpansions = [];
251
- const listArg = arg;
252
- expansions.forEach((expansion) => {
253
- listArg.value.forEach((listItem) => {
254
- const newComponents = [...expansion.value];
255
- if (isNode(listItem, 'CompoundSelector')) {
256
- newComponents.splice(index, 1, ...listItem.value);
257
- }
258
- else {
259
- newComponents.splice(index, 1, listItem);
260
- }
261
- newExpansions.push(new CompoundSelector(newComponents));
262
- });
263
- });
264
- expansions.push(...newExpansions);
265
- }
266
- }
267
- });
268
- return expansions;
269
- }
270
- /**
271
- * Expands complex selectors containing :is() pseudo-selectors into equivalent selector lists
272
- * This handles cases like: a :is(b, c) -> a b, a c
273
- */
274
- export function expandComplexSelectorWithIs(complexSelector) {
275
- // Look for :is() pseudo-selectors in the complex selector
276
- let hasIsSelector = false;
277
- let isIndex = -1;
278
- let isArg = null;
279
- let isFromBareIsCompound = false;
280
- let isFromAmpersandSelector = false;
281
- for (let i = 0; i < complexSelector.value.length; i++) {
282
- const component = complexSelector.value[i];
283
- if (isNode(component, 'PseudoSelector') && component.value.name === ':is' && component.value.arg && isSelector(component.value.arg)) {
284
- hasIsSelector = true;
285
- isIndex = i;
286
- isArg = component.value.arg;
287
- break; // Handle first :is() found for now
288
- }
289
- // Also support the common case where `:is(...)` is wrapped in a single-item CompoundSelector
290
- // (e.g. `:is(.a, .b) .c`) so matching can expand alternatives.
291
- if (isNode(component, 'CompoundSelector') && component.value.length === 1) {
292
- const only = component.value[0];
293
- if (isNode(only, 'PseudoSelector') && only.value.name === ':is' && only.value.arg && isSelector(only.value.arg)) {
294
- hasIsSelector = true;
295
- isIndex = i;
296
- isArg = only.value.arg;
297
- isFromBareIsCompound = true;
298
- break; // Handle first :is() found for now
299
- }
300
- }
301
- // Also support the case where `:is(...)` is carried inside an implicit ampersand's resolved selector.
302
- // This shows up as a ComplexSelector beginning with Ampersand(selector=:is(...)).
303
- if (isNode(component, 'Ampersand')) {
304
- const sel = component.getResolvedSelector();
305
- if (sel && isNode(sel, 'PseudoSelector') && sel.value.name === ':is' && sel.value.arg && isSelector(sel.value.arg)) {
306
- hasIsSelector = true;
307
- isIndex = i;
308
- isArg = sel.value.arg;
309
- isFromAmpersandSelector = true;
310
- break;
311
- }
312
- if (sel && isNode(sel, 'CompoundSelector') && sel.value.length === 1) {
313
- const only = sel.value[0];
314
- if (isNode(only, 'PseudoSelector') && only.value.name === ':is' && only.value.arg && isSelector(only.value.arg)) {
315
- hasIsSelector = true;
316
- isIndex = i;
317
- isArg = only.value.arg;
318
- isFromAmpersandSelector = true;
319
- break;
320
- }
321
- }
322
- }
323
- }
324
- if (!hasIsSelector || !isArg) {
325
- return [complexSelector]; // No :is() found, return original
326
- }
327
- const results = [];
328
- // Get the list of alternatives from :is()
329
- const alternatives = isNode(isArg, 'SelectorList') ? isArg.value : [isArg];
330
- // For each alternative, create a new complex selector
331
- alternatives.forEach((alternative) => {
332
- const newComponents = [...complexSelector.value];
333
- if (isFromAmpersandSelector) {
334
- // Inline the resolved alternative directly so we do not reintroduce synthetic
335
- // ampersand nodes while expanding match candidates.
336
- if (isNode(alternative, 'ComplexSelector')) {
337
- newComponents.splice(isIndex, 1, ...alternative.value);
338
- }
339
- else {
340
- newComponents[isIndex] = alternative;
341
- }
342
- }
343
- else if (isFromBareIsCompound) {
344
- // The original `:is(...)` lived inside a CompoundSelector position. Replace that slot with the
345
- // alternative selector's components where possible.
346
- if (isNode(alternative, 'ComplexSelector')) {
347
- newComponents.splice(isIndex, 1, ...alternative.value);
348
- }
349
- else {
350
- newComponents[isIndex] = alternative;
351
- }
352
- }
353
- else {
354
- newComponents[isIndex] = alternative; // Replace :is() with the alternative
355
- }
356
- results.push(new ComplexSelector(newComponents).inherit(complexSelector));
357
- });
358
- return results;
359
- }
360
- /**
361
- * Expands any selector that might contain :is() into equivalent forms for comparison
362
- */
363
- export function expandSelectorWithIs(selector) {
364
- if (isNode(selector, 'ComplexSelector')) {
365
- return expandComplexSelectorWithIs(selector);
366
- }
367
- // For other types, check if they need expansion
368
- if (isNode(selector, 'CompoundSelector')) {
369
- const expansions = expandCompoundWithPseudoSelectors(selector);
370
- return expansions.length > 1 ? expansions : [selector];
371
- }
372
- return [selector]; // No expansion needed
373
- }
374
- /**
375
- * Creates a standardized path representation for selector tree navigation
376
- * Eliminates duplicate path building logic
377
- */
378
- export function buildSelectorPath(basePath, ...segments) {
379
- return [...basePath, ...segments];
380
- }
381
- /**
382
- * Checks if two complex selectors are equivalent using the original algorithm
383
- * Preserves exact combinator and component matching logic from find-extendable-locations.ts
384
- */
385
- export function areComplexSelectorsEquivalent(a, b) {
386
- if (a.value.length !== b.value.length) {
387
- return false;
388
- }
389
- // Check each component matches
390
- for (let i = 0; i < a.value.length; i++) {
391
- const aComp = a.value[i];
392
- const bComp = b.value[i];
393
- if (!aComp || !bComp) {
394
- return false;
395
- }
396
- // Both must be same type
397
- if (isNode(aComp, 'Combinator') && isNode(bComp, 'Combinator')) {
398
- if (aComp.value !== bComp.value) {
399
- return false;
400
- }
401
- }
402
- else if (!isNode(aComp, 'Combinator') && !isNode(bComp, 'Combinator')) {
403
- // Both are selectors - check equivalence
404
- if (isNode(aComp, 'CompoundSelector') && isNode(bComp, 'CompoundSelector')) {
405
- if (!areCompoundSelectorsEquivalent(aComp, bComp)) {
406
- return false;
407
- }
408
- }
409
- else if (isNode(aComp, 'PseudoSelector') && aComp.value.name === ':is' && aComp.value.arg && isSelector(aComp.value.arg)) {
410
- // Allow `:is(.a, .b)` to match `.a` (or any selector in its arg list) for complex selector equivalence.
411
- const arg = aComp.value.arg;
412
- if (isNode(arg, 'SelectorList')) {
413
- const matchesAny = arg.value.some(sel => sel.valueOf() === bComp.valueOf());
414
- if (!matchesAny) {
415
- return false;
416
- }
417
- }
418
- else {
419
- if (arg.valueOf() !== bComp.valueOf()) {
420
- return false;
421
- }
422
- }
423
- }
424
- else if (isNode(bComp, 'PseudoSelector') && bComp.value.name === ':is' && bComp.value.arg && isSelector(bComp.value.arg)) {
425
- // Symmetric case: allow `.a` to match `:is(.a, .b)`
426
- const arg = bComp.value.arg;
427
- if (isNode(arg, 'SelectorList')) {
428
- const matchesAny = arg.value.some(sel => sel.valueOf() === aComp.valueOf());
429
- if (!matchesAny) {
430
- return false;
431
- }
432
- }
433
- else {
434
- if (arg.valueOf() !== aComp.valueOf()) {
435
- return false;
436
- }
437
- }
438
- }
439
- else if (aComp.valueOf() !== bComp.valueOf()) {
440
- return false;
441
- }
442
- }
443
- else {
444
- // One is combinator, other is not
445
- return false;
446
- }
447
- }
448
- return true;
449
- }
450
- /**
451
- * Checks if two selectors are structurally equal (same type and content)
452
- * This is different from valueOf() comparison which might do normalization
453
- */
454
- export function isStructurallyEqual(a, b) {
455
- // For pseudo-selectors, compare name and arguments first (before basic selector check)
456
- if (isNode(a, 'PseudoSelector') && isNode(b, 'PseudoSelector')) {
457
- if (a.value.name !== b.value.name) {
458
- return false;
459
- }
460
- const aArg = a.value.arg;
461
- const bArg = b.value.arg;
462
- // Both have no args
463
- if (!aArg && !bArg) {
464
- return true;
465
- }
466
- // One has arg, other doesn't
467
- if (!aArg || !bArg) {
468
- return false;
469
- }
470
- // Both have args - compare them recursively
471
- if (isSelector(aArg) && isSelector(bArg)) {
472
- return isStructurallyEqual(aArg, bArg);
473
- }
474
- // Fallback to valueOf comparison for other arg types (non-selector nodes)
475
- return aArg.valueOf() === bArg.valueOf();
476
- }
477
- // For basic selectors (div, .foo, #bar) and other simple selectors, use valueOf comparison
478
- if (isNode(a, 'SimpleSelector') && isNode(b, 'SimpleSelector')) {
479
- return a.valueOf() === b.valueOf();
480
- }
481
- // For other selector types, use valueOf as fallback
482
- // This handles compound, complex, and selector list comparisons
483
- if (isNode(a, 'CompoundSelector') || isNode(a, 'ComplexSelector') || isNode(a, 'SelectorList')) {
484
- return a.valueOf() === b.valueOf();
485
- }
486
- // Default fallback
487
- return false;
488
- }
489
- function inferMatchScope(path, matchedNode) {
490
- if (path.includes('arg')) {
491
- return 'isArgument';
492
- }
493
- if (isNode(matchedNode, 'SelectorList')) {
494
- return 'selectorList';
495
- }
496
- return 'root';
497
- }
498
- function withMatchScope(location) {
499
- if (location.matchScope) {
500
- return location;
501
- }
502
- location.matchScope = inferMatchScope(location.path, location.matchedNode);
503
- return location;
504
- }
505
- // Performance optimization: Pre-allocated result cache
506
- const EXACT_MATCH_CACHE = new WeakMap();
507
- // General search result cache: WeakMap<target, Map<find, ExtendSearchResult>>
508
- const SEARCH_RESULT_CACHE = new WeakMap();
509
- const EMPTY_LOCATIONS = [];
510
- /**
511
- * Enhanced selector matching with 7-layer optimization system from matchSelectors
512
- * Recursively searches a selector tree to find all locations where a target selector appears
513
- * This is designed specifically for extend use cases with maximum performance
514
- *
515
- * @param target - The selector tree to search within
516
- * @param find - The selector pattern to find
517
- * @returns ExtendSearchResult with all found locations and performance optimizations
518
- */
519
- export function findExtendableLocations(target, find) {
520
- // Check general search result cache first
521
- let targetCache = SEARCH_RESULT_CACHE.get(target);
522
- if (targetCache) {
523
- const cached = targetCache.get(find);
524
- if (cached) {
525
- return cached;
526
- }
527
- }
528
- else {
529
- targetCache = new Map();
530
- SEARCH_RESULT_CACHE.set(target, targetCache);
531
- }
532
- const locations = [];
533
- const metrics = { fastRejections: 0, fastPathHits: 0, fullSearches: 0 };
534
- // OPTIMIZATION 1: Exact match cache for identical selectors
535
- const targetValue = target.valueOf();
536
- const findValue = find.valueOf();
537
- if (targetValue === findValue) {
538
- const cached = EXACT_MATCH_CACHE.get(target);
539
- if (cached) {
540
- const result = { locations: cached, hasMatches: cached.length > 0, hasWholeMatch: true, metrics };
541
- targetCache.set(find, result);
542
- return result;
543
- }
544
- // Cache the exact match result
545
- const exactLocation = withMatchScope({
546
- path: [],
547
- matchedNode: target,
548
- extensionType: 'replace'
549
- });
550
- EXACT_MATCH_CACHE.set(target, [exactLocation]);
551
- const result = { locations: [exactLocation], hasMatches: true, hasWholeMatch: true, metrics };
552
- targetCache.set(find, result);
553
- return result;
554
- }
555
- // OPTIMIZATION 2: KeySet fast rejection - bail early for impossible matches
556
- if (target.keySet && find.keySet
557
- && target.keySet.isDisjointFrom(find.keySet)
558
- && target.canFastReject && find.canFastReject) {
559
- metrics.fastRejections++;
560
- const result = { locations: EMPTY_LOCATIONS, hasMatches: false, hasWholeMatch: false, metrics };
561
- targetCache.set(find, result);
562
- return result;
563
- }
564
- // OPTIMIZATION 3: KeySet subset rejection for partial matching
565
- if (find.canFastReject && target.keySet && find.keySet
566
- && !find.keySet.isSubsetOf(target.keySet)) {
567
- metrics.fastRejections++;
568
- const result = { locations: EMPTY_LOCATIONS, hasMatches: false, hasWholeMatch: false, metrics };
569
- targetCache.set(find, result);
570
- return result;
571
- }
572
- // OPTIMIZATION 4: Fast path for common selector patterns - runs first and skips slow path when successful
573
- // Special case: Handle SelectorList in find parameter regardless of canFastReject
574
- if (isNode(find, 'SelectorList')) {
575
- // Check if target matches any item in the find list
576
- for (let i = 0; i < find.value.length; i++) {
577
- const listItem = find.value[i];
578
- const result = findExtendableLocations(target, listItem);
579
- if (result.hasMatches) {
580
- targetCache.set(find, result);
581
- return result;
582
- }
583
- }
584
- const result = { locations: EMPTY_LOCATIONS, hasMatches: false, hasWholeMatch: false, metrics };
585
- targetCache.set(find, result);
586
- return result;
587
- }
588
- if (target.canFastReject && find.canFastReject) {
589
- const fastPathResult = tryFastPathExtendMatch(target, find, []);
590
- if (fastPathResult && fastPathResult.length > 0) {
591
- metrics.fastPathHits++;
592
- const hasWholeMatch = fastPathResult.some(loc => loc.path.length === 0 && loc.matchedNode === target);
593
- const result = { locations: fastPathResult, hasMatches: true, hasWholeMatch, metrics };
594
- targetCache.set(find, result);
595
- return result;
596
- }
597
- }
598
- // Full recursive search with optimizations - only when fast path fails
599
- metrics.fullSearches++;
600
- searchWithinSelector(target, find, [], locations);
601
- const hasWholeMatch = locations.some(loc => loc.path.length === 0 && loc.matchedNode === target);
602
- const result = {
603
- locations,
604
- hasMatches: locations.length > 0,
605
- hasWholeMatch,
606
- metrics
607
- };
608
- targetCache.set(find, result);
609
- return result;
610
- }
611
- /**
612
- * Whether a ruleset's selector matches an extend target. Encapsulates all extend matching
613
- * semantics (keySet subset, valueOf early exit, partial vs exact). Extend-roots should
614
- * only decide which rulesets are visible; they hand off to this to determine matches.
615
- */
616
- export function selectorMatchesExtendTarget(selector, target, partial) {
617
- const keySet = target.keySet instanceof Set ? target.keySet : (target.keySet ? new Set(target.keySet) : undefined);
618
- if (keySet?.size && 'keySet' in selector && selector.keySet) {
619
- for (const k of keySet) {
620
- if (!selector.keySet.has(k)) {
621
- return false;
622
- }
623
- }
624
- }
625
- const targetValue = target.valueOf();
626
- if (typeof selector.valueOf === 'function' && selector.valueOf() === targetValue) {
627
- return true;
628
- }
629
- if (isNode(selector, 'SelectorList')) {
630
- return selector.value.some((item) => {
631
- const comparison = selectorCompare(item, target);
632
- return partial ? comparison.locations.length > 0 : comparison.hasWholeMatch;
633
- });
634
- }
635
- const comparison = selectorCompare(selector, target);
636
- return partial ? comparison.locations.length > 0 : comparison.hasWholeMatch;
637
- }
638
- /**
639
- * OPTIMIZATION 4: Fast path extend matching for common patterns
640
- * Handles the most frequent selector types in typical stylesheets with optimized logic
641
- * Now comprehensive enough to skip slow path for most common cases
642
- */
643
- function tryFastPathExtendMatch(target, find, basePath) {
644
- // Fast path 1: Exact match (most common case)
645
- if (target.valueOf() === find.valueOf()) {
646
- return [withMatchScope({
647
- path: [...basePath],
648
- matchedNode: target,
649
- extensionType: determineExtensionType(target, basePath)
650
- })];
651
- }
652
- // Fast path 2: Simple selector to simple selector (.foo === .foo)
653
- if (isNode(target, 'SimpleSelector') && isNode(find, 'SimpleSelector')) {
654
- // Handle pseudo-selectors with selector arguments using enhanced equivalence
655
- if (isNode(target, 'PseudoSelector') && isNode(find, 'PseudoSelector')
656
- && target.value.name === find.value.name
657
- && target.value.arg && isSelector(target.value.arg)
658
- && find.value.arg && isSelector(find.value.arg)) {
659
- // Same pseudo-selector name with selector args - check if args are equivalent
660
- if (areSelectorArgumentsEquivalent(target.value.arg, find.value.arg)) {
661
- return [withMatchScope({
662
- path: [...basePath],
663
- matchedNode: target,
664
- extensionType: determineExtensionType(target, basePath)
665
- })];
666
- }
667
- return [];
668
- }
669
- if (target.valueOf() === find.valueOf()) {
670
- return [withMatchScope({
671
- path: [...basePath],
672
- matchedNode: target,
673
- extensionType: determineExtensionType(target, basePath)
674
- })];
675
- }
676
- return [];
677
- }
678
- // Fast path 3: Compound selector containing simple target (.foo.bar contains .foo)
679
- if (isNode(target, 'CompoundSelector') && isNode(find, 'SimpleSelector') && target.value.length <= 4) {
680
- // Skip pseudo-selectors with Selector arguments
681
- if (isNode(find, 'PseudoSelector') && find.value.arg && isSelector(find.value.arg)) {
682
- return null;
683
- }
684
- const findVal = find.valueOf();
685
- const locations = [];
686
- for (let i = 0; i < target.value.length; i++) {
687
- if (target.value[i].valueOf() === findVal) {
688
- // Found exact match - this enables partial replacement
689
- const remainderComponents = target.value.filter((_, idx) => idx !== i);
690
- const remainders = remainderComponents.length === 0
691
- ? []
692
- : remainderComponents.length === 1
693
- ? [remainderComponents[0]]
694
- : [new CompoundSelector(remainderComponents).inherit(target)];
695
- locations.push(withMatchScope({
696
- path: [...basePath, i],
697
- matchedNode: target,
698
- extensionType: determineExtensionType(target, basePath),
699
- isPartialMatch: remainders.length > 0,
700
- remainders
701
- }));
702
- }
703
- }
704
- return locations;
705
- }
706
- // Fast path 4: Small compound to compound matching (.a.b === .b.a)
707
- if (isNode(target, 'CompoundSelector') && isNode(find, 'CompoundSelector')
708
- && target.value.length <= 4 && find.value.length <= 4) {
709
- return trySmallCompoundExtendMatch(target, find, basePath);
710
- }
711
- // Fast path 5: When find parameter is a selector list (legacy match-selector behavior)
712
- // Handles matchSelectors(target=".a", find=".a,.b") → should match because .a is in the list
713
- if (isNode(find, 'SelectorList')) {
714
- // Check if target matches any item in the find list
715
- for (let i = 0; i < find.value.length; i++) {
716
- const listItem = find.value[i];
717
- const result = tryFastPathExtendMatch(target, listItem, basePath);
718
- if (result && result.length > 0) {
719
- // Found a match with one of the list items
720
- return result;
721
- }
722
- }
723
- return []; // No matches found in list
724
- }
725
- // Fast path 6: Small selector list containing target
726
- if (isNode(target, 'SelectorList') && target.value.length <= 3) {
727
- const locations = [];
728
- for (let i = 0; i < target.value.length; i++) {
729
- const childResult = tryFastPathExtendMatch(target.value[i], find, [...basePath, i]);
730
- if (childResult) {
731
- locations.push(...childResult);
732
- }
733
- }
734
- return locations.length > 0 ? locations : [];
735
- }
736
- // Fast path 7: Complex selector patterns with partial match support
737
- if (isNode(target, 'ComplexSelector') && target.value.length <= 7) {
738
- // First check for exact complex selector matches
739
- if (isNode(find, 'ComplexSelector')) {
740
- const eq = areComplexSelectorsEquivalent(target, find);
741
- if (eq) {
742
- return [withMatchScope({
743
- path: [...basePath],
744
- matchedNode: target,
745
- extensionType: determineExtensionType(target, basePath)
746
- })];
747
- }
748
- }
749
- // Try partial complex matching
750
- if (isNode(find, 'ComplexSelector')) {
751
- const partialResult = tryPartialComplexMatch(target, find, basePath);
752
- if (partialResult && partialResult.length > 0) {
753
- return partialResult;
754
- }
755
- }
756
- // Try backtracking match for complex :is() scenarios
757
- if (isNode(find, 'ComplexSelector')) {
758
- const backtrackResult = tryBacktrackingComplexMatch(target, find, basePath);
759
- if (backtrackResult) {
760
- return backtrackResult;
761
- }
762
- // Try sequential complex matching with partial compound support
763
- const sequentialResult = trySequentialComplexMatch(target, find, basePath);
764
- if (sequentialResult) {
765
- return sequentialResult;
766
- }
767
- }
768
- // Try individual component matching
769
- const locations = [];
770
- for (let i = 0; i < target.value.length; i++) {
771
- const component = target.value[i];
772
- if (component && !isNode(component, 'Combinator')) {
773
- const childResult = tryFastPathExtendMatch(component, find, [...basePath, i]);
774
- if (childResult) {
775
- locations.push(...childResult);
776
- }
777
- }
778
- }
779
- // Post-process: when find matches one component of a multi-component complex selector,
780
- // that is always a partial match (full mode should reject it). Mark ALL such component
781
- // matches as partial, not just position 0.
782
- if (locations.length > 0 && target.value.length > 1) {
783
- for (const location of locations) {
784
- const lastSeg = location.path[location.path.length - 1];
785
- if (typeof lastSeg === 'number') {
786
- // Match is inside a component of this complex selector
787
- location.isPartialMatch = true;
788
- if (lastSeg === 0) {
789
- const remainingComponents = target.value.slice(1);
790
- location.remainders = remainingComponents.length === 1 && !isNode(remainingComponents[0], 'Combinator')
791
- ? [remainingComponents[0]]
792
- : [new ComplexSelector(remainingComponents).inherit(target)];
793
- }
794
- }
795
- }
796
- }
797
- return locations.length > 0 ? locations : null;
798
- }
799
- return null;
800
- }
801
- /**
802
- * Tries to match partial complex selectors
803
- */
804
- function tryPartialComplexMatch(target, find, basePath) {
805
- const targetComponents = target.value;
806
- const findComponents = find.value;
807
- if (findComponents.length > targetComponents.length) {
808
- return null;
809
- }
810
- // Try to match find at different positions (allow compound superset: .a.c.b contains .a.b)
811
- for (let startPos = 0; startPos <= targetComponents.length - findComponents.length; startPos++) {
812
- let matches = true;
813
- let hasCompoundPartialMatch = false;
814
- for (let i = 0; i < findComponents.length; i++) {
815
- const tComp = targetComponents[startPos + i];
816
- const fComp = findComponents[i];
817
- if (!tComp || !fComp) {
818
- matches = false;
819
- break;
820
- }
821
- if (isNode(tComp, 'Combinator') && isNode(fComp, 'Combinator')) {
822
- if (tComp.value !== fComp.value) {
823
- matches = false;
824
- break;
825
- }
826
- }
827
- else if (!isNode(tComp, 'Combinator') && !isNode(fComp, 'Combinator')) {
828
- let compMatch = componentsMatch(tComp, fComp);
829
- // Compound superset: target compound can contain find compound as subsequence (.a.c.b contains .a.b)
830
- if (!compMatch && isNode(tComp, 'CompoundSelector') && isNode(fComp, 'CompoundSelector')) {
831
- compMatch = compoundContainsCompoundSubsequence(tComp, fComp);
832
- }
833
- // Simple in compound: .x in .y.x
834
- if (!compMatch && isNode(tComp, 'CompoundSelector') && isNode(fComp, 'SimpleSelector')) {
835
- compMatch = tComp.value.some((c) => c.valueOf() === fComp.valueOf());
836
- }
837
- if (compMatch && isNode(tComp, 'CompoundSelector') && isNode(fComp, 'SimpleSelector')) {
838
- hasCompoundPartialMatch = true;
839
- }
840
- if (compMatch && isNode(tComp, 'CompoundSelector') && isNode(fComp, 'CompoundSelector') && tComp.value.length > fComp.value.length) {
841
- hasCompoundPartialMatch = true;
842
- }
843
- if (!compMatch) {
844
- matches = false;
845
- break;
846
- }
847
- }
848
- else {
849
- matches = false;
850
- break;
851
- }
852
- }
853
- if (matches) {
854
- // Calculate remainders
855
- const beforeComponents = targetComponents.slice(0, startPos);
856
- const afterComponents = targetComponents.slice(startPos + findComponents.length);
857
- const remainders = [];
858
- if (beforeComponents.length > 0) {
859
- remainders.push(new ComplexSelector(beforeComponents).inherit(target));
860
- }
861
- if (afterComponents.length > 0) {
862
- remainders.push(new ComplexSelector(afterComponents).inherit(target));
863
- }
864
- // Mark as partial if we have remainders OR if there was a compound partial match
865
- const isPartialMatch = remainders.length > 0 || hasCompoundPartialMatch;
866
- const loc = {
867
- path: [...basePath],
868
- matchedNode: target,
869
- extensionType: 'replace',
870
- isPartialMatch,
871
- remainders: remainders.length > 0 ? remainders : undefined
872
- };
873
- // Segment range for §3a: wrap full segment when match spans combinator
874
- if (remainders.length > 0) {
875
- loc.complexMatchRange = [startPos, startPos + findComponents.length];
876
- }
877
- return [withMatchScope(loc)];
878
- }
879
- }
880
- return null;
881
- }
882
- /**
883
- * Optimized compound selector matching for small compounds
884
- */
885
- function trySmallCompoundExtendMatch(target, find, basePath) {
886
- // Check for exact equivalence (order-independent)
887
- if (areCompoundSelectorsEquivalent(target, find)) {
888
- return [withMatchScope({
889
- path: [...basePath],
890
- matchedNode: target,
891
- extensionType: determineExtensionType(target, basePath)
892
- })];
893
- }
894
- // Check for subset matching (find is subset of target)
895
- if (find.value.length <= target.value.length) {
896
- const isSubset = find.value.every((findComp) => target.value.some((targetComp) => compoundComponentMatches(findComp, targetComp)));
897
- if (isSubset) {
898
- // Find contiguous slice [start, end) that matches find in order (for wrap :is(matched, extendWith).rest)
899
- const n = find.value.length;
900
- let contiguousStart = null;
901
- for (let start = 0; start <= target.value.length - n; start++) {
902
- let match = true;
903
- for (let j = 0; j < n; j++) {
904
- const tComp = target.value[start + j];
905
- const fComp = find.value[j];
906
- if (!tComp || !fComp) {
907
- match = false;
908
- break;
909
- }
910
- if (!compoundComponentMatches(fComp, tComp)) {
911
- match = false;
912
- break;
913
- }
914
- }
915
- if (match) {
916
- contiguousStart = start;
917
- break;
918
- }
919
- }
920
- // Calculate remainder after removing matched components
921
- const remainderComponents = target.value.filter((targetComp) => !find.value.some((findComp) => compoundComponentMatches(findComp, targetComp)));
922
- const remainders = remainderComponents.length === 0
923
- ? []
924
- : remainderComponents.length === 1
925
- ? [remainderComponents[0]]
926
- : [new CompoundSelector(remainderComponents).inherit(target)];
927
- const loc = {
928
- path: [...basePath],
929
- matchedNode: target,
930
- extensionType: determineExtensionType(target, basePath),
931
- isPartialMatch: remainders.length > 0,
932
- remainders
933
- };
934
- // When find is a contiguous slice, record range so we can wrap that slice as :is(find, extendWith)
935
- if (contiguousStart !== null && remainders.length > 0) {
936
- loc.contiguousCompoundRange = [contiguousStart, contiguousStart + n];
937
- loc.matchedNode = new CompoundSelector(find.value.slice()).inherit(target);
938
- loc.extensionType = 'wrap';
939
- }
940
- else if (remainders.length > 0) {
941
- // Non-contiguous: find leftmost subsequence of target indices that matches find in order
942
- const matchIndices = [];
943
- let findIdx = 0;
944
- for (let i = 0; i < target.value.length && findIdx < find.value.length; i++) {
945
- const tComp = target.value[i];
946
- const fComp = find.value[findIdx];
947
- if (compoundComponentMatches(fComp, tComp)) {
948
- matchIndices.push(i);
949
- findIdx++;
950
- }
951
- }
952
- if (matchIndices.length === find.value.length) {
953
- loc.compoundMatchIndices = matchIndices;
954
- loc.matchedNode = new CompoundSelector(find.value.slice()).inherit(target);
955
- loc.extensionType = 'wrap';
956
- }
957
- }
958
- return [withMatchScope(loc)];
959
- }
960
- }
961
- return [];
962
- }
963
- /**
964
- * Enhanced recursive search with :is() backtracking and optimization layers
965
- * @param current - Current selector being examined
966
- * @param target - Target selector to find
967
- * @param currentPath - Current path in the selector tree
968
- * @param locations - Array to collect found locations
969
- */
970
- function searchWithinSelector(current, target, currentPath, locations) {
971
- // OPTIMIZATION 1: Check for exact match
972
- if (current.valueOf() === target.valueOf()) {
973
- locations.push(withMatchScope({
974
- path: [...currentPath],
975
- matchedNode: current,
976
- extensionType: determineExtensionType(current, currentPath)
977
- }));
978
- }
979
- // OPTIMIZATION 2: Enhanced recursive search with specialized handlers for each selector type
980
- if (isNode(current, 'SelectorList')) {
981
- searchWithinSelectorList(current, target, currentPath, locations);
982
- }
983
- else if (isNode(current, 'CompoundSelector')) {
984
- searchWithinCompoundSelector(current, target, currentPath, locations);
985
- }
986
- else if (isNode(current, 'ComplexSelector')) {
987
- searchWithinComplexSelector(current, target, currentPath, locations);
988
- }
989
- else if (isNode(current, 'PseudoSelector')) {
990
- // OPTIMIZATION 3: Special handling for :is() pseudo-selectors with backtracking
991
- searchWithinPseudoSelector(current, target, currentPath, locations);
992
- }
993
- // SimpleSelector doesn't have nested content to search
994
- }
995
- /**
996
- * Searches within a selector list
997
- */
998
- function searchWithinSelectorList(selectorList, target, currentPath, locations) {
999
- selectorList.value.forEach((selector, index) => {
1000
- searchWithinSelector(selector, target, [...currentPath, index], locations);
1001
- });
1002
- }
1003
- /**
1004
- * Enhanced compound selector search with partial matching support
1005
- */
1006
- function searchWithinCompoundSelector(compound, target, currentPath, locations) {
1007
- // Handle when target is a PseudoSelector - check for equivalent matches
1008
- if (isNode(target, 'PseudoSelector') && target.value.arg && isSelector(target.value.arg)) {
1009
- // Look for matching pseudo-selectors within the compound
1010
- compound.value.forEach((component, index) => {
1011
- if (isNode(component, 'PseudoSelector') && arePseudoSelectorsEquivalent(component, target)) {
1012
- locations.push(withMatchScope({
1013
- path: [...currentPath, index],
1014
- matchedNode: component,
1015
- extensionType: 'replace'
1016
- }));
1017
- }
1018
- });
1019
- }
1020
- // Standard recursive search through each component
1021
- compound.value.forEach((component, index) => {
1022
- searchWithinSelector(component, target, [...currentPath, index], locations);
1023
- });
1024
- // OPTIMIZATION 5: Check for partial matches within compound selectors
1025
- // This enables extending when target is a subset of the compound
1026
- if (isNode(target, 'SimpleSelector')) {
1027
- const targetVal = target.valueOf();
1028
- for (let i = 0; i < compound.value.length; i++) {
1029
- if (compound.value[i].valueOf() === targetVal) {
1030
- // Found a component that matches target - create partial match
1031
- // Use unique path with component index to distinguish duplicate components
1032
- const remainderComponents = compound.value.filter((_, idx) => idx !== i);
1033
- const remainders = remainderComponents.length === 0
1034
- ? []
1035
- : remainderComponents.length === 1
1036
- ? [remainderComponents[0]]
1037
- : [new CompoundSelector(remainderComponents).inherit(compound)];
1038
- locations.push(withMatchScope({
1039
- path: [...currentPath, i],
1040
- matchedNode: compound.value[i],
1041
- extensionType: 'replace',
1042
- isPartialMatch: remainders.length > 0,
1043
- remainders
1044
- }));
1045
- }
1046
- }
1047
- }
1048
- // OPTIMIZATION 6: Compound-to-compound partial matching
1049
- if (isNode(target, 'CompoundSelector') && target.value.length <= compound.value.length) {
1050
- const isSubset = target.value.every(targetComp => compound.value.some(compComp => isNode(targetComp, 'PseudoSelector') && targetComp.value.arg && isSelector(targetComp.value.arg)
1051
- ? arePseudoSelectorsEquivalent(compComp, targetComp)
1052
- : compComp.valueOf() === targetComp.valueOf()));
1053
- if (isSubset) {
1054
- // Calculate remainder after removing matched components
1055
- const remainderComponents = compound.value.filter(compComp => !target.value.some(targetComp => isNode(targetComp, 'PseudoSelector') && targetComp.value.arg && isSelector(targetComp.value.arg)
1056
- ? arePseudoSelectorsEquivalent(compComp, targetComp)
1057
- : compComp.valueOf() === targetComp.valueOf()));
1058
- const remainders = remainderComponents.length === 0
1059
- ? []
1060
- : remainderComponents.length === 1
1061
- ? [remainderComponents[0]]
1062
- : [new CompoundSelector(remainderComponents).inherit(compound)];
1063
- locations.push(withMatchScope({
1064
- path: [...currentPath],
1065
- matchedNode: target,
1066
- extensionType: 'replace',
1067
- isPartialMatch: remainders.length > 0,
1068
- remainders
1069
- }));
1070
- }
1071
- }
1072
- }
1073
- /**
1074
- * Enhanced complex selector search with combinator-aware optimizations
1075
- */
1076
- function searchWithinComplexSelector(complex, target, currentPath, locations) {
1077
- const initialLocationCount = locations.length;
1078
- // If we're searching for a ComplexSelector target, allow full structural equivalence (including `:is(...)`).
1079
- if (isNode(target, 'ComplexSelector')) {
1080
- const eq = areComplexSelectorsEquivalent(complex, target);
1081
- if (eq) {
1082
- locations.push(withMatchScope({
1083
- path: [...currentPath],
1084
- matchedNode: complex,
1085
- extensionType: determineExtensionType(complex, currentPath)
1086
- }));
1087
- }
1088
- }
1089
- complex.value.forEach((component, index) => {
1090
- // Skip combinators, only search selector components
1091
- if (!isNode(component, 'Combinator')) {
1092
- searchWithinSelector(component, target, [...currentPath, index], locations);
1093
- }
1094
- });
1095
- // Post-process: when find matches one component of a multi-component complex selector,
1096
- // that is always a partial match (full mode should reject it). Mark ALL such component
1097
- // matches as partial, not just position 0.
1098
- if (locations.length > initialLocationCount && complex.value.length > 1) {
1099
- for (let i = initialLocationCount; i < locations.length; i++) {
1100
- const location = locations[i];
1101
- const lastPathSegment = location.path[location.path.length - 1];
1102
- if (typeof lastPathSegment === 'number') {
1103
- // Match is inside a component of this complex selector
1104
- location.isPartialMatch = true;
1105
- if (lastPathSegment === 0) {
1106
- const remainingComponents = complex.value.slice(1);
1107
- if (remainingComponents.length === 1 && !isNode(remainingComponents[0], 'Combinator')) {
1108
- location.remainders = [remainingComponents[0]];
1109
- }
1110
- else if (remainingComponents.length > 0) {
1111
- location.remainders = [new ComplexSelector(remainingComponents).inherit(complex)];
1112
- }
1113
- }
1114
- }
1115
- }
1116
- }
1117
- // OPTIMIZATION 8: Complex selector pattern matching
1118
- // Handle common patterns like descendant, child, sibling selectors efficiently
1119
- if (isNode(target, 'ComplexSelector')) {
1120
- // Check for structural matches within complex selector patterns
1121
- // This enables extending complex selectors that contain the target pattern
1122
- tryComplexSelectorPatternMatch(complex, target, currentPath, locations);
1123
- // Try backtracking match for :is() scenarios
1124
- const backtrackResult = tryBacktrackingComplexMatch(complex, target, currentPath);
1125
- if (backtrackResult) {
1126
- locations.push(...backtrackResult);
1127
- }
1128
- }
1129
- }
1130
- /**
1131
- * Attempts to find pattern matches within complex selectors
1132
- * Handles common CSS combinator patterns with optimized matching
1133
- */
1134
- function tryComplexSelectorPatternMatch(complex, target, currentPath, locations) {
1135
- // Enhanced pattern matching for cross-boundary matches
1136
- // Example: .a > .b should match within .a > .b.c
1137
- if (complex.value.length < target.value.length) {
1138
- return; // Complex selector must be at least as long as target
1139
- }
1140
- const targetComponents = target.value;
1141
- const complexComponents = complex.value;
1142
- // Try to match target pattern at different positions within complex selector
1143
- for (let startPos = 0; startPos <= complexComponents.length - targetComponents.length; startPos++) {
1144
- let isMatch = true;
1145
- const remainingComponents = [];
1146
- // Check if target matches at this position
1147
- for (let i = 0; i < targetComponents.length; i++) {
1148
- const targetComp = targetComponents[i];
1149
- const complexComp = complexComponents[startPos + i];
1150
- if (!targetComp || !complexComp) {
1151
- isMatch = false;
1152
- break;
1153
- }
1154
- if (isNode(targetComp, 'Combinator') && isNode(complexComp, 'Combinator')) {
1155
- // Both are combinators - must match exactly
1156
- if (targetComp.value !== complexComp.value) {
1157
- isMatch = false;
1158
- break;
1159
- }
1160
- }
1161
- else if (isNode(targetComp, 'Combinator') || isNode(complexComp, 'Combinator')) {
1162
- // One is combinator, other is not - no match
1163
- isMatch = false;
1164
- break;
1165
- }
1166
- else {
1167
- // Both are selector components
1168
- if (isNode(complexComp, 'CompoundSelector') && !isNode(targetComp, 'CompoundSelector')) {
1169
- // Complex component is compound, target is simple
1170
- // Check if target component appears within the compound
1171
- const foundInCompound = complexComp.value.some(comp => comp && componentsMatch(comp, targetComp));
1172
- if (foundInCompound) {
1173
- // Partial match - calculate remainder
1174
- const remainderComps = complexComp.value.filter(comp => comp && !componentsMatch(comp, targetComp));
1175
- if (remainderComps.length > 0) {
1176
- const remainder = remainderComps.length === 1
1177
- ? remainderComps[0]
1178
- : CompoundSelector.create(remainderComps).inherit(complexComp);
1179
- remainingComponents.push(remainder);
1180
- }
1181
- }
1182
- else {
1183
- isMatch = false;
1184
- break;
1185
- }
1186
- }
1187
- else if (!componentsMatch(targetComp, complexComp)) {
1188
- isMatch = false;
1189
- break;
1190
- }
1191
- }
1192
- }
1193
- if (isMatch) {
1194
- // Found a match! Add remaining components from complex selector
1195
- const postMatchComponents = complexComponents.slice(startPos + targetComponents.length);
1196
- remainingComponents.push(...postMatchComponents);
1197
- // Create remainder selector if there are remaining components
1198
- let remainders = [];
1199
- if (remainingComponents.length > 0) {
1200
- if (remainingComponents.length === 1 && !isNode(remainingComponents[0], 'Combinator')) {
1201
- remainders = [remainingComponents[0]];
1202
- }
1203
- else if (remainingComponents.length > 1) {
1204
- remainders = [ComplexSelector.create(remainingComponents).inherit(complex)];
1205
- }
1206
- }
1207
- locations.push(withMatchScope({
1208
- path: [...currentPath],
1209
- matchedNode: target,
1210
- extensionType: determineExtensionType(complex, currentPath),
1211
- isPartialMatch: remainders.length > 0,
1212
- remainders: remainders.length > 0 ? remainders : undefined
1213
- }));
1214
- // Only find the first match to avoid duplicates
1215
- return;
1216
- }
1217
- }
1218
- }
1219
- /**
1220
- * Add backtracking support for complex :is() scenarios
1221
- * This handles cases like :is(.a > .b).d > .c matching .a > .b > .c
1222
- * IMPORTANT: This must preserve combinator sequences for correct matching
1223
- */
1224
- function trySequentialComplexMatch(target, // what to search within
1225
- find, // what to find
1226
- basePath) {
1227
- // Don't strip combinators - we need to match the exact sequence
1228
- const targetComponents = target.value;
1229
- const findComponents = find.value;
1230
- if (findComponents.length === 0 || targetComponents.length < findComponents.length) {
1231
- return null;
1232
- }
1233
- // Try to find a contiguous subsequence match that preserves combinator structure
1234
- for (let startIdx = 0; startIdx <= targetComponents.length - findComponents.length; startIdx++) {
1235
- let matches = true;
1236
- // Check if the subsequence starting at startIdx matches the find pattern
1237
- for (let i = 0; i < findComponents.length; i++) {
1238
- const targetComp = targetComponents[startIdx + i];
1239
- const findComp = findComponents[i];
1240
- if (!targetComp || !findComp) {
1241
- matches = false;
1242
- break;
1243
- }
1244
- // Both must be same type (combinator vs selector)
1245
- if (isNode(targetComp, 'Combinator') !== isNode(findComp, 'Combinator')) {
1246
- matches = false;
1247
- break;
1248
- }
1249
- // If both are combinators, they must match exactly
1250
- if (isNode(targetComp, 'Combinator') && isNode(findComp, 'Combinator')) {
1251
- if (targetComp.value !== findComp.value) {
1252
- matches = false;
1253
- break;
1254
- }
1255
- }
1256
- else if (!isNode(targetComp, 'Combinator') && !isNode(findComp, 'Combinator')) {
1257
- // If both are selectors, use existing selector matching logic
1258
- // But also check for partial compound matching
1259
- let componentMatches = areSelectorArgumentsEquivalent(targetComp, findComp);
1260
- if (!componentMatches) {
1261
- // Check for partial compound matching: .b should match within .b.c
1262
- if (isNode(targetComp, 'CompoundSelector') && isNode(findComp, 'SimpleSelector')) {
1263
- componentMatches = targetComp.value.some(comp => comp.valueOf() === findComp.valueOf());
1264
- }
1265
- }
1266
- if (!componentMatches) {
1267
- matches = false;
1268
- break;
1269
- }
1270
- }
1271
- }
1272
- if (matches) {
1273
- // Calculate what remains before and after the match
1274
- const beforeComponents = targetComponents.slice(0, startIdx);
1275
- const afterComponents = targetComponents.slice(startIdx + findComponents.length);
1276
- const remainders = [];
1277
- if (beforeComponents.length > 0) {
1278
- remainders.push(new ComplexSelector(beforeComponents).inherit(target));
1279
- }
1280
- if (afterComponents.length > 0) {
1281
- remainders.push(new ComplexSelector(afterComponents).inherit(target));
1282
- }
1283
- // Check for compound-level remainders within the matched components
1284
- for (let i = 0; i < findComponents.length; i++) {
1285
- const targetComp = targetComponents[startIdx + i];
1286
- const findComp = findComponents[i];
1287
- if (!isNode(targetComp, 'Combinator') && !isNode(findComp, 'Combinator')) {
1288
- if (isNode(targetComp, 'CompoundSelector') && isNode(findComp, 'SimpleSelector')) {
1289
- // Check if there's a partial match leaving compound remainders
1290
- const matchingComponent = targetComp.value.find(comp => comp.valueOf() === findComp.valueOf());
1291
- if (matchingComponent) {
1292
- // Calculate remainder components within this compound
1293
- const compoundRemainders = targetComp.value.filter(comp => comp.valueOf() !== findComp.valueOf());
1294
- if (compoundRemainders.length > 0) {
1295
- if (compoundRemainders.length === 1) {
1296
- remainders.push(compoundRemainders[0]);
1297
- }
1298
- else {
1299
- remainders.push(new CompoundSelector(compoundRemainders).inherit(targetComp));
1300
- }
1301
- }
1302
- }
1303
- }
1304
- }
1305
- }
1306
- const isPartialMatch = remainders.length > 0;
1307
- return [{
1308
- path: [...basePath],
1309
- matchedNode: find,
1310
- extensionType: determineExtensionType(target, basePath),
1311
- isPartialMatch,
1312
- remainders: remainders.length > 0 ? remainders : undefined
1313
- }];
1314
- }
1315
- }
1316
- return null;
1317
- }
1318
- function tryBacktrackingComplexMatch(target, // what to search within
1319
- find, // what to find
1320
- basePath) {
1321
- // Extract non-combinator components
1322
- const targetComponents = target.value.filter(c => !isNode(c, 'Combinator'));
1323
- const findComponents = find.value.filter(c => !isNode(c, 'Combinator'));
1324
- if (findComponents.length === 0) {
1325
- return null;
1326
- }
1327
- // Special case: Check if target has a compound with :is() that can expand to match find
1328
- for (let i = 0; i < targetComponents.length; i++) {
1329
- const comp = targetComponents[i];
1330
- if (isNode(comp, 'CompoundSelector')) {
1331
- // Look for :is() pseudo-selectors in the compound
1332
- const isPseudos = comp.value.filter(v => isNode(v, 'PseudoSelector') && v.value.name === ':is' && v.value.arg && isSelector(v.value.arg));
1333
- for (const isPseudo of isPseudos) {
1334
- const isArg = isPseudo.value.arg;
1335
- // If :is() contains a complex selector
1336
- if (isNode(isArg, 'ComplexSelector')) {
1337
- // Get the :is() content components
1338
- const isArgComponents = isArg.value.filter(c => !isNode(c, 'Combinator'));
1339
- // Try to match the find pattern
1340
- if (isArgComponents.length >= 2) {
1341
- // Get the last component from :is() (e.g., .b from .a > .b)
1342
- const lastIsComponent = isArgComponents[isArgComponents.length - 1];
1343
- // Get other components in the compound (e.g., .d)
1344
- const otherCompoundComponents = comp.value.filter(v => v !== isPseudo);
1345
- // Check if find starts with the :is() pattern (improved structural matching)
1346
- // Only check the prefix components, allowing structural compound matching for the last component
1347
- let matchesIsPattern = true;
1348
- for (let j = 0; j < isArgComponents.length - 1; j++) {
1349
- if (j >= findComponents.length
1350
- || !componentsMatch(isArgComponents[j], findComponents[j])) {
1351
- matchesIsPattern = false;
1352
- break;
1353
- }
1354
- }
1355
- if (matchesIsPattern) {
1356
- // Check if the last :is() component with compound additions matches the next target component
1357
- const compoundWithIsLast = otherCompoundComponents.length > 0
1358
- ? new CompoundSelector([lastIsComponent, ...otherCompoundComponents])
1359
- : lastIsComponent;
1360
- const nextTargetIdx = isArgComponents.length - 1;
1361
- // Special compound matching for backtracking: allow compound to match simple if simple is contained
1362
- let compoundMatches = false;
1363
- if (nextTargetIdx < findComponents.length) {
1364
- const findComp = findComponents[nextTargetIdx];
1365
- if (isNode(compoundWithIsLast, 'CompoundSelector') && isNode(findComp, 'SimpleSelector')) {
1366
- // In improved structural semantics: compound matches simple if simple is contained
1367
- const containsTarget = compoundWithIsLast.value.some(comp => comp.valueOf() === findComp.valueOf());
1368
- if (containsTarget) {
1369
- compoundMatches = true;
1370
- }
1371
- }
1372
- else {
1373
- compoundMatches = componentsMatch(compoundWithIsLast, findComp);
1374
- }
1375
- }
1376
- if (compoundMatches) {
1377
- // Check if remaining selector components match remaining target
1378
- const targetRemaining = targetComponents.slice(i + 1);
1379
- const findRemaining = findComponents.slice(isArgComponents.length);
1380
- if (targetRemaining.length === findRemaining.length) {
1381
- let allMatch = true;
1382
- for (let k = 0; k < targetRemaining.length; k++) {
1383
- if (!componentsMatch(targetRemaining[k], findRemaining[k])) {
1384
- allMatch = false;
1385
- break;
1386
- }
1387
- }
1388
- if (allMatch) {
1389
- // We have a match!
1390
- const location = {
1391
- path: [...basePath],
1392
- matchedNode: target,
1393
- extensionType: 'replace',
1394
- isPartialMatch: true,
1395
- remainders: [] // Calculate proper remainders if needed
1396
- };
1397
- return [withMatchScope(location)];
1398
- }
1399
- }
1400
- }
1401
- }
1402
- }
1403
- }
1404
- }
1405
- }
1406
- }
1407
- return null;
1408
- }
1409
- /**
1410
- * Enhanced pseudo-selector search with :is() backtracking optimization
1411
- */
1412
- function searchWithinPseudoSelector(pseudo, target, currentPath, locations) {
1413
- const arg = pseudo.value.arg;
1414
- if (!arg || !isSelector(arg)) {
1415
- return;
1416
- }
1417
- const argSelector = arg;
1418
- // OPTIMIZATION 7: Special handling for :is() pseudo-selectors
1419
- // Implements sophisticated right-to-left backtracking algorithm from matchSelectors
1420
- if (pseudo.value.name === ':is') {
1421
- if (isNode(argSelector, 'SelectorList')) {
1422
- // Check if target matches any alternative in the :is() selector list
1423
- argSelector.value.forEach((alternative, altIndex) => {
1424
- const itemPath = [...currentPath, 'arg', altIndex];
1425
- // Direct structural match: use determineExtensionType so we get 'wrap' when inside a compound (not just 'append')
1426
- if (isStructurallyEqual(alternative, target)) {
1427
- locations.push(withMatchScope({
1428
- path: itemPath,
1429
- matchedNode: alternative,
1430
- extensionType: determineExtensionType(alternative, itemPath)
1431
- }));
1432
- }
1433
- // Recursive search within each alternative
1434
- searchWithinSelector(alternative, target, itemPath, locations);
1435
- });
1436
- // Additional optimization: Check if target could be added as new alternative
1437
- // This enables extending :is(.a, .b) with .c to become :is(.a, .b, .c)
1438
- const canExtendAsList = !argSelector.value.some(alt => isStructurallyEqual(alt, target));
1439
- if (canExtendAsList) {
1440
- locations.push(withMatchScope({
1441
- path: [...currentPath, 'arg'],
1442
- matchedNode: argSelector,
1443
- extensionType: 'append', // Append new alternative to :is() list
1444
- isPartialMatch: false
1445
- }));
1446
- }
1447
- }
1448
- else {
1449
- // Single argument in :is() - check for direct match
1450
- if (isStructurallyEqual(argSelector, target)) {
1451
- locations.push(withMatchScope({
1452
- path: [...currentPath, 'arg'],
1453
- matchedNode: argSelector,
1454
- extensionType: 'append', // Will convert single arg to SelectorList and append
1455
- isPartialMatch: false
1456
- }));
1457
- // Don't do recursive search since we found the direct match
1458
- return;
1459
- }
1460
- // Only do recursive search if no direct match found
1461
- searchWithinSelector(argSelector, target, [...currentPath, 'arg'], locations);
1462
- }
1463
- }
1464
- else {
1465
- // Standard recursive search for other pseudo-selectors
1466
- searchWithinSelector(argSelector, target, [...currentPath, 'arg'], locations);
1467
- }
1468
- }
1469
- /**
1470
- * Normalizes a selector to handle :is() equivalences
1471
- * This is the single source of truth for :is() expansion logic
1472
- *
1473
- * Examples:
1474
- * - :is(.a) -> .a
1475
- * - a :is(b, c) -> a b, a c (as SelectorList)
1476
- * - :is(.foo, .bar) -> .foo, .bar (as SelectorList)
1477
- */
1478
- function normalizeSelector(selector) {
1479
- if (isNode(selector, 'PseudoSelector') && selector.value.name === ':is' && selector.value.arg) {
1480
- const arg = selector.value.arg;
1481
- if (isNode(arg, 'SimpleSelector')) {
1482
- return arg;
1483
- }
1484
- if (isNode(arg, 'SelectorList')) {
1485
- return arg;
1486
- }
1487
- return arg;
1488
- }
1489
- if (isNode(selector, 'ComplexSelector')) {
1490
- const expanded = expandComplexSelectorWithIs(selector);
1491
- if (expanded.length > 1) {
1492
- return new SelectorList(expanded);
1493
- }
1494
- if (expanded.length === 1) {
1495
- return expanded[0];
1496
- }
1497
- }
1498
- if (isNode(selector, 'SelectorList')) {
1499
- const normalizedSelectors = [];
1500
- for (const sel of selector.value) {
1501
- const normalized = normalizeSelector(sel);
1502
- if (isNode(normalized, 'SelectorList')) {
1503
- normalizedSelectors.push(...normalized.value);
1504
- }
1505
- else {
1506
- normalizedSelectors.push(normalized);
1507
- }
1508
- }
1509
- if (normalizedSelectors.length === 1) {
1510
- return normalizedSelectors[0];
1511
- }
1512
- return new SelectorList(normalizedSelectors);
1513
- }
1514
- return selector;
1515
- }
1516
- export function normalizeSelectorForExtend(selector) {
1517
- return normalizeSelector(selector);
1518
- }
1519
- /**
1520
- * Legacy matchSelectors function for backward compatibility
1521
- * Maps to the new findExtendableLocations API
1522
- */
1523
- export function matchSelectors(target, find, partial = false) {
1524
- const normalizedTarget = normalizeSelector(target);
1525
- const normalizedFind = normalizeSelector(find);
1526
- if (normalizedTarget.valueOf() === normalizedFind.valueOf()) {
1527
- return {
1528
- hasMatch: true,
1529
- hasFullMatch: true,
1530
- hasPartialMatch: false,
1531
- matched: [find],
1532
- remainders: []
1533
- };
1534
- }
1535
- const searchResult = findExtendableLocations(normalizedTarget, normalizedFind);
1536
- if (!searchResult.hasMatches) {
1537
- return {
1538
- hasMatch: false,
1539
- hasFullMatch: false,
1540
- hasPartialMatch: false,
1541
- matched: [],
1542
- remainders: []
1543
- };
1544
- }
1545
- const hasAnyPartialMatch = searchResult.locations.some((loc) => loc.isPartialMatch);
1546
- const hasAnyFullMatch = searchResult.locations.some((loc) => !loc.isPartialMatch);
1547
- const isPartialMatch = partial && (hasAnyPartialMatch || searchResult.locations.some((loc) => loc.remainders && loc.remainders.length > 0));
1548
- return {
1549
- hasMatch: true,
1550
- hasFullMatch: hasAnyFullMatch && !isPartialMatch,
1551
- hasPartialMatch: isPartialMatch,
1552
- matched: hasAnyFullMatch && !isPartialMatch ? [find] : [],
1553
- remainders: searchResult.locations[0]?.remainders || []
1554
- };
1555
- }
1556
- export function combineKeys(a, b) {
1557
- if (a instanceof Set) {
1558
- if (b instanceof Set) {
1559
- return a.union(b);
1560
- }
1561
- else {
1562
- return (new Set(a)).add(b);
1563
- }
1564
- }
1565
- else {
1566
- if (b instanceof Set) {
1567
- return (new Set(b)).add(a);
1568
- }
1569
- else {
1570
- return new Set([a, b]);
1571
- }
1572
- }
1573
- }
1574
- export function selectorCompare(a, b, forwardSearch, backwardSearch) {
1575
- const normalizedA = normalizeSelectorForExtend(a);
1576
- const normalizedB = normalizeSelectorForExtend(b);
1577
- if (isNode(normalizedA, 'SelectorList') && isNode(normalizedB, 'SelectorList')) {
1578
- const aValues = normalizedA.value;
1579
- const bValues = normalizedB.value;
1580
- // Use a Set for O(N) order-independent comparison instead of O(N log N) sort
1581
- const equivalent = aValues.length === bValues.length && (() => {
1582
- const aSet = new Set(aValues.map(item => normalizeSelectorForExtend(item).valueOf()));
1583
- return bValues.every(item => aSet.has(normalizeSelectorForExtend(item).valueOf()));
1584
- })();
1585
- if (equivalent) {
1586
- return {
1587
- isEquivalent: true,
1588
- hasWholeMatch: true,
1589
- hasPartialMatch: false,
1590
- locations: []
1591
- };
1592
- }
1593
- }
1594
- const forward = forwardSearch ?? findExtendableLocations(normalizedA, normalizedB);
1595
- const backward = backwardSearch ?? findExtendableLocations(normalizedB, normalizedA);
1596
- return {
1597
- isEquivalent: forward.hasWholeMatch && backward.hasWholeMatch,
1598
- hasWholeMatch: forward.hasWholeMatch,
1599
- hasPartialMatch: forward.hasMatches && !forward.hasWholeMatch,
1600
- locations: forward.locations
1601
- };
1602
- }
1603
- //# sourceMappingURL=selector-match-core.js.map