@jesscss/core 2.0.0-alpha.1

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