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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/lib/index.cjs +20159 -0
  2. package/lib/index.d.cts +5993 -0
  3. package/lib/index.d.cts.map +1 -0
  4. package/lib/index.d.ts +5992 -21
  5. package/lib/index.d.ts.map +1 -1
  6. package/lib/index.js +19926 -22
  7. package/lib/index.js.map +1 -1
  8. package/package.json +15 -14
  9. package/src/__tests__/define-function-record.test.ts +58 -0
  10. package/src/__tests__/define-function-simple.test.ts +55 -0
  11. package/src/__tests__/define-function-split-sequence.test.ts +547 -0
  12. package/src/__tests__/define-function-type-parity.test.ts +9 -0
  13. package/src/__tests__/define-function.test.ts +763 -0
  14. package/src/__tests__/num-operations.test.ts +91 -0
  15. package/src/__tests__/safe-parse.test.ts +374 -0
  16. package/src/context.ts +896 -0
  17. package/src/conversions.ts +282 -0
  18. package/src/debug-log.ts +29 -0
  19. package/src/define-function.ts +1006 -0
  20. package/src/deprecation.ts +67 -0
  21. package/src/globals.d.ts +26 -0
  22. package/src/index.ts +31 -0
  23. package/src/jess-error.ts +773 -0
  24. package/src/logger/deprecation-processing.ts +109 -0
  25. package/src/logger.ts +31 -0
  26. package/src/plugin.ts +292 -0
  27. package/src/tree/LOOKUP_CHAINS.md +35 -0
  28. package/src/tree/README.md +18 -0
  29. package/src/tree/__tests__/__snapshots__/extend-eval-integration.test.ts.snap +1455 -0
  30. package/src/tree/__tests__/ampersand.test.ts +382 -0
  31. package/src/tree/__tests__/at-rule.test.ts +2047 -0
  32. package/src/tree/__tests__/basic-render.test.ts +212 -0
  33. package/src/tree/__tests__/block.test.ts +40 -0
  34. package/src/tree/__tests__/call.test.ts +346 -0
  35. package/src/tree/__tests__/color.test.ts +537 -0
  36. package/src/tree/__tests__/condition.test.ts +186 -0
  37. package/src/tree/__tests__/control.test.ts +564 -0
  38. package/src/tree/__tests__/declaration.test.ts +253 -0
  39. package/src/tree/__tests__/dependency-graph.test.ts +177 -0
  40. package/src/tree/__tests__/detached-rulesets.test.ts +213 -0
  41. package/src/tree/__tests__/dimension.test.ts +236 -0
  42. package/src/tree/__tests__/expression.test.ts +73 -0
  43. package/src/tree/__tests__/ext-node.test.ts +31 -0
  44. package/src/tree/__tests__/extend-eval-integration.test.ts +1033 -0
  45. package/src/tree/__tests__/extend-import-style.test.ts +929 -0
  46. package/src/tree/__tests__/extend-less-fixtures.test.ts +851 -0
  47. package/src/tree/__tests__/extend-list.test.ts +31 -0
  48. package/src/tree/__tests__/extend-roots.test.ts +1045 -0
  49. package/src/tree/__tests__/extend-rules.test.ts +740 -0
  50. package/src/tree/__tests__/func.test.ts +171 -0
  51. package/src/tree/__tests__/import-js.test.ts +33 -0
  52. package/src/tree/__tests__/import-style-test-helpers.ts +56 -0
  53. package/src/tree/__tests__/import-style.test.ts +1967 -0
  54. package/src/tree/__tests__/interpolated-reference.test.ts +44 -0
  55. package/src/tree/__tests__/interpolated.test.ts +41 -0
  56. package/src/tree/__tests__/list.test.ts +177 -0
  57. package/src/tree/__tests__/log.test.ts +83 -0
  58. package/src/tree/__tests__/mixin-recursion.test.ts +639 -0
  59. package/src/tree/__tests__/mixin.test.ts +2171 -0
  60. package/src/tree/__tests__/negative.test.ts +45 -0
  61. package/src/tree/__tests__/nesting-collapse.test.ts +519 -0
  62. package/src/tree/__tests__/node-flags-perf.test.ts +195 -0
  63. package/src/tree/__tests__/node-flags.test.ts +410 -0
  64. package/src/tree/__tests__/node-graph.test.ts +598 -0
  65. package/src/tree/__tests__/node-mutation.test.ts +182 -0
  66. package/src/tree/__tests__/operation.test.ts +18 -0
  67. package/src/tree/__tests__/paren.test.ts +90 -0
  68. package/src/tree/__tests__/preserve-mode-output.test.ts +50 -0
  69. package/src/tree/__tests__/quoted.test.ts +72 -0
  70. package/src/tree/__tests__/range.test.ts +59 -0
  71. package/src/tree/__tests__/reference.test.ts +743 -0
  72. package/src/tree/__tests__/rest.test.ts +29 -0
  73. package/src/tree/__tests__/rules-raw.test.ts +14 -0
  74. package/src/tree/__tests__/rules.test.ts +1271 -0
  75. package/src/tree/__tests__/ruleset.test.ts +597 -0
  76. package/src/tree/__tests__/selector-attr.test.ts +50 -0
  77. package/src/tree/__tests__/selector-basic.test.ts +44 -0
  78. package/src/tree/__tests__/selector-capture.test.ts +22 -0
  79. package/src/tree/__tests__/selector-complex.test.ts +120 -0
  80. package/src/tree/__tests__/selector-compound.test.ts +74 -0
  81. package/src/tree/__tests__/selector-interpolated.test.ts +50 -0
  82. package/src/tree/__tests__/selector-list.test.ts +59 -0
  83. package/src/tree/__tests__/selector-pseudo.test.ts +23 -0
  84. package/src/tree/__tests__/selector.test.ts +182 -0
  85. package/src/tree/__tests__/sequence.test.ts +226 -0
  86. package/src/tree/__tests__/serialize-types.test.ts +529 -0
  87. package/src/tree/__tests__/spaced.test.ts +8 -0
  88. package/src/tree/__tests__/url.test.ts +72 -0
  89. package/src/tree/__tests__/var-declaration.test.ts +90 -0
  90. package/src/tree/ampersand.ts +538 -0
  91. package/src/tree/any.ts +169 -0
  92. package/src/tree/at-rule.ts +760 -0
  93. package/src/tree/block.ts +72 -0
  94. package/src/tree/bool.ts +46 -0
  95. package/src/tree/call.ts +593 -0
  96. package/src/tree/collection.ts +52 -0
  97. package/src/tree/color.ts +629 -0
  98. package/src/tree/combinator.ts +30 -0
  99. package/src/tree/comment.ts +36 -0
  100. package/src/tree/condition.ts +194 -0
  101. package/src/tree/control.ts +452 -0
  102. package/src/tree/declaration-custom.ts +56 -0
  103. package/src/tree/declaration-var.ts +87 -0
  104. package/src/tree/declaration.ts +742 -0
  105. package/src/tree/default-guard.ts +35 -0
  106. package/src/tree/dimension.ts +392 -0
  107. package/src/tree/expression.ts +97 -0
  108. package/src/tree/extend-list.ts +51 -0
  109. package/src/tree/extend.ts +391 -0
  110. package/src/tree/function.ts +254 -0
  111. package/src/tree/import-js.ts +130 -0
  112. package/src/tree/import-style.ts +875 -0
  113. package/{lib/tree/index.js → src/tree/index.ts} +49 -22
  114. package/src/tree/interpolated.ts +346 -0
  115. package/src/tree/js-array.ts +21 -0
  116. package/src/tree/js-expr.ts +50 -0
  117. package/src/tree/js-function.ts +31 -0
  118. package/src/tree/js-object.ts +22 -0
  119. package/src/tree/list.ts +415 -0
  120. package/src/tree/log.ts +89 -0
  121. package/src/tree/mixin.ts +331 -0
  122. package/src/tree/negative.ts +58 -0
  123. package/src/tree/nil.ts +57 -0
  124. package/src/tree/node-base.ts +1716 -0
  125. package/src/tree/node-type.ts +122 -0
  126. package/src/tree/node.ts +118 -0
  127. package/src/tree/number.ts +54 -0
  128. package/src/tree/operation.ts +187 -0
  129. package/src/tree/paren.ts +132 -0
  130. package/src/tree/query-condition.ts +47 -0
  131. package/src/tree/quoted.ts +119 -0
  132. package/src/tree/range.ts +101 -0
  133. package/src/tree/reference.ts +1099 -0
  134. package/src/tree/rest.ts +55 -0
  135. package/src/tree/rules-raw.ts +52 -0
  136. package/src/tree/rules.ts +2896 -0
  137. package/src/tree/ruleset.ts +1217 -0
  138. package/src/tree/selector-attr.ts +172 -0
  139. package/src/tree/selector-basic.ts +75 -0
  140. package/src/tree/selector-capture.ts +85 -0
  141. package/src/tree/selector-complex.ts +189 -0
  142. package/src/tree/selector-compound.ts +205 -0
  143. package/src/tree/selector-interpolated.ts +95 -0
  144. package/src/tree/selector-list.ts +245 -0
  145. package/src/tree/selector-pseudo.ts +173 -0
  146. package/src/tree/selector-simple.ts +10 -0
  147. package/src/tree/selector.ts +152 -0
  148. package/src/tree/sequence.ts +463 -0
  149. package/src/tree/tree.ts +130 -0
  150. package/src/tree/url.ts +95 -0
  151. package/src/tree/util/EXTEND_ARCHITECTURE_ANALYSIS.md +215 -0
  152. package/src/tree/util/EXTEND_AUDIT.md +233 -0
  153. package/src/tree/util/EXTEND_BASELINE.md +64 -0
  154. package/src/tree/util/EXTEND_CALL_GRAPH_ANALYSIS.md +244 -0
  155. package/src/tree/util/EXTEND_DOCS.md +24 -0
  156. package/src/tree/util/EXTEND_FINAL_SUMMARY.md +95 -0
  157. package/src/tree/util/EXTEND_FUNCTION_AUDIT.md +1433 -0
  158. package/src/tree/util/EXTEND_OPTIMIZATION_PLAN.md +114 -0
  159. package/src/tree/util/EXTEND_REFACTORING_SUMMARY.md +152 -0
  160. package/src/tree/util/EXTEND_RULES.md +74 -0
  161. package/src/tree/util/EXTEND_UNUSED_FUNCTIONS.md +127 -0
  162. package/src/tree/util/EXTEND_UNUSED_FUNCTIONS_ANALYSIS.md +227 -0
  163. package/src/tree/util/NODE_COPY_REDUCTION_PLAN.md +12 -0
  164. package/src/tree/util/__tests__/EXTEND_TEST_INDEX.md +59 -0
  165. package/src/tree/util/__tests__/OPTIMIZATION-ANALYSIS.md +130 -0
  166. package/src/tree/util/__tests__/WALK_AND_CONSUME_DESIGN.md +138 -0
  167. package/src/tree/util/__tests__/_archive/2026-02-09__OPTIMIZATION-ANALYSIS.md +9 -0
  168. package/src/tree/util/__tests__/_archive/README.md +4 -0
  169. package/src/tree/util/__tests__/bitset.test.ts +142 -0
  170. package/src/tree/util/__tests__/debug-log.ts +50 -0
  171. package/src/tree/util/__tests__/extend-comment-handling.test.ts +187 -0
  172. package/src/tree/util/__tests__/extend-core-unit.test.ts +941 -0
  173. package/src/tree/util/__tests__/extend-pipeline-bench.test.ts +154 -0
  174. package/src/tree/util/__tests__/extend-pipeline-bench.ts +190 -0
  175. package/src/tree/util/__tests__/fast-reject.test.ts +377 -0
  176. package/src/tree/util/__tests__/is-node.test.ts +63 -0
  177. package/src/tree/util/__tests__/list-like.test.ts +63 -0
  178. package/src/tree/util/__tests__/outputwriter.test.ts +523 -0
  179. package/src/tree/util/__tests__/print.test.ts +183 -0
  180. package/src/tree/util/__tests__/process-extends.test.ts +226 -0
  181. package/src/tree/util/__tests__/process-leading-is.test.ts +205 -0
  182. package/src/tree/util/__tests__/recursion-helper.test.ts +184 -0
  183. package/src/tree/util/__tests__/selector-match-unit.test.ts +1427 -0
  184. package/src/tree/util/__tests__/sourcemap.test.ts +117 -0
  185. package/src/tree/util/ampersand-template.ts +9 -0
  186. package/src/tree/util/bitset.ts +194 -0
  187. package/src/tree/util/calculate.ts +11 -0
  188. package/src/tree/util/cast.ts +89 -0
  189. package/src/tree/util/cloning.ts +8 -0
  190. package/src/tree/util/collections.ts +299 -0
  191. package/src/tree/util/compare.ts +90 -0
  192. package/src/tree/util/cursor.ts +171 -0
  193. package/src/tree/util/extend-core.ts +2139 -0
  194. package/src/tree/util/extend-roots.ts +1108 -0
  195. package/src/tree/util/field-helpers.ts +354 -0
  196. package/src/tree/util/is-node.ts +43 -0
  197. package/src/tree/util/list-like.ts +93 -0
  198. package/src/tree/util/mixin-instance-primitives.ts +2020 -0
  199. package/src/tree/util/print.ts +303 -0
  200. package/src/tree/util/process-leading-is.ts +421 -0
  201. package/src/tree/util/recursion-helper.ts +54 -0
  202. package/src/tree/util/regex.ts +2 -0
  203. package/src/tree/util/registry-utils.ts +1953 -0
  204. package/src/tree/util/ruleset-trace.ts +17 -0
  205. package/src/tree/util/scoped-body-eval.ts +320 -0
  206. package/src/tree/util/selector-match-core.ts +2005 -0
  207. package/src/tree/util/selector-utils.ts +757 -0
  208. package/src/tree/util/serialize-helper.ts +535 -0
  209. package/src/tree/util/serialize-types.ts +318 -0
  210. package/src/tree/util/should-operate.ts +78 -0
  211. package/src/tree/util/sourcemap.ts +37 -0
  212. package/src/types/config.ts +247 -0
  213. package/src/types/index.ts +12 -0
  214. package/{lib/types/modes.d.ts → src/types/modes.ts} +2 -1
  215. package/src/types.d.ts +9 -0
  216. package/src/types.ts +68 -0
  217. package/src/use-webpack-resolver.ts +56 -0
  218. package/src/visitor/__tests__/visitor.test.ts +136 -0
  219. package/src/visitor/index.ts +263 -0
  220. package/{lib/visitor/less-visitor.js → src/visitor/less-visitor.ts} +3 -2
  221. package/lib/context.d.ts +0 -352
  222. package/lib/context.d.ts.map +0 -1
  223. package/lib/context.js +0 -636
  224. package/lib/context.js.map +0 -1
  225. package/lib/conversions.d.ts +0 -73
  226. package/lib/conversions.d.ts.map +0 -1
  227. package/lib/conversions.js +0 -253
  228. package/lib/conversions.js.map +0 -1
  229. package/lib/debug-log.d.ts +0 -2
  230. package/lib/debug-log.d.ts.map +0 -1
  231. package/lib/debug-log.js +0 -27
  232. package/lib/debug-log.js.map +0 -1
  233. package/lib/define-function.d.ts +0 -587
  234. package/lib/define-function.d.ts.map +0 -1
  235. package/lib/define-function.js +0 -726
  236. package/lib/define-function.js.map +0 -1
  237. package/lib/deprecation.d.ts +0 -34
  238. package/lib/deprecation.d.ts.map +0 -1
  239. package/lib/deprecation.js +0 -57
  240. package/lib/deprecation.js.map +0 -1
  241. package/lib/jess-error.d.ts +0 -343
  242. package/lib/jess-error.d.ts.map +0 -1
  243. package/lib/jess-error.js +0 -508
  244. package/lib/jess-error.js.map +0 -1
  245. package/lib/logger/deprecation-processing.d.ts +0 -41
  246. package/lib/logger/deprecation-processing.d.ts.map +0 -1
  247. package/lib/logger/deprecation-processing.js +0 -81
  248. package/lib/logger/deprecation-processing.js.map +0 -1
  249. package/lib/logger.d.ts +0 -10
  250. package/lib/logger.d.ts.map +0 -1
  251. package/lib/logger.js +0 -20
  252. package/lib/logger.js.map +0 -1
  253. package/lib/plugin.d.ts +0 -94
  254. package/lib/plugin.d.ts.map +0 -1
  255. package/lib/plugin.js +0 -174
  256. package/lib/plugin.js.map +0 -1
  257. package/lib/tree/ampersand.d.ts +0 -98
  258. package/lib/tree/ampersand.d.ts.map +0 -1
  259. package/lib/tree/ampersand.js +0 -319
  260. package/lib/tree/ampersand.js.map +0 -1
  261. package/lib/tree/any.d.ts +0 -58
  262. package/lib/tree/any.d.ts.map +0 -1
  263. package/lib/tree/any.js +0 -104
  264. package/lib/tree/any.js.map +0 -1
  265. package/lib/tree/at-rule.d.ts +0 -53
  266. package/lib/tree/at-rule.d.ts.map +0 -1
  267. package/lib/tree/at-rule.js +0 -503
  268. package/lib/tree/at-rule.js.map +0 -1
  269. package/lib/tree/block.d.ts +0 -22
  270. package/lib/tree/block.d.ts.map +0 -1
  271. package/lib/tree/block.js +0 -24
  272. package/lib/tree/block.js.map +0 -1
  273. package/lib/tree/bool.d.ts +0 -18
  274. package/lib/tree/bool.d.ts.map +0 -1
  275. package/lib/tree/bool.js +0 -28
  276. package/lib/tree/bool.js.map +0 -1
  277. package/lib/tree/call.d.ts +0 -66
  278. package/lib/tree/call.d.ts.map +0 -1
  279. package/lib/tree/call.js +0 -306
  280. package/lib/tree/call.js.map +0 -1
  281. package/lib/tree/collection.d.ts +0 -30
  282. package/lib/tree/collection.d.ts.map +0 -1
  283. package/lib/tree/collection.js +0 -37
  284. package/lib/tree/collection.js.map +0 -1
  285. package/lib/tree/color.d.ts +0 -101
  286. package/lib/tree/color.d.ts.map +0 -1
  287. package/lib/tree/color.js +0 -513
  288. package/lib/tree/color.js.map +0 -1
  289. package/lib/tree/combinator.d.ts +0 -13
  290. package/lib/tree/combinator.d.ts.map +0 -1
  291. package/lib/tree/combinator.js +0 -12
  292. package/lib/tree/combinator.js.map +0 -1
  293. package/lib/tree/comment.d.ts +0 -20
  294. package/lib/tree/comment.d.ts.map +0 -1
  295. package/lib/tree/comment.js +0 -19
  296. package/lib/tree/comment.js.map +0 -1
  297. package/lib/tree/condition.d.ts +0 -31
  298. package/lib/tree/condition.d.ts.map +0 -1
  299. package/lib/tree/condition.js +0 -103
  300. package/lib/tree/condition.js.map +0 -1
  301. package/lib/tree/control.d.ts +0 -104
  302. package/lib/tree/control.d.ts.map +0 -1
  303. package/lib/tree/control.js +0 -430
  304. package/lib/tree/control.js.map +0 -1
  305. package/lib/tree/declaration-custom.d.ts +0 -18
  306. package/lib/tree/declaration-custom.d.ts.map +0 -1
  307. package/lib/tree/declaration-custom.js +0 -24
  308. package/lib/tree/declaration-custom.js.map +0 -1
  309. package/lib/tree/declaration-var.d.ts +0 -35
  310. package/lib/tree/declaration-var.d.ts.map +0 -1
  311. package/lib/tree/declaration-var.js +0 -63
  312. package/lib/tree/declaration-var.js.map +0 -1
  313. package/lib/tree/declaration.d.ts +0 -78
  314. package/lib/tree/declaration.d.ts.map +0 -1
  315. package/lib/tree/declaration.js +0 -286
  316. package/lib/tree/declaration.js.map +0 -1
  317. package/lib/tree/default-guard.d.ts +0 -15
  318. package/lib/tree/default-guard.d.ts.map +0 -1
  319. package/lib/tree/default-guard.js +0 -19
  320. package/lib/tree/default-guard.js.map +0 -1
  321. package/lib/tree/dimension.d.ts +0 -34
  322. package/lib/tree/dimension.d.ts.map +0 -1
  323. package/lib/tree/dimension.js +0 -294
  324. package/lib/tree/dimension.js.map +0 -1
  325. package/lib/tree/expression.d.ts +0 -25
  326. package/lib/tree/expression.d.ts.map +0 -1
  327. package/lib/tree/expression.js +0 -32
  328. package/lib/tree/expression.js.map +0 -1
  329. package/lib/tree/extend-list.d.ts +0 -23
  330. package/lib/tree/extend-list.d.ts.map +0 -1
  331. package/lib/tree/extend-list.js +0 -23
  332. package/lib/tree/extend-list.js.map +0 -1
  333. package/lib/tree/extend.d.ts +0 -47
  334. package/lib/tree/extend.d.ts.map +0 -1
  335. package/lib/tree/extend.js +0 -296
  336. package/lib/tree/extend.js.map +0 -1
  337. package/lib/tree/function.d.ts +0 -48
  338. package/lib/tree/function.d.ts.map +0 -1
  339. package/lib/tree/function.js +0 -74
  340. package/lib/tree/function.js.map +0 -1
  341. package/lib/tree/import-js.d.ts +0 -35
  342. package/lib/tree/import-js.d.ts.map +0 -1
  343. package/lib/tree/import-js.js +0 -45
  344. package/lib/tree/import-js.js.map +0 -1
  345. package/lib/tree/import-style.d.ts +0 -156
  346. package/lib/tree/import-style.d.ts.map +0 -1
  347. package/lib/tree/import-style.js +0 -566
  348. package/lib/tree/import-style.js.map +0 -1
  349. package/lib/tree/index.d.ts +0 -71
  350. package/lib/tree/index.d.ts.map +0 -1
  351. package/lib/tree/index.js.map +0 -1
  352. package/lib/tree/interpolated-reference.d.ts +0 -24
  353. package/lib/tree/interpolated-reference.d.ts.map +0 -1
  354. package/lib/tree/interpolated-reference.js +0 -37
  355. package/lib/tree/interpolated-reference.js.map +0 -1
  356. package/lib/tree/interpolated.d.ts +0 -62
  357. package/lib/tree/interpolated.d.ts.map +0 -1
  358. package/lib/tree/interpolated.js +0 -204
  359. package/lib/tree/interpolated.js.map +0 -1
  360. package/lib/tree/js-array.d.ts +0 -10
  361. package/lib/tree/js-array.d.ts.map +0 -1
  362. package/lib/tree/js-array.js +0 -10
  363. package/lib/tree/js-array.js.map +0 -1
  364. package/lib/tree/js-expr.d.ts +0 -23
  365. package/lib/tree/js-expr.d.ts.map +0 -1
  366. package/lib/tree/js-expr.js +0 -28
  367. package/lib/tree/js-expr.js.map +0 -1
  368. package/lib/tree/js-function.d.ts +0 -20
  369. package/lib/tree/js-function.d.ts.map +0 -1
  370. package/lib/tree/js-function.js +0 -16
  371. package/lib/tree/js-function.js.map +0 -1
  372. package/lib/tree/js-object.d.ts +0 -10
  373. package/lib/tree/js-object.d.ts.map +0 -1
  374. package/lib/tree/js-object.js +0 -10
  375. package/lib/tree/js-object.js.map +0 -1
  376. package/lib/tree/list.d.ts +0 -38
  377. package/lib/tree/list.d.ts.map +0 -1
  378. package/lib/tree/list.js +0 -83
  379. package/lib/tree/list.js.map +0 -1
  380. package/lib/tree/log.d.ts +0 -29
  381. package/lib/tree/log.d.ts.map +0 -1
  382. package/lib/tree/log.js +0 -56
  383. package/lib/tree/log.js.map +0 -1
  384. package/lib/tree/mixin.d.ts +0 -87
  385. package/lib/tree/mixin.d.ts.map +0 -1
  386. package/lib/tree/mixin.js +0 -112
  387. package/lib/tree/mixin.js.map +0 -1
  388. package/lib/tree/negative.d.ts +0 -17
  389. package/lib/tree/negative.d.ts.map +0 -1
  390. package/lib/tree/negative.js +0 -22
  391. package/lib/tree/negative.js.map +0 -1
  392. package/lib/tree/nil.d.ts +0 -30
  393. package/lib/tree/nil.d.ts.map +0 -1
  394. package/lib/tree/nil.js +0 -35
  395. package/lib/tree/nil.js.map +0 -1
  396. package/lib/tree/node-base.d.ts +0 -361
  397. package/lib/tree/node-base.d.ts.map +0 -1
  398. package/lib/tree/node-base.js +0 -930
  399. package/lib/tree/node-base.js.map +0 -1
  400. package/lib/tree/node.d.ts +0 -10
  401. package/lib/tree/node.d.ts.map +0 -1
  402. package/lib/tree/node.js +0 -45
  403. package/lib/tree/node.js.map +0 -1
  404. package/lib/tree/number.d.ts +0 -21
  405. package/lib/tree/number.d.ts.map +0 -1
  406. package/lib/tree/number.js +0 -27
  407. package/lib/tree/number.js.map +0 -1
  408. package/lib/tree/operation.d.ts +0 -26
  409. package/lib/tree/operation.d.ts.map +0 -1
  410. package/lib/tree/operation.js +0 -103
  411. package/lib/tree/operation.js.map +0 -1
  412. package/lib/tree/paren.d.ts +0 -19
  413. package/lib/tree/paren.d.ts.map +0 -1
  414. package/lib/tree/paren.js +0 -92
  415. package/lib/tree/paren.js.map +0 -1
  416. package/lib/tree/query-condition.d.ts +0 -17
  417. package/lib/tree/query-condition.d.ts.map +0 -1
  418. package/lib/tree/query-condition.js +0 -39
  419. package/lib/tree/query-condition.js.map +0 -1
  420. package/lib/tree/quoted.d.ts +0 -28
  421. package/lib/tree/quoted.d.ts.map +0 -1
  422. package/lib/tree/quoted.js +0 -75
  423. package/lib/tree/quoted.js.map +0 -1
  424. package/lib/tree/range.d.ts +0 -33
  425. package/lib/tree/range.d.ts.map +0 -1
  426. package/lib/tree/range.js +0 -47
  427. package/lib/tree/range.js.map +0 -1
  428. package/lib/tree/reference.d.ts +0 -76
  429. package/lib/tree/reference.d.ts.map +0 -1
  430. package/lib/tree/reference.js +0 -521
  431. package/lib/tree/reference.js.map +0 -1
  432. package/lib/tree/rest.d.ts +0 -15
  433. package/lib/tree/rest.d.ts.map +0 -1
  434. package/lib/tree/rest.js +0 -32
  435. package/lib/tree/rest.js.map +0 -1
  436. package/lib/tree/rules-raw.d.ts +0 -17
  437. package/lib/tree/rules-raw.d.ts.map +0 -1
  438. package/lib/tree/rules-raw.js +0 -37
  439. package/lib/tree/rules-raw.js.map +0 -1
  440. package/lib/tree/rules.d.ts +0 -262
  441. package/lib/tree/rules.d.ts.map +0 -1
  442. package/lib/tree/rules.js +0 -2359
  443. package/lib/tree/rules.js.map +0 -1
  444. package/lib/tree/ruleset.d.ts +0 -92
  445. package/lib/tree/ruleset.d.ts.map +0 -1
  446. package/lib/tree/ruleset.js +0 -528
  447. package/lib/tree/ruleset.js.map +0 -1
  448. package/lib/tree/selector-attr.d.ts +0 -31
  449. package/lib/tree/selector-attr.d.ts.map +0 -1
  450. package/lib/tree/selector-attr.js +0 -99
  451. package/lib/tree/selector-attr.js.map +0 -1
  452. package/lib/tree/selector-basic.d.ts +0 -24
  453. package/lib/tree/selector-basic.d.ts.map +0 -1
  454. package/lib/tree/selector-basic.js +0 -38
  455. package/lib/tree/selector-basic.js.map +0 -1
  456. package/lib/tree/selector-capture.d.ts +0 -23
  457. package/lib/tree/selector-capture.d.ts.map +0 -1
  458. package/lib/tree/selector-capture.js +0 -34
  459. package/lib/tree/selector-capture.js.map +0 -1
  460. package/lib/tree/selector-complex.d.ts +0 -40
  461. package/lib/tree/selector-complex.d.ts.map +0 -1
  462. package/lib/tree/selector-complex.js +0 -143
  463. package/lib/tree/selector-complex.js.map +0 -1
  464. package/lib/tree/selector-compound.d.ts +0 -16
  465. package/lib/tree/selector-compound.d.ts.map +0 -1
  466. package/lib/tree/selector-compound.js +0 -114
  467. package/lib/tree/selector-compound.js.map +0 -1
  468. package/lib/tree/selector-interpolated.d.ts +0 -23
  469. package/lib/tree/selector-interpolated.d.ts.map +0 -1
  470. package/lib/tree/selector-interpolated.js +0 -27
  471. package/lib/tree/selector-interpolated.js.map +0 -1
  472. package/lib/tree/selector-list.d.ts +0 -17
  473. package/lib/tree/selector-list.d.ts.map +0 -1
  474. package/lib/tree/selector-list.js +0 -174
  475. package/lib/tree/selector-list.js.map +0 -1
  476. package/lib/tree/selector-pseudo.d.ts +0 -42
  477. package/lib/tree/selector-pseudo.d.ts.map +0 -1
  478. package/lib/tree/selector-pseudo.js +0 -204
  479. package/lib/tree/selector-pseudo.js.map +0 -1
  480. package/lib/tree/selector-simple.d.ts +0 -5
  481. package/lib/tree/selector-simple.d.ts.map +0 -1
  482. package/lib/tree/selector-simple.js +0 -6
  483. package/lib/tree/selector-simple.js.map +0 -1
  484. package/lib/tree/selector.d.ts +0 -43
  485. package/lib/tree/selector.d.ts.map +0 -1
  486. package/lib/tree/selector.js +0 -56
  487. package/lib/tree/selector.js.map +0 -1
  488. package/lib/tree/sequence.d.ts +0 -43
  489. package/lib/tree/sequence.d.ts.map +0 -1
  490. package/lib/tree/sequence.js +0 -151
  491. package/lib/tree/sequence.js.map +0 -1
  492. package/lib/tree/tree.d.ts +0 -87
  493. package/lib/tree/tree.d.ts.map +0 -1
  494. package/lib/tree/tree.js +0 -2
  495. package/lib/tree/tree.js.map +0 -1
  496. package/lib/tree/url.d.ts +0 -18
  497. package/lib/tree/url.d.ts.map +0 -1
  498. package/lib/tree/url.js +0 -35
  499. package/lib/tree/url.js.map +0 -1
  500. package/lib/tree/util/__tests__/debug-log.d.ts +0 -1
  501. package/lib/tree/util/__tests__/debug-log.d.ts.map +0 -1
  502. package/lib/tree/util/__tests__/debug-log.js +0 -36
  503. package/lib/tree/util/__tests__/debug-log.js.map +0 -1
  504. package/lib/tree/util/calculate.d.ts +0 -3
  505. package/lib/tree/util/calculate.d.ts.map +0 -1
  506. package/lib/tree/util/calculate.js +0 -10
  507. package/lib/tree/util/calculate.js.map +0 -1
  508. package/lib/tree/util/cast.d.ts +0 -10
  509. package/lib/tree/util/cast.d.ts.map +0 -1
  510. package/lib/tree/util/cast.js +0 -87
  511. package/lib/tree/util/cast.js.map +0 -1
  512. package/lib/tree/util/cloning.d.ts +0 -4
  513. package/lib/tree/util/cloning.d.ts.map +0 -1
  514. package/lib/tree/util/cloning.js +0 -8
  515. package/lib/tree/util/cloning.js.map +0 -1
  516. package/lib/tree/util/collections.d.ts +0 -57
  517. package/lib/tree/util/collections.d.ts.map +0 -1
  518. package/lib/tree/util/collections.js +0 -136
  519. package/lib/tree/util/collections.js.map +0 -1
  520. package/lib/tree/util/compare.d.ts +0 -11
  521. package/lib/tree/util/compare.d.ts.map +0 -1
  522. package/lib/tree/util/compare.js +0 -89
  523. package/lib/tree/util/compare.js.map +0 -1
  524. package/lib/tree/util/extend-helpers.d.ts +0 -2
  525. package/lib/tree/util/extend-helpers.d.ts.map +0 -1
  526. package/lib/tree/util/extend-helpers.js +0 -2
  527. package/lib/tree/util/extend-helpers.js.map +0 -1
  528. package/lib/tree/util/extend-roots.d.ts +0 -37
  529. package/lib/tree/util/extend-roots.d.ts.map +0 -1
  530. package/lib/tree/util/extend-roots.js +0 -700
  531. package/lib/tree/util/extend-roots.js.map +0 -1
  532. package/lib/tree/util/extend-roots.old.d.ts +0 -132
  533. package/lib/tree/util/extend-roots.old.d.ts.map +0 -1
  534. package/lib/tree/util/extend-roots.old.js +0 -2272
  535. package/lib/tree/util/extend-roots.old.js.map +0 -1
  536. package/lib/tree/util/extend-trace-debug.d.ts +0 -13
  537. package/lib/tree/util/extend-trace-debug.d.ts.map +0 -1
  538. package/lib/tree/util/extend-trace-debug.js +0 -34
  539. package/lib/tree/util/extend-trace-debug.js.map +0 -1
  540. package/lib/tree/util/extend-walk.d.ts +0 -53
  541. package/lib/tree/util/extend-walk.d.ts.map +0 -1
  542. package/lib/tree/util/extend-walk.js +0 -881
  543. package/lib/tree/util/extend-walk.js.map +0 -1
  544. package/lib/tree/util/extend.d.ts +0 -218
  545. package/lib/tree/util/extend.d.ts.map +0 -1
  546. package/lib/tree/util/extend.js +0 -3182
  547. package/lib/tree/util/extend.js.map +0 -1
  548. package/lib/tree/util/find-extendable-locations.d.ts +0 -2
  549. package/lib/tree/util/find-extendable-locations.d.ts.map +0 -1
  550. package/lib/tree/util/find-extendable-locations.js +0 -2
  551. package/lib/tree/util/find-extendable-locations.js.map +0 -1
  552. package/lib/tree/util/format.d.ts +0 -20
  553. package/lib/tree/util/format.d.ts.map +0 -1
  554. package/lib/tree/util/format.js +0 -67
  555. package/lib/tree/util/format.js.map +0 -1
  556. package/lib/tree/util/is-node.d.ts +0 -13
  557. package/lib/tree/util/is-node.d.ts.map +0 -1
  558. package/lib/tree/util/is-node.js +0 -43
  559. package/lib/tree/util/is-node.js.map +0 -1
  560. package/lib/tree/util/print.d.ts +0 -80
  561. package/lib/tree/util/print.d.ts.map +0 -1
  562. package/lib/tree/util/print.js +0 -205
  563. package/lib/tree/util/print.js.map +0 -1
  564. package/lib/tree/util/process-leading-is.d.ts +0 -25
  565. package/lib/tree/util/process-leading-is.d.ts.map +0 -1
  566. package/lib/tree/util/process-leading-is.js +0 -364
  567. package/lib/tree/util/process-leading-is.js.map +0 -1
  568. package/lib/tree/util/recursion-helper.d.ts +0 -15
  569. package/lib/tree/util/recursion-helper.d.ts.map +0 -1
  570. package/lib/tree/util/recursion-helper.js +0 -43
  571. package/lib/tree/util/recursion-helper.js.map +0 -1
  572. package/lib/tree/util/regex.d.ts +0 -4
  573. package/lib/tree/util/regex.d.ts.map +0 -1
  574. package/lib/tree/util/regex.js +0 -4
  575. package/lib/tree/util/regex.js.map +0 -1
  576. package/lib/tree/util/registry-utils.d.ts +0 -192
  577. package/lib/tree/util/registry-utils.d.ts.map +0 -1
  578. package/lib/tree/util/registry-utils.js +0 -1214
  579. package/lib/tree/util/registry-utils.js.map +0 -1
  580. package/lib/tree/util/ruleset-trace.d.ts +0 -4
  581. package/lib/tree/util/ruleset-trace.d.ts.map +0 -1
  582. package/lib/tree/util/ruleset-trace.js +0 -14
  583. package/lib/tree/util/ruleset-trace.js.map +0 -1
  584. package/lib/tree/util/selector-compare.d.ts +0 -2
  585. package/lib/tree/util/selector-compare.d.ts.map +0 -1
  586. package/lib/tree/util/selector-compare.js +0 -2
  587. package/lib/tree/util/selector-compare.js.map +0 -1
  588. package/lib/tree/util/selector-match-core.d.ts +0 -184
  589. package/lib/tree/util/selector-match-core.d.ts.map +0 -1
  590. package/lib/tree/util/selector-match-core.js +0 -1603
  591. package/lib/tree/util/selector-match-core.js.map +0 -1
  592. package/lib/tree/util/selector-utils.d.ts +0 -30
  593. package/lib/tree/util/selector-utils.d.ts.map +0 -1
  594. package/lib/tree/util/selector-utils.js +0 -100
  595. package/lib/tree/util/selector-utils.js.map +0 -1
  596. package/lib/tree/util/serialize-helper.d.ts +0 -13
  597. package/lib/tree/util/serialize-helper.d.ts.map +0 -1
  598. package/lib/tree/util/serialize-helper.js +0 -387
  599. package/lib/tree/util/serialize-helper.js.map +0 -1
  600. package/lib/tree/util/serialize-types.d.ts +0 -9
  601. package/lib/tree/util/serialize-types.d.ts.map +0 -1
  602. package/lib/tree/util/serialize-types.js +0 -216
  603. package/lib/tree/util/serialize-types.js.map +0 -1
  604. package/lib/tree/util/should-operate.d.ts +0 -23
  605. package/lib/tree/util/should-operate.d.ts.map +0 -1
  606. package/lib/tree/util/should-operate.js +0 -46
  607. package/lib/tree/util/should-operate.js.map +0 -1
  608. package/lib/tree/util/sourcemap.d.ts +0 -7
  609. package/lib/tree/util/sourcemap.d.ts.map +0 -1
  610. package/lib/tree/util/sourcemap.js +0 -25
  611. package/lib/tree/util/sourcemap.js.map +0 -1
  612. package/lib/types/config.d.ts +0 -205
  613. package/lib/types/config.d.ts.map +0 -1
  614. package/lib/types/config.js +0 -2
  615. package/lib/types/config.js.map +0 -1
  616. package/lib/types/index.d.ts +0 -15
  617. package/lib/types/index.d.ts.map +0 -1
  618. package/lib/types/index.js +0 -3
  619. package/lib/types/index.js.map +0 -1
  620. package/lib/types/modes.d.ts.map +0 -1
  621. package/lib/types/modes.js +0 -2
  622. package/lib/types/modes.js.map +0 -1
  623. package/lib/types.d.ts +0 -61
  624. package/lib/types.d.ts.map +0 -1
  625. package/lib/types.js +0 -2
  626. package/lib/types.js.map +0 -1
  627. package/lib/use-webpack-resolver.d.ts +0 -9
  628. package/lib/use-webpack-resolver.d.ts.map +0 -1
  629. package/lib/use-webpack-resolver.js +0 -41
  630. package/lib/use-webpack-resolver.js.map +0 -1
  631. package/lib/visitor/index.d.ts +0 -136
  632. package/lib/visitor/index.d.ts.map +0 -1
  633. package/lib/visitor/index.js +0 -135
  634. package/lib/visitor/index.js.map +0 -1
  635. package/lib/visitor/less-visitor.d.ts +0 -7
  636. package/lib/visitor/less-visitor.d.ts.map +0 -1
  637. package/lib/visitor/less-visitor.js.map +0 -1
@@ -0,0 +1,2020 @@
1
+ import { Context } from '../../context.js';
2
+ import { EVAL, Node } from '../node-base.js';
3
+
4
+ import { Bool } from '../bool.js';
5
+ import type { Condition } from '../condition.js';
6
+ import { Nil } from '../nil.js';
7
+ import { Rules } from '../rules.js';
8
+ import type { Ruleset } from '../ruleset.js';
9
+ import type { AtRule } from '../at-rule.js';
10
+ import type { VarDeclaration } from '../declaration-var.js';
11
+ import { VarDeclaration as VarDeclarationCtor } from '../declaration-var.js';
12
+ import { list, type List } from '../list.js';
13
+ import { Sequence } from '../sequence.js';
14
+ import { Any } from '../any.js';
15
+ import { N } from '../node-type.js';
16
+ import { CALLER, CANONICAL, F_VISIBLE } from '../node.js';
17
+ import { isNode } from './is-node.js';
18
+ import { comparePosition } from './compare.js';
19
+ import { getParent, getSourceParent, setChildren, setParent, setSourceParent } from './field-helpers.js';
20
+ import { addParentEdge } from './cursor.js';
21
+ import type { Mixin } from '../mixin.js';
22
+ import { isThenable, type MaybePromise } from '@jesscss/awaitable-pipe';
23
+ import { cast } from './cast.js';
24
+ import { isPlainObject } from './collections.js';
25
+ import type { MixinEntry } from '../rules.js';
26
+ import type { RenderKey } from '../node-base.js';
27
+ import { getCurrentParentNode } from './selector-utils.js';
28
+
29
+ export const enum MixinDefaultGroup {
30
+ FalseEither = -1,
31
+ None = 0,
32
+ True = 1,
33
+ False = 2
34
+ }
35
+
36
+ export type PreparedMixinCandidateInvocation = {
37
+ rules: Rules;
38
+ params: List<Node> | undefined;
39
+ outerRules: Rules | undefined;
40
+ };
41
+
42
+ export type EvaluatedMixinGuard = {
43
+ passes: boolean;
44
+ outerRules: Rules | undefined;
45
+ defaultGroup?: MixinDefaultGroup;
46
+ };
47
+
48
+ export type PendingMixinDefaultCandidate<TCandidate = unknown> = {
49
+ candidate: TCandidate;
50
+ rules: Rules;
51
+ outerRules?: Rules;
52
+ params?: List<Node>;
53
+ group: MixinDefaultGroup;
54
+ lookupScope?: Rules;
55
+ };
56
+
57
+ export type ProcessPreparedMixinCandidateOptions<TCandidate> = {
58
+ candidate: TCandidate;
59
+ rules: Rules;
60
+ params?: List<Node>;
61
+ outerRules?: Rules;
62
+ guard?: Condition | Bool;
63
+ parent: Node | undefined;
64
+ guardScopeChildren?: readonly Node[];
65
+ hasAnyDefault: boolean;
66
+ candidateHasDefault: boolean;
67
+ context: Context;
68
+ evaluateCandidateOutput: (
69
+ candidate: TCandidate,
70
+ rules: Rules,
71
+ outerRules: Rules | undefined,
72
+ params: List<Node> | undefined,
73
+ ) => MaybePromise<void>;
74
+ };
75
+
76
+ const bindableParamTemplates = new WeakMap<Node, VarDeclaration>();
77
+ const restParamTemplates = new WeakMap<Node, VarDeclaration>();
78
+ function getCurrentRulesetGuard(
79
+ ruleset: Ruleset,
80
+ context: Context
81
+ ): Node | undefined {
82
+ return ruleset.get('guard', context) as Node | undefined;
83
+ }
84
+
85
+ function getCurrentMixinParams(
86
+ mixin: Mixin,
87
+ context: Context | RenderKey | undefined
88
+ ): List<Node> | undefined {
89
+ return mixin.get('params', context) as List<Node> | undefined;
90
+ }
91
+
92
+ function getCanonicalSourceParent(
93
+ node: Node | undefined
94
+ ): Node | undefined {
95
+ return node?.sourceParent;
96
+ }
97
+
98
+ function getBindableParamTemplate(
99
+ param: Node,
100
+ context: Context
101
+ ): VarDeclaration {
102
+ const cached = bindableParamTemplates.get(param);
103
+ if (cached) {
104
+ return cached;
105
+ }
106
+ const name = String(param.valueOf());
107
+ const template = new VarDeclarationCtor({
108
+ name: new Any(name, { role: 'property' }),
109
+ value: new Nil()
110
+ }, { paramVar: true }, param.location, context.treeContext);
111
+ bindableParamTemplates.set(param, template);
112
+ return template;
113
+ }
114
+
115
+ function getRestParamTemplate(
116
+ param: Node,
117
+ restName: string,
118
+ context: Context
119
+ ): VarDeclaration {
120
+ const cached = restParamTemplates.get(param);
121
+ if (cached) {
122
+ return cached;
123
+ }
124
+ const template = new VarDeclarationCtor({
125
+ name: new Any(restName, { role: 'property' }),
126
+ value: new Nil()
127
+ }, { paramVar: true }, param.location, context.treeContext);
128
+ restParamTemplates.set(param, template);
129
+ return template;
130
+ }
131
+
132
+ /**
133
+ * Follow a Rules node back to its canonical source root. Mixin/ruleset
134
+ * candidate setup wants this shared notion of "the source rules subtree" so
135
+ * that eval state subtrees are always created against the canonical backing body.
136
+ */
137
+ export function getRootSourceRules(rules: Rules): Rules {
138
+ let current = rules;
139
+ const seen = new Set<Rules>();
140
+ while (current.sourceNode && isNode(current.sourceNode, N.Rules)) {
141
+ const next = current.sourceNode as Rules;
142
+ if (next === current || seen.has(next)) {
143
+ break;
144
+ }
145
+ seen.add(current);
146
+ current = next;
147
+ }
148
+ return current;
149
+ }
150
+
151
+ /**
152
+ * Resolve the canonical source rules for a mixin-like candidate and create a
153
+ * per-call eval state subtree when a session is active.
154
+ */
155
+ export function createMixinCandidateInstanceRoot(
156
+ _candidate: MixinEntry,
157
+ _context: Context
158
+ ): undefined {
159
+ return undefined;
160
+ }
161
+
162
+ /**
163
+ * Apply the final return policy for mixin invocation output.
164
+ *
165
+ * - Context receivers get a live `Rules` result (or `Nil` if empty), with
166
+ * `ruleCounter` assigned on first return.
167
+ * - Non-Context receivers get a plain object view, preserving legacy
168
+ * `getFunctionFromMixins()` semantics.
169
+ */
170
+ export function finalizeMixinInvocationReturn(
171
+ output: Rules,
172
+ receiver: Context | Node
173
+ ): Rules | Nil | ReturnType<Rules['toObject']> {
174
+ if (receiver instanceof Context) {
175
+ output.index ??= receiver.ruleCounter++;
176
+ if (output.value.length === 0) {
177
+ return new Nil();
178
+ }
179
+ return output;
180
+ }
181
+ return output.toObject();
182
+ }
183
+
184
+ /**
185
+ * Bind one mixin param through the active eval state subtree instead of mutating the
186
+ * canonical VarDeclaration. This is the smallest useful primitive behind direct
187
+ * mixin invocation.
188
+ */
189
+ export function bindMixinParamValue(
190
+ param: VarDeclaration,
191
+ value: Node,
192
+ context: Context
193
+ ): void {
194
+ param.value = value;
195
+ param.adopt(value, context);
196
+ }
197
+
198
+ /**
199
+ * Create the transient scope that holds bound mixin parameters. This is the
200
+ * direct replacement for the inlined outerRules construction in
201
+ * getFunctionFromMixins().
202
+ */
203
+ export function createMixinParamScope(
204
+ index: number,
205
+ renderKey: RenderKey
206
+ ): Rules {
207
+ const scope = Rules.create([], {
208
+ rulesVisibility: {
209
+ Ruleset: 'public',
210
+ Declaration: 'public',
211
+ VarDeclaration: 'public',
212
+ Mixin: 'public'
213
+ }
214
+ });
215
+ scope.index = index;
216
+ scope.renderKey = renderKey;
217
+ return scope;
218
+ }
219
+
220
+ function createRenderOwnedSequence(
221
+ items: readonly Node[],
222
+ renderKey: RenderKey,
223
+ context: Context
224
+ ): Sequence {
225
+ const sequence = new Sequence([], { forceSpacing: true }, undefined, context.treeContext);
226
+ sequence.renderKey = renderKey;
227
+ (sequence as unknown as { value: Node[] }).value = [...items];
228
+ const edgeContext = { ...context, renderKey };
229
+ for (const item of items) {
230
+ setParent(item, sequence, edgeContext);
231
+ }
232
+ return sequence;
233
+ }
234
+
235
+ /**
236
+ * Register already-bound parameter declarations into the transient mixin scope.
237
+ * Matching/rest conversion still happens outside this helper; this primitive is
238
+ * only responsible for making those params visible to lookup.
239
+ */
240
+ export function populateMixinParamScope(
241
+ scope: Rules,
242
+ params: List<Node>,
243
+ context: Context
244
+ ): void {
245
+ const paramItems = params.get('value');
246
+ for (let i = 0; i < paramItems.length; i++) {
247
+ const param = paramItems[i]!;
248
+ if (!isNode(param, N.VarDeclaration)) {
249
+ continue;
250
+ }
251
+ if (param.index === undefined) {
252
+ param.index = -(i + 1);
253
+ }
254
+ param.options ??= {};
255
+ param.options.paramVar = true;
256
+ param.removeFlag(F_VISIBLE);
257
+ const name = String(param.get('name', scope.renderKey).valueOf());
258
+ setParent(param, scope, context);
259
+ scope.setInvocationBinding(name, { declaration: param });
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Define the Less-style @arguments variable inside the transient mixin scope.
265
+ * This stays a separate primitive so direct mixin invocation can reuse it
266
+ * without dragging along the rest of getFunctionFromMixins().
267
+ */
268
+ export function defineMixinArgumentsInScope(
269
+ scope: Rules,
270
+ params: List<Node> | undefined,
271
+ nodeArgs: readonly Node[],
272
+ context: Context
273
+ ): void {
274
+ scope.setInvocationBinding('arguments', {
275
+ factory: (rules, bindingContext) => {
276
+ const nextRenderKey = rules.renderKey;
277
+ const paramValues = params?.get('value', nextRenderKey)
278
+ .filter((p): p is VarDeclaration => isNode(p, N.VarDeclaration))
279
+ .map(p => p.get('value', nextRenderKey))
280
+ .filter((value): value is Node => value instanceof Node);
281
+ const argumentNodes = (paramValues && paramValues.length > 0) ? paramValues : nodeArgs;
282
+ const argumentsArgs: Node[] = [];
283
+ for (const argNode of argumentNodes) {
284
+ if (isNode(argNode, N.Sequence) && (argNode as Sequence).get('value', nextRenderKey).length > 1) {
285
+ argumentsArgs.push(...(argNode as Sequence).get('value', nextRenderKey));
286
+ } else {
287
+ argumentsArgs.push(argNode);
288
+ }
289
+ }
290
+ const argumentsDecl = new VarDeclarationCtor({
291
+ name: new Any('arguments', { role: 'property' }),
292
+ value: createRenderOwnedSequence(argumentsArgs, nextRenderKey, bindingContext ?? context)
293
+ }, { readonly: true, paramVar: true });
294
+ argumentsDecl.removeFlag(F_VISIBLE);
295
+ argumentsDecl.renderKey = nextRenderKey;
296
+ argumentsDecl.preEvaluated = true;
297
+ argumentsDecl.evaluated = true;
298
+ return argumentsDecl;
299
+ }
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Seed a fresh reset-eval guard scope from the active param scope without
305
+ * touching canonical parentage. The returned scope is safe to reuse for a
306
+ * single guard probe.
307
+ */
308
+ export function seedMixinGuardScope(
309
+ scope: Rules | undefined,
310
+ guardParent: Node | undefined,
311
+ guardNode: Node | undefined,
312
+ context: Context,
313
+ scopeChildren?: readonly Node[]
314
+ ): Rules {
315
+ const nextScope = scope ?? Rules.create([]);
316
+ setParent(nextScope, guardParent, context);
317
+ const activeChildren = scopeChildren ?? nextScope.getRegistryChildren(context);
318
+ if (scopeChildren) {
319
+ setChildren(nextScope, activeChildren, context, { markDirty: false });
320
+ } else {
321
+ for (const child of activeChildren) {
322
+ setParent(child, nextScope, context);
323
+ }
324
+ }
325
+ if (guardNode) {
326
+ nextScope.adopt(guardNode, context);
327
+ }
328
+ return nextScope;
329
+ }
330
+
331
+ function captureMixinScopeSnapshot(
332
+ scope: Rules | undefined,
333
+ scopeChildren: readonly Node[] | undefined,
334
+ context: Context
335
+ ): Rules | undefined {
336
+ if (!scope) {
337
+ return undefined;
338
+ }
339
+ const capturedChildren = scopeChildren ?? scope.getRegistryChildren(context);
340
+ const captured = scope.createPlacementWrapperWithChildren(
341
+ capturedChildren,
342
+ scope.renderKey
343
+ );
344
+ captured.parent = getParent(scope, context);
345
+ captured.sourceParent = scope.sourceParent;
346
+ return captured;
347
+ }
348
+
349
+ /**
350
+ * Prepare the transient scope used by a single mixin invocation. This is the
351
+ * smallest complete lookup-ready scope primitive for direct canonical-body eval:
352
+ * the caller gets a param scope with registered params / @arguments and the
353
+ * canonical body attached through state parent shadow only.
354
+ */
355
+ export function prepareMixinInvocationScope(
356
+ definitionParent: Node | undefined,
357
+ placementParent: Node | undefined,
358
+ sourceParent: Node | undefined,
359
+ index: number,
360
+ renderKey: RenderKey,
361
+ params: List<Node> | undefined,
362
+ nodeArgs: readonly Node[],
363
+ context: Context
364
+ ): Rules | undefined {
365
+ if (!params) {
366
+ return undefined;
367
+ }
368
+ const scope = createMixinParamScope(index, renderKey);
369
+ populateMixinParamScope(scope, params, context);
370
+ defineMixinArgumentsInScope(scope, params, nodeArgs, context);
371
+ scope.parent = placementParent ?? definitionParent;
372
+ scope.sourceParent = sourceParent;
373
+ return scope;
374
+ }
375
+
376
+ export function createMixinInvocationRules(
377
+ body: Rules,
378
+ lookupParent: Node | undefined,
379
+ lexicalSourceParent: Node | undefined,
380
+ sourceParent: Node | undefined,
381
+ index: number,
382
+ context: Context,
383
+ renderKey: RenderKey
384
+ ): Rules {
385
+ const wrapper = body.createShallowBodyWrapper(undefined, renderKey);
386
+ wrapper.index = index;
387
+ wrapper.parent = lookupParent;
388
+ wrapper.sourceParent = sourceParent ?? lexicalSourceParent;
389
+
390
+ wrapper.options = {
391
+ ...wrapper.options,
392
+ rulesVisibility: {
393
+ ...(wrapper.options.rulesVisibility ?? {}),
394
+ VarDeclaration: 'public'
395
+ }
396
+ };
397
+ return wrapper;
398
+ }
399
+
400
+ function bindStructuralSourceParent(
401
+ node: Node,
402
+ sourceParent: Node,
403
+ context: Context,
404
+ seen: Set<Node> = new Set()
405
+ ): void {
406
+ if (seen.has(node)) {
407
+ return;
408
+ }
409
+ seen.add(node);
410
+ node.sourceParent = sourceParent;
411
+ setSourceParent(node, sourceParent, context);
412
+ if (
413
+ node.renderKey !== undefined
414
+ && node.renderKey !== CANONICAL
415
+ && node.renderKey !== context.renderKey
416
+ ) {
417
+ setSourceParent(node, sourceParent, {
418
+ ...context,
419
+ renderKey: node.renderKey
420
+ } as Context);
421
+ }
422
+ const childKeys = (node.constructor as typeof Node).childKeys;
423
+ if (!childKeys) {
424
+ return;
425
+ }
426
+ for (const key of childKeys) {
427
+ const value = (node as unknown as Record<string, unknown>)[key];
428
+ if (Array.isArray(value)) {
429
+ for (const item of value) {
430
+ if (item instanceof Node) {
431
+ bindStructuralSourceParent(item, sourceParent, context, seen);
432
+ }
433
+ }
434
+ continue;
435
+ }
436
+ if (value instanceof Node) {
437
+ bindStructuralSourceParent(value, sourceParent, context, seen);
438
+ }
439
+ }
440
+ }
441
+
442
+ function bindStructuralParentTree(
443
+ node: Node,
444
+ parent: Node,
445
+ context: Context,
446
+ seen: Set<Node> = new Set()
447
+ ): void {
448
+ if (seen.has(node)) {
449
+ return;
450
+ }
451
+ seen.add(node);
452
+ setParent(node, parent, context);
453
+ const childKeys = (node.constructor as typeof Node).childKeys;
454
+ if (!childKeys) {
455
+ return;
456
+ }
457
+ for (const key of childKeys) {
458
+ const value = (node as unknown as Record<string, unknown>)[key];
459
+ if (Array.isArray(value)) {
460
+ for (const item of value) {
461
+ if (item instanceof Node) {
462
+ bindStructuralParentTree(item, node, context, seen);
463
+ }
464
+ }
465
+ continue;
466
+ }
467
+ if (value instanceof Node) {
468
+ bindStructuralParentTree(value, node, context, seen);
469
+ }
470
+ }
471
+ }
472
+
473
+ function projectParentChainForRenderKey(
474
+ node: Node | undefined,
475
+ sourceContext: Context,
476
+ targetContext: Context,
477
+ seen: Set<Node> = new Set()
478
+ ): void {
479
+ if (!node || seen.has(node)) {
480
+ return;
481
+ }
482
+ seen.add(node);
483
+ const nodeSourceContext = {
484
+ ...sourceContext,
485
+ renderKey: node.renderKey ?? sourceContext.renderKey
486
+ } as Context;
487
+ const parent = getParent(node, nodeSourceContext);
488
+ if (!parent) {
489
+ return;
490
+ }
491
+ if (getParent(node, targetContext) !== parent) {
492
+ setParent(node, parent, targetContext);
493
+ }
494
+ projectParentChainForRenderKey(parent, sourceContext, targetContext, seen);
495
+ }
496
+
497
+ function anchorCallSiteValue(
498
+ value: Node,
499
+ sourceParent: Node,
500
+ context: Context
501
+ ): void {
502
+ if (
503
+ getSourceParent(value, context) === undefined
504
+ && isNode(value, N.Reference | N.Mixin | N.Sequence | N.List | N.Rules | N.Ruleset | N.AtRule)
505
+ ) {
506
+ setSourceParent(value, sourceParent, context);
507
+ }
508
+ }
509
+
510
+ function getUsableInvocationSourceParent(
511
+ sourceParent: Node | undefined,
512
+ fallback: Node | undefined
513
+ ): Node | undefined {
514
+ if (sourceParent && !isNode(sourceParent, N.Reference | N.Call)) {
515
+ return sourceParent;
516
+ }
517
+ return fallback;
518
+ }
519
+
520
+ /**
521
+ * Normalize mixin params for invocation-time lookup registration.
522
+ *
523
+ * Rest params must become VarDeclarations before they can participate in the
524
+ * transient param scope. Keep that conversion here instead of inline inside the
525
+ * candidate loop.
526
+ */
527
+ export function normalizeMixinInvocationParams(
528
+ params: List<Node> | undefined,
529
+ context: Context
530
+ ): List<Node> | undefined {
531
+ if (!params) {
532
+ return undefined;
533
+ }
534
+
535
+ let unnamedRestCount = 0;
536
+ const paramItems = params.get('value');
537
+ for (let i = 0; i < paramItems.length; i++) {
538
+ const param = paramItems[i]!;
539
+ if (param.type !== 'Rest') {
540
+ continue;
541
+ }
542
+
543
+ let restName: string;
544
+ if (typeof (param as any).value === 'string') {
545
+ restName = (param as any).value;
546
+ } else {
547
+ restName = unnamedRestCount === 0 ? 'rest' : `rest${unnamedRestCount + 1}`;
548
+ unnamedRestCount++;
549
+ }
550
+
551
+ const restValue = isNode((param as any).value)
552
+ ? (param as any).value as Node
553
+ : (
554
+ context.treeContext?.file
555
+ ? new Sequence([])
556
+ : new Any(restName, { role: 'property' })
557
+ );
558
+ const restVarDecl = new VarDeclarationCtor({
559
+ name: new Any(restName, { role: 'property' }),
560
+ value: restValue
561
+ }, { paramVar: true });
562
+
563
+ params.value[i] = restVarDecl;
564
+ params.adopt(restVarDecl, context);
565
+ }
566
+
567
+ return params;
568
+ }
569
+
570
+ function buildBoundMixinParams(
571
+ params: List<Node> | undefined,
572
+ nodeArgs: readonly Node[],
573
+ bindingSourceParent: Node | undefined,
574
+ renderKey: RenderKey,
575
+ context: Context
576
+ ): List<Node> | undefined {
577
+ if (!params) {
578
+ return undefined;
579
+ }
580
+
581
+ const namedArgs = new Map<string, Node>();
582
+ const positionalArgs: Node[] = [];
583
+ for (const arg of nodeArgs) {
584
+ if (isNode(arg, N.VarDeclaration)) {
585
+ const argName = String((arg as VarDeclaration).get('name').valueOf());
586
+ const argValue = (arg as VarDeclaration).get('value') as Node;
587
+ namedArgs.set(argName, argValue);
588
+ } else {
589
+ positionalArgs.push(arg);
590
+ }
591
+ }
592
+
593
+ const bindingContext = { ...context, renderKey };
594
+ const boundParams = list([], params.options ? { ...params.options } : undefined);
595
+ let positionalIndex = 0;
596
+ const cloneDefaultParamValue = (value: Node): Node => {
597
+ return value.clone(false, undefined, bindingContext);
598
+ };
599
+ const cloneBoundParamTemplate = (
600
+ param: VarDeclaration,
601
+ boundValue: Node | undefined
602
+ ): VarDeclaration => {
603
+ const boundParam = param.clone(false, undefined, bindingContext) as VarDeclaration;
604
+ boundParam.renderKey = renderKey;
605
+ boundParam.options = { ...(boundParam.options ?? {}), paramVar: true };
606
+ boundParam.preEvaluated = true;
607
+ boundParam.evaluated = true;
608
+ if (boundValue) {
609
+ boundParam.setCurrentValue(boundValue, bindingContext);
610
+ }
611
+ return boundParam;
612
+ };
613
+
614
+ for (let index = 0; index < params.get('value').length; index++) {
615
+ const param = params.get('value')[index]!;
616
+
617
+ if (isNode(param, N.VarDeclaration)) {
618
+ const paramDecl = param as VarDeclaration;
619
+ const name = String(paramDecl.get('name').valueOf());
620
+
621
+ const hasNamedArg = namedArgs.has(name);
622
+ const hasPositionalArg = positionalIndex < positionalArgs.length;
623
+ const boundValue = hasNamedArg
624
+ ? namedArgs.get(name)!
625
+ : hasPositionalArg
626
+ ? positionalArgs[positionalIndex++]!
627
+ : cloneDefaultParamValue(paramDecl.get('value'));
628
+ if (
629
+ bindingSourceParent
630
+ && isNode(boundValue)
631
+ ) {
632
+ anchorCallSiteValue(boundValue, bindingSourceParent, bindingContext);
633
+ }
634
+ const boundParam = cloneBoundParamTemplate(paramDecl, boundValue);
635
+ boundParam.index = paramDecl.index ?? -(index + 1);
636
+ boundParams.push(boundParam);
637
+ namedArgs.delete(name);
638
+ continue;
639
+ }
640
+
641
+ if (isNode(param, N.Any) && param.role === 'property') {
642
+ const name = String(param.valueOf());
643
+ const hasNamedArg = namedArgs.has(name);
644
+ const hasPositionalArg = positionalIndex < positionalArgs.length;
645
+ const boundValue = hasNamedArg
646
+ ? namedArgs.get(name)!
647
+ : hasPositionalArg
648
+ ? positionalArgs[positionalIndex++]!
649
+ : undefined;
650
+ if (
651
+ boundValue
652
+ && (hasNamedArg || hasPositionalArg)
653
+ && bindingSourceParent
654
+ && isNode(boundValue)
655
+ ) {
656
+ anchorCallSiteValue(boundValue, bindingSourceParent, bindingContext);
657
+ }
658
+ const template = getBindableParamTemplate(param, context);
659
+ const boundParam = cloneBoundParamTemplate(template, boundValue);
660
+ boundParam.index = -(index + 1);
661
+ boundParams.push(boundParam);
662
+ namedArgs.delete(name);
663
+ continue;
664
+ }
665
+
666
+ if (param.type === 'Rest') {
667
+ const restName = typeof (param as unknown as { value?: string }).value === 'string'
668
+ ? String((param as unknown as { value: string }).value)
669
+ : 'rest';
670
+ const restValues = positionalArgs.slice(positionalIndex);
671
+ positionalIndex = positionalArgs.length;
672
+ const restValue = restValues.length > 0
673
+ ? createRenderOwnedSequence(restValues, renderKey, context)
674
+ : (
675
+ context.treeContext?.file
676
+ ? createRenderOwnedSequence([], renderKey, context)
677
+ : new Any(restName, { role: 'property' })
678
+ );
679
+ const restTemplate = getRestParamTemplate(param, restName, context);
680
+ const restVarDecl = cloneBoundParamTemplate(restTemplate, restValue);
681
+ restVarDecl.index = -(index + 1);
682
+ boundParams.push(restVarDecl);
683
+ continue;
684
+ }
685
+
686
+ // Non-binding pattern params still consume a positional argument slot
687
+ // when matched, so later bindable params line up with the correct arg.
688
+ if (positionalIndex < positionalArgs.length) {
689
+ positionalIndex++;
690
+ }
691
+ }
692
+
693
+ return boundParams;
694
+ }
695
+
696
+ /**
697
+ * Prepare the normal mixin-candidate body for direct invocation.
698
+ *
699
+ * This is the slice of the old candidate loop that wires per-call identity,
700
+ * visibility, parent/source provenance, param normalization, and lookup scope
701
+ * construction before guard evaluation or output shaping runs.
702
+ */
703
+ export function prepareMixinCandidateInvocation(
704
+ rules: Rules,
705
+ params: List<Node> | undefined,
706
+ parent: Node | undefined,
707
+ sourceParent: Node | undefined,
708
+ index: number,
709
+ nodeArgs: readonly Node[],
710
+ context: Context
711
+ ): PreparedMixinCandidateInvocation {
712
+ const renderKey = context.nextRenderKey();
713
+ const bindingSourceParent = sourceParent ?? context.caller;
714
+ const boundParams = buildBoundMixinParams(
715
+ params,
716
+ nodeArgs,
717
+ bindingSourceParent,
718
+ renderKey,
719
+ context
720
+ );
721
+ const outerRules = boundParams
722
+ ? prepareMixinInvocationScope(
723
+ parent,
724
+ parent,
725
+ sourceParent,
726
+ index,
727
+ renderKey,
728
+ boundParams,
729
+ nodeArgs,
730
+ context
731
+ )
732
+ : undefined;
733
+ const lookupParent = outerRules ?? parent;
734
+ const sourceRules = getRootSourceRules(rules);
735
+ const invocationRules = createMixinInvocationRules(
736
+ sourceRules,
737
+ lookupParent,
738
+ parent,
739
+ sourceParent,
740
+ index,
741
+ context,
742
+ renderKey
743
+ );
744
+
745
+ return {
746
+ rules: invocationRules,
747
+ params: boundParams,
748
+ outerRules
749
+ };
750
+ }
751
+
752
+ /**
753
+ * Run an evaluation step with the mixin invocation scope as the active lookup
754
+ * scope, then restore the caller's prior rulesContext.
755
+ */
756
+ export function withMixinLookupScope<T>(
757
+ scope: Rules | undefined,
758
+ lookupScope: Rules | undefined,
759
+ context: Context,
760
+ fn: () => MaybePromise<T>
761
+ ): MaybePromise<T> {
762
+ const previousRulesContext = context.rulesContext;
763
+ const previousRenderKey = context.renderKey;
764
+ const previousLookupScope = context.lookupScope;
765
+ if (scope) {
766
+ context.rulesContext = scope;
767
+ }
768
+ context.lookupScope = lookupScope;
769
+ context.renderKey = scope?.renderKey ?? previousRenderKey;
770
+ try {
771
+ const out = fn();
772
+ if (isThenable(out)) {
773
+ return (out as Promise<T>).finally(() => {
774
+ context.rulesContext = previousRulesContext;
775
+ context.renderKey = previousRenderKey;
776
+ context.lookupScope = previousLookupScope;
777
+ });
778
+ }
779
+ context.rulesContext = previousRulesContext;
780
+ context.renderKey = previousRenderKey;
781
+ context.lookupScope = previousLookupScope;
782
+ return out;
783
+ } catch (error) {
784
+ context.rulesContext = previousRulesContext;
785
+ context.renderKey = previousRenderKey;
786
+ context.lookupScope = previousLookupScope;
787
+ throw error;
788
+ }
789
+ }
790
+
791
+ /**
792
+ * Evaluate one mixin guard candidate against the prepared invocation scope.
793
+ *
794
+ * This centralizes the reset-session guard probe behavior so the caller loop
795
+ * only has to deal with the result (`passes`, optional default group, evolved
796
+ * scope) instead of the probing mechanics.
797
+ */
798
+ export async function evaluateMixinGuardCandidate(
799
+ guardNode: Condition | Bool | undefined,
800
+ outerRules: Rules | undefined,
801
+ guardParent: Node | undefined,
802
+ context: Context,
803
+ scopeChildren: readonly Node[] | undefined,
804
+ hasDefault: boolean
805
+ ): Promise<EvaluatedMixinGuard> {
806
+ if (!guardNode) {
807
+ return { passes: true, outerRules };
808
+ }
809
+
810
+ const evaluateWithDefault = async (
811
+ isDefaultValue: boolean
812
+ ): Promise<{ passes: boolean; outerRules: Rules | undefined }> => {
813
+ const prevIsDefault = context.isDefault;
814
+ const previousRulesContext = context.rulesContext;
815
+ const previousRenderKey = context.renderKey;
816
+ const previousLookupScope = context.lookupScope;
817
+ try {
818
+ const nextScope = seedMixinGuardScope(
819
+ outerRules,
820
+ guardParent,
821
+ guardNode,
822
+ context,
823
+ scopeChildren
824
+ );
825
+ context.isDefault = isDefaultValue;
826
+ context.rulesContext = nextScope;
827
+ context.lookupScope = nextScope;
828
+ context.renderKey = nextScope.renderKey ?? previousRenderKey;
829
+ const probeResult = await guardNode.eval(context);
830
+ return {
831
+ passes: probeResult instanceof Bool && probeResult.value === true,
832
+ outerRules: nextScope
833
+ };
834
+ } finally {
835
+ context.rulesContext = previousRulesContext;
836
+ context.renderKey = previousRenderKey;
837
+ context.lookupScope = previousLookupScope;
838
+ context.isDefault = prevIsDefault;
839
+ }
840
+ };
841
+
842
+ if (hasDefault) {
843
+ const passWhenDefaultFalse = await evaluateWithDefault(false);
844
+ const passWhenDefaultTrue = await evaluateWithDefault(true);
845
+ const defaultGroup = classifyMixinDefaultGroup(
846
+ passWhenDefaultFalse.passes,
847
+ passWhenDefaultTrue.passes
848
+ );
849
+ return {
850
+ passes: defaultGroup !== undefined,
851
+ outerRules: passWhenDefaultTrue.outerRules ?? passWhenDefaultFalse.outerRules ?? outerRules,
852
+ defaultGroup
853
+ };
854
+ }
855
+
856
+ const result = await evaluateWithDefault(false);
857
+ return {
858
+ passes: result.passes,
859
+ outerRules: result.outerRules
860
+ };
861
+ }
862
+
863
+ /**
864
+ * Classify a default() guard probe pair into Less-style default groups.
865
+ */
866
+ export function classifyMixinDefaultGroup(
867
+ passWhenDefaultFalse: boolean,
868
+ passWhenDefaultTrue: boolean
869
+ ): MixinDefaultGroup | undefined {
870
+ if (!passWhenDefaultFalse && !passWhenDefaultTrue) {
871
+ return undefined;
872
+ }
873
+ if (passWhenDefaultFalse && passWhenDefaultTrue) {
874
+ return MixinDefaultGroup.None;
875
+ }
876
+ return passWhenDefaultTrue
877
+ ? MixinDefaultGroup.True
878
+ : MixinDefaultGroup.False;
879
+ }
880
+
881
+ /**
882
+ * Resolve which default() candidate groups should win for the current call.
883
+ */
884
+ export function resolveWinningMixinDefaultGroups(
885
+ groups: readonly MixinDefaultGroup[]
886
+ ): Set<MixinDefaultGroup> {
887
+ let hasDefNoneCandidate = false;
888
+ let hasDefTrueCandidate = false;
889
+ let hasDefFalseCandidate = false;
890
+
891
+ for (const group of groups) {
892
+ if (group === MixinDefaultGroup.True) {
893
+ hasDefTrueCandidate = true;
894
+ } else if (group === MixinDefaultGroup.False) {
895
+ hasDefFalseCandidate = true;
896
+ } else if (group === MixinDefaultGroup.None) {
897
+ hasDefNoneCandidate = true;
898
+ }
899
+ }
900
+
901
+ if (!hasDefNoneCandidate && hasDefTrueCandidate && hasDefFalseCandidate) {
902
+ throw new ReferenceError('Ambiguous use of default() while matching mixins.');
903
+ }
904
+
905
+ if (hasDefNoneCandidate) {
906
+ return new Set([MixinDefaultGroup.None, MixinDefaultGroup.False]);
907
+ }
908
+
909
+ return new Set([MixinDefaultGroup.True]);
910
+ }
911
+
912
+ /**
913
+ * Replay only the winning pending default() candidates with the correct lookup
914
+ * scope active for each candidate.
915
+ */
916
+ export async function replayWinningMixinDefaultCandidates<TCandidate>(
917
+ pendingCandidates: readonly PendingMixinDefaultCandidate<TCandidate>[],
918
+ context: Context,
919
+ evaluateCandidateOutput: (
920
+ pending: PendingMixinDefaultCandidate<TCandidate>
921
+ ) => MaybePromise<void>
922
+ ): Promise<void> {
923
+ if (pendingCandidates.length === 0) {
924
+ return;
925
+ }
926
+
927
+ const winningGroups = resolveWinningMixinDefaultGroups(
928
+ pendingCandidates.map(pending => pending.group)
929
+ );
930
+
931
+ for (const pending of pendingCandidates) {
932
+ if (!winningGroups.has(pending.group)) {
933
+ continue;
934
+ }
935
+ await withMixinLookupScope(
936
+ pending.outerRules ?? pending.rules,
937
+ pending.outerRules ?? pending.lookupScope ?? findRulesAncestor(pending.rules, context),
938
+ context,
939
+ () => evaluateCandidateOutput(pending)
940
+ );
941
+ }
942
+ }
943
+
944
+ /**
945
+ * Assemble the final mixin output `Rules` from already-evaluated candidate
946
+ * results, preserving source order and mixin-output visibility semantics.
947
+ */
948
+ export function assembleMixinInvocationOutput(
949
+ outputRules: Rules[],
950
+ restrictMixinOutputLookup: boolean,
951
+ context: Context
952
+ ): Rules {
953
+ outputRules.sort(comparePosition);
954
+
955
+ if (outputRules.length === 0) {
956
+ const output = Rules.create([], {
957
+ rulesVisibility: {
958
+ Ruleset: 'public',
959
+ Declaration: 'public',
960
+ VarDeclaration: 'public',
961
+ Mixin: 'public'
962
+ },
963
+ isMixinOutput: restrictMixinOutputLookup
964
+ });
965
+ output.renderKey = context.renderKey ?? output.renderKey;
966
+ return output;
967
+ }
968
+
969
+ if (outputRules.length === 1) {
970
+ const output = outputRules[0]!;
971
+ output.options.isMixinOutput ??= restrictMixinOutputLookup;
972
+ return output;
973
+ }
974
+
975
+ for (let i = 0; i < outputRules.length; i++) {
976
+ const candidateOutput = outputRules[i]!;
977
+ candidateOutput.frozen = true;
978
+ candidateOutput.index = i;
979
+ }
980
+
981
+ const firstOutput = outputRules[0]!;
982
+ const nextRenderKey = context.renderKey ?? EVAL;
983
+ const output = new Rules(
984
+ [],
985
+ {
986
+ rulesVisibility: {
987
+ Ruleset: 'public',
988
+ Declaration: 'public',
989
+ VarDeclaration: 'public',
990
+ Mixin: 'public'
991
+ },
992
+ isMixinOutput: restrictMixinOutputLookup
993
+ },
994
+ firstOutput.location,
995
+ firstOutput.treeContext
996
+ );
997
+ output.renderKey = nextRenderKey;
998
+ output.sourceParent = firstOutput.sourceParent;
999
+ output._setValueArray([...outputRules]);
1000
+ const outputContext = {
1001
+ ...context,
1002
+ renderKey: output.renderKey,
1003
+ rulesContext: output
1004
+ } as Context;
1005
+ for (const child of outputRules) {
1006
+ if (isNode(child, N.Rules)) {
1007
+ const childContext = {
1008
+ ...outputContext,
1009
+ renderKey: (child as Rules).renderKey,
1010
+ rulesContext: child as Rules
1011
+ } as Context;
1012
+ setParent(child, output, childContext);
1013
+ output.registerNode(child, undefined, outputContext);
1014
+ projectCurrentRenderParents(
1015
+ child as Rules,
1016
+ childContext
1017
+ );
1018
+ continue;
1019
+ }
1020
+ setParent(child, output, outputContext);
1021
+ output.registerNode(child, undefined, outputContext);
1022
+ }
1023
+
1024
+ return output;
1025
+ }
1026
+
1027
+ /**
1028
+ * Evaluate a ruleset candidate whose guard already passed during Ruleset
1029
+ * evaluation, preserving mixin-output semantics and eval-state-subtree association.
1030
+ */
1031
+ export async function evaluateRulesetMixinCandidateOutput(
1032
+ sourceRules: Rules,
1033
+ definitionParent: Node | undefined,
1034
+ placementParent: Node | undefined,
1035
+ candidateIndex: number,
1036
+ restrictMixinOutputLookup: boolean,
1037
+ context: Context
1038
+ ): Promise<Rules> {
1039
+ const renderKey = context.nextRenderKey();
1040
+ let rules = sourceRules.createShallowBodyWrapper(undefined, renderKey);
1041
+ const placementContext = {
1042
+ ...context,
1043
+ renderKey,
1044
+ rulesContext: rules
1045
+ } as Context;
1046
+ setParent(rules, placementParent, placementContext);
1047
+ setSourceParent(rules, definitionParent, placementContext);
1048
+ const previousRulesContext = context.rulesContext;
1049
+ const previousRenderKey = context.renderKey;
1050
+ const previousFrames = context.frames;
1051
+ const previousRulesetFrames = context.rulesetFrames;
1052
+ const seededFrames: Array<Ruleset | AtRule> = [];
1053
+ let frameCursor = placementParent;
1054
+ while (frameCursor) {
1055
+ if (isNode(frameCursor, N.Ruleset | N.AtRule)) {
1056
+ seededFrames.push(frameCursor as Ruleset | AtRule);
1057
+ }
1058
+ frameCursor = getParent(frameCursor, context);
1059
+ }
1060
+ seededFrames.reverse();
1061
+ context.rulesContext = rules;
1062
+ context.renderKey = rules.renderKey;
1063
+ context.frames = seededFrames;
1064
+ context.rulesetFrames = seededFrames.filter((frame): frame is Ruleset => isNode(frame, N.Ruleset));
1065
+ try {
1066
+ rules = await rules.preEval(context) as Rules;
1067
+ rules = await rules.eval(context);
1068
+ } finally {
1069
+ context.rulesContext = previousRulesContext;
1070
+ context.renderKey = previousRenderKey;
1071
+ context.frames = previousFrames;
1072
+ context.rulesetFrames = previousRulesetFrames;
1073
+ }
1074
+ setSourceParent(rules, definitionParent, placementContext);
1075
+ setParent(rules, placementParent, placementContext);
1076
+ finalizeInvocationOutputRules(rules, context);
1077
+ rules.index = candidateIndex;
1078
+ rules.options.isMixinOutput = restrictMixinOutputLookup;
1079
+ return rules;
1080
+ }
1081
+
1082
+ export function finalizeInvocationOutputRules(
1083
+ rules: Rules,
1084
+ context: Context
1085
+ ): void {
1086
+ const scopedContext = {
1087
+ ...context,
1088
+ renderKey: rules.renderKey,
1089
+ rulesContext: rules
1090
+ } as Context;
1091
+
1092
+ const children = rules.get('value', scopedContext);
1093
+ for (let index = 0; index < children.length; index++) {
1094
+ let child = children[index]!;
1095
+ if (isNode(child, N.Rules)) {
1096
+ continue;
1097
+ }
1098
+ if (isNode(child, N.Ruleset | N.AtRule) && getCurrentParentNode(child, scopedContext) !== rules) {
1099
+ child = child.clone(false, undefined, scopedContext);
1100
+ rules._setChildAt(index, child, scopedContext, false);
1101
+ rules.adopt(child, scopedContext);
1102
+ }
1103
+
1104
+ if (isNode(child, N.Ruleset)) {
1105
+ const childRuleset = child as Ruleset;
1106
+ if (
1107
+ childRuleset.getOwnSelector()
1108
+ && childRuleset.getExtendedSelector(rules.renderKey)
1109
+ && !childRuleset.getSelectorBeforeExtend(rules.renderKey)
1110
+ ) {
1111
+ childRuleset.setExtendedSelector(
1112
+ childRuleset.getSelector(rules.renderKey),
1113
+ scopedContext
1114
+ );
1115
+ }
1116
+ finalizeInvocationOutputRules(
1117
+ childRuleset.enterRules(scopedContext),
1118
+ scopedContext
1119
+ );
1120
+ continue;
1121
+ }
1122
+
1123
+ if (isNode(child, N.AtRule)) {
1124
+ const childRules = (child as AtRule).enterRules(scopedContext);
1125
+ if (childRules) {
1126
+ finalizeInvocationOutputRules(childRules, scopedContext);
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+
1132
+ function projectCurrentRenderParents(
1133
+ rules: Rules,
1134
+ context: Context
1135
+ ): void {
1136
+ const scopedContext = {
1137
+ ...context,
1138
+ renderKey: rules.renderKey,
1139
+ rulesContext: rules
1140
+ } as Context;
1141
+ const children = rules.get('value', scopedContext);
1142
+ for (const child of children) {
1143
+ setParent(child, rules, scopedContext);
1144
+ if (isNode(child, N.Rules)) {
1145
+ if ((child as Rules).renderKey !== rules.renderKey) {
1146
+ (child as Rules).renderKey = rules.renderKey;
1147
+ }
1148
+ projectCurrentRenderParents(child as Rules, {
1149
+ ...scopedContext,
1150
+ renderKey: (child as Rules).renderKey,
1151
+ rulesContext: child as Rules
1152
+ } as Context);
1153
+ continue;
1154
+ }
1155
+ if (isNode(child, N.Ruleset | N.AtRule)) {
1156
+ const nestedRules = child.enterRules(scopedContext);
1157
+ if (nestedRules) {
1158
+ projectCurrentRenderParents(nestedRules, {
1159
+ ...scopedContext,
1160
+ renderKey: nestedRules.renderKey,
1161
+ rulesContext: nestedRules
1162
+ } as Context);
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ /**
1169
+ * Create the unlocked output for a detached ruleset call without flattening
1170
+ * the source body eagerly.
1171
+ */
1172
+ export function unlockDetachedRulesetMixinCandidateOutput(
1173
+ sourceRules: Rules,
1174
+ placementParent: Node | undefined,
1175
+ definitionParent: Node | undefined,
1176
+ candidateIndex: number,
1177
+ context: Context
1178
+ ): Rules {
1179
+ const unlocked = sourceRules.cloneDetachedUnlockWrapper(context);
1180
+ const placementContext = {
1181
+ ...context,
1182
+ renderKey: unlocked.renderKey,
1183
+ rulesContext: unlocked
1184
+ } as Context;
1185
+ setParent(unlocked, placementParent, placementContext);
1186
+ setSourceParent(unlocked, definitionParent, placementContext);
1187
+ finalizeInvocationOutputRules(unlocked, placementContext);
1188
+ unlocked.options.isMixinOutput = false;
1189
+ unlocked.index = candidateIndex;
1190
+ return unlocked;
1191
+ }
1192
+
1193
+ /**
1194
+ * Process one prepared normal mixin candidate. This owns the remaining guard /
1195
+ * default orchestration for the standard candidate path: either dispatch the
1196
+ * candidate output immediately or return a pending default() replay record.
1197
+ */
1198
+ export async function processPreparedMixinCandidate<TCandidate>(
1199
+ options: ProcessPreparedMixinCandidateOptions<TCandidate>
1200
+ ): Promise<PendingMixinDefaultCandidate<TCandidate> | undefined> {
1201
+ const {
1202
+ candidate,
1203
+ rules,
1204
+ params,
1205
+ outerRules,
1206
+ guard,
1207
+ parent,
1208
+ guardScopeChildren,
1209
+ hasAnyDefault,
1210
+ candidateHasDefault,
1211
+ context,
1212
+ evaluateCandidateOutput
1213
+ } = options;
1214
+
1215
+ let nextOuterRules = outerRules;
1216
+ const stableGuardScopeChildren = guard
1217
+ ? (guardScopeChildren ?? (nextOuterRules ? [...nextOuterRules.value] : undefined))
1218
+ : guardScopeChildren;
1219
+ const getCapturedOuterRules = () => captureMixinScopeSnapshot(
1220
+ nextOuterRules,
1221
+ stableGuardScopeChildren,
1222
+ context
1223
+ );
1224
+ const pendingLookupScope = () => getCapturedOuterRules() ?? getMixinCandidateLookupScope(parent, rules, context);
1225
+ if (guard) {
1226
+ const evaluatedGuard = await evaluateMixinGuardCandidate(
1227
+ guard,
1228
+ nextOuterRules,
1229
+ parent,
1230
+ context,
1231
+ stableGuardScopeChildren,
1232
+ candidateHasDefault
1233
+ );
1234
+ nextOuterRules = evaluatedGuard.outerRules;
1235
+ if (!evaluatedGuard.passes) {
1236
+ return undefined;
1237
+ }
1238
+ if (hasAnyDefault) {
1239
+ const capturedOuterRules = getCapturedOuterRules();
1240
+ const lookupScope = capturedOuterRules ?? getMixinCandidateLookupScope(parent, rules, context);
1241
+ return {
1242
+ candidate,
1243
+ rules,
1244
+ outerRules: capturedOuterRules,
1245
+ params,
1246
+ group: candidateHasDefault ? evaluatedGuard.defaultGroup! : MixinDefaultGroup.None,
1247
+ lookupScope
1248
+ };
1249
+ }
1250
+ }
1251
+
1252
+ if (hasAnyDefault) {
1253
+ const capturedOuterRules = getCapturedOuterRules();
1254
+ const lookupScope = capturedOuterRules ?? getMixinCandidateLookupScope(parent, rules, context);
1255
+ return {
1256
+ candidate,
1257
+ rules,
1258
+ outerRules: capturedOuterRules,
1259
+ params,
1260
+ group: MixinDefaultGroup.None,
1261
+ lookupScope
1262
+ };
1263
+ }
1264
+
1265
+ await withMixinLookupScope(
1266
+ nextOuterRules ?? rules,
1267
+ pendingLookupScope(),
1268
+ context,
1269
+ () => evaluateCandidateOutput(candidate, rules, nextOuterRules, params)
1270
+ );
1271
+ return undefined;
1272
+ }
1273
+
1274
+ // -- Scope ancestry helpers --
1275
+
1276
+ /**
1277
+ * Walk the parent chain (via eval state helpers) to find the nearest Rules ancestor.
1278
+ */
1279
+ export function findRulesAncestor(node: Node | undefined, context: Context): Rules | undefined {
1280
+ let current = node ? getParent(node, context) : undefined;
1281
+ while (current && current.type !== 'Rules') {
1282
+ current = getParent(current, context);
1283
+ }
1284
+ return current as Rules | undefined;
1285
+ }
1286
+
1287
+ /**
1288
+ * Walk sourceParent chain then parent chain to find the nearest source Rules ancestor.
1289
+ */
1290
+ export function findSourceRulesAncestor(node: Node | undefined, context: Context): Rules | undefined {
1291
+ let current = node;
1292
+ let sp = current ? getSourceParent(current, context) : undefined;
1293
+ while (current && !sp) {
1294
+ current = getParent(current, context);
1295
+ sp = current ? getSourceParent(current, context) : undefined;
1296
+ }
1297
+ return sp ? findRulesAncestor(sp, context) : undefined;
1298
+ }
1299
+
1300
+ export function getMixinCandidateLookupScope(
1301
+ parent: Node | undefined,
1302
+ rules: Rules,
1303
+ context: Context
1304
+ ): Rules | undefined {
1305
+ if (isNode(parent, N.Rules)) {
1306
+ return parent as Rules;
1307
+ }
1308
+ return findRulesAncestor(parent, context) ?? findRulesAncestor(rules, context);
1309
+ }
1310
+
1311
+ /**
1312
+ * Resolve the candidate's parent, throwing if absent.
1313
+ *
1314
+ * Accepts `Node<any, any>` so callers don't need `as unknown as Node` casts
1315
+ * for typed subclasses like Mixin or Ruleset.
1316
+ */
1317
+ export function getCandidateParent(node: Node<any, any>, context: Context): Node {
1318
+ const parent = getParent(node as Node, context);
1319
+ if (!parent) {
1320
+ throw new ReferenceError(`${node.type} candidate must have a parent during mixin evaluation`);
1321
+ }
1322
+ return parent;
1323
+ }
1324
+
1325
+ // -- Arg evaluation --
1326
+
1327
+ /**
1328
+ * Evaluate raw call args into a flat array of current Node values.
1329
+ */
1330
+ export async function evaluateMixinArgs(
1331
+ args: any[],
1332
+ caller: Node | undefined,
1333
+ context: Context
1334
+ ): Promise<Node[]> {
1335
+ const nodeArgs: Node[] = [];
1336
+ const callerSourceNode = (caller as any)?.name instanceof Node
1337
+ ? (caller as any).name
1338
+ : caller;
1339
+ const callerSourceParent = callerSourceNode
1340
+ ? getSourceParent(callerSourceNode, context)
1341
+ : undefined;
1342
+ const savedRulesContext = context.rulesContext;
1343
+ const savedLookupScope = context.lookupScope;
1344
+ const savedRenderKey = context.renderKey;
1345
+ const argEvalRulesContext = context.lookupScope
1346
+ ?? context.rulesContext
1347
+ ?? findRulesAncestor(caller, context)
1348
+ ?? findSourceRulesAncestor(callerSourceNode, context)
1349
+ ?? findRulesAncestor(callerSourceParent, context);
1350
+ context.rulesContext = argEvalRulesContext;
1351
+ context.lookupScope = argEvalRulesContext;
1352
+ context.renderKey = argEvalRulesContext?.renderKey ?? savedRenderKey;
1353
+ try {
1354
+ for (const arg of args) {
1355
+ if (isNode(arg)) {
1356
+ if (isNode(arg, N.VarDeclaration)) {
1357
+ // Evaluate the value, keep the VarDeclaration structure
1358
+ const value = (arg as VarDeclaration).get('value');
1359
+ if (value instanceof Node) {
1360
+ if (callerSourceParent && (isNode(value, N.Reference) || isNode(value, N.Mixin))) {
1361
+ setSourceParent(value, callerSourceParent, context);
1362
+ }
1363
+ const evaldValue = await value.eval(context);
1364
+ const argName = String((arg as VarDeclaration).get('name').valueOf());
1365
+ const bound = new VarDeclarationCtor({
1366
+ name: new Any(argName, { role: 'property' }),
1367
+ value: new Nil()
1368
+ }, { ...((arg as VarDeclaration).options ?? {}) }, arg.location, context.treeContext);
1369
+ bound.renderKey = context.renderKey ?? bound.renderKey;
1370
+ (bound as VarDeclaration).value = evaldValue;
1371
+ bound.adopt(evaldValue, context);
1372
+ nodeArgs.push(bound);
1373
+ } else {
1374
+ nodeArgs.push(arg);
1375
+ }
1376
+ continue;
1377
+ }
1378
+ if (callerSourceParent && (isNode(arg, N.Reference) || isNode(arg, N.Mixin))) {
1379
+ setSourceParent(arg, callerSourceParent, context);
1380
+ }
1381
+ const evald = await arg.eval(context);
1382
+ if (evald.type === 'Rest') {
1383
+ let restValue = (evald as unknown as { value: unknown }).value;
1384
+ if (isNode(restValue as Node) && !isNode(restValue as Node, N.Sequence | N.List)) {
1385
+ restValue = await (restValue as Node).eval(context);
1386
+ }
1387
+ if (isNode(restValue, N.Sequence) || isNode(restValue, N.List)) {
1388
+ for (const restArg of (restValue as unknown as { value: Node[] }).value) {
1389
+ nodeArgs.push(restArg);
1390
+ }
1391
+ continue;
1392
+ }
1393
+ }
1394
+ nodeArgs.push(evald);
1395
+ } else {
1396
+ nodeArgs.push(cast(arg));
1397
+ }
1398
+ }
1399
+ } finally {
1400
+ context.rulesContext = savedRulesContext;
1401
+ context.lookupScope = savedLookupScope;
1402
+ context.renderKey = savedRenderKey;
1403
+ }
1404
+ return nodeArgs;
1405
+ }
1406
+
1407
+ // -- Candidate matching --
1408
+
1409
+ /**
1410
+ * Evaluate a pattern-match operand in a fresh session scope.
1411
+ */
1412
+ async function preparePatternOperand(node: Node, context: Context): Promise<Node> {
1413
+ try {
1414
+ return await node.eval(context);
1415
+ } finally {
1416
+ }
1417
+ }
1418
+
1419
+ /**
1420
+ * Match the mixin array against evaluated args. Returns the candidates whose
1421
+ * param signatures match (with params bound).
1422
+ */
1423
+ export async function matchMixinCandidates(
1424
+ mixinArr: MixinEntry[],
1425
+ nodeArgs: Node[],
1426
+ caller: Node | undefined,
1427
+ sourceParent: Node | undefined,
1428
+ context: Context
1429
+ ): Promise<MixinEntry[]> {
1430
+ if (process.env.JESS_DEBUG_LOCK === 'throw-match') {
1431
+ const callerName = caller && isNode(caller, N.Call)
1432
+ ? (caller as unknown as { name?: Node }).name
1433
+ : undefined;
1434
+ const callerKey = isNode(callerName, N.Reference)
1435
+ ? String(callerName.key?.valueOf?.() ?? '')
1436
+ : '';
1437
+ const candidateNames = mixinArr.map(mixin => {
1438
+ if (isNode(mixin, N.Mixin)) {
1439
+ return String(mixin.get('name')?.valueOf?.() ?? '');
1440
+ }
1441
+ if (isNode(mixin, N.Ruleset)) {
1442
+ return String(mixin.get('selector')?.valueOf?.() ?? '');
1443
+ }
1444
+ return mixin.type;
1445
+ });
1446
+ if (
1447
+ callerKey.includes('inner-locked-mixin')
1448
+ || candidateNames.some(name => name.includes('inner-locked-mixin'))
1449
+ ) {
1450
+ throw new Error(`[lock-match] ${JSON.stringify({
1451
+ callerKey,
1452
+ candidateNames,
1453
+ sourceParent: sourceParent?.type,
1454
+ rulesContext: context.rulesContext?.type,
1455
+ lookupScope: context.lookupScope?.type,
1456
+ argTypes: nodeArgs.map(arg => arg.type)
1457
+ })}`);
1458
+ }
1459
+ }
1460
+ const mixinCandidates: MixinEntry[] = [];
1461
+ const bindingSourceParent = sourceParent ?? caller;
1462
+
1463
+ for (let i = 0; i < mixinArr.length; i++) {
1464
+ let mixin = mixinArr[i]!;
1465
+ const isPlainRule = isNode(mixin, N.Rules);
1466
+ const currentParams = !isPlainRule && isNode(mixin, N.Mixin)
1467
+ ? getCurrentMixinParams(mixin as Mixin, mixin.renderKey ?? context)
1468
+ : undefined;
1469
+ const paramLength = isPlainRule ? 0 : (currentParams?.length ?? 0);
1470
+
1471
+ if (!paramLength) {
1472
+ if (nodeArgs.length) {
1473
+ continue;
1474
+ }
1475
+ mixinCandidates.push(mixin);
1476
+ } else {
1477
+ const params = currentParams!;
1478
+ const hasRestParamOriginal = params.get('value').some(
1479
+ (p: Node) => p.type === 'Rest'
1480
+ );
1481
+ const maxPositionalArgs = hasRestParamOriginal ? Number.POSITIVE_INFINITY : params.length;
1482
+ const positions = params.length;
1483
+ let requiredPositions = 0;
1484
+ for (const param of params.get('value')) {
1485
+ if (isNode(param, N.VarDeclaration)) {
1486
+ if ((param as VarDeclaration).get('value') instanceof Nil) {
1487
+ requiredPositions++;
1488
+ }
1489
+ } else if (isNode(param, N.Any) && param.role === 'property') {
1490
+ requiredPositions++;
1491
+ } else if (param.type !== 'Rest') {
1492
+ requiredPositions++;
1493
+ }
1494
+ }
1495
+
1496
+ let argPos = 0;
1497
+ let match = true;
1498
+ for (let pi = 0; pi < positions; pi++) {
1499
+ const arg = nodeArgs[argPos];
1500
+ if (!arg) {
1501
+ continue;
1502
+ }
1503
+ let param: Node | undefined;
1504
+ let argValue: Node;
1505
+
1506
+ if (isNode(arg, N.VarDeclaration)) {
1507
+ param = params.get('value').find((p: Node) => {
1508
+ if (isNode(p, N.VarDeclaration)) {
1509
+ return (p as VarDeclaration).get('name').valueOf() === (arg as VarDeclaration).get('name').valueOf();
1510
+ }
1511
+ if (isNode(p, N.Any) && p.role === 'property') {
1512
+ return p.valueOf() === (arg as VarDeclaration).get('name').valueOf();
1513
+ }
1514
+ return false;
1515
+ });
1516
+ if (param) {
1517
+ argValue = (arg as VarDeclaration).get('value') as Node;
1518
+ } else {
1519
+ match = false;
1520
+ break;
1521
+ }
1522
+ } else {
1523
+ param = params.get('value')[pi];
1524
+ if (!param) {
1525
+ match = false;
1526
+ break;
1527
+ }
1528
+ argValue = arg;
1529
+ }
1530
+
1531
+ if (!param) {
1532
+ match = false;
1533
+ break;
1534
+ }
1535
+
1536
+ if (
1537
+ isNode(param, N.VarDeclaration)
1538
+ || (isNode(param, N.Any) && param.role === 'property')
1539
+ || param.type === 'Rest'
1540
+ ) {
1541
+ if (bindingSourceParent && (isNode(argValue, N.Reference) || isNode(argValue, N.Mixin))) {
1542
+ setSourceParent(argValue, bindingSourceParent, context);
1543
+ }
1544
+ } else {
1545
+ const originalPatternParam = !isNode(arg, N.VarDeclaration)
1546
+ ? currentParams?.get('value')[pi]
1547
+ : undefined;
1548
+ const preparedParam = isNode(originalPatternParam as Node | undefined, N.Selector)
1549
+ ? await preparePatternOperand(originalPatternParam as Node, context)
1550
+ : isNode(param, N.Selector)
1551
+ ? await preparePatternOperand(param, context)
1552
+ : param;
1553
+ if (preparedParam.compare(argValue, context) !== 0) {
1554
+ match = false;
1555
+ break;
1556
+ }
1557
+ }
1558
+ argPos++;
1559
+ }
1560
+
1561
+ const positionalArgCount = nodeArgs.filter(argNode => !isNode(argNode, N.VarDeclaration)).length;
1562
+ if (positionalArgCount > maxPositionalArgs) {
1563
+ continue;
1564
+ }
1565
+ if (argPos < requiredPositions) {
1566
+ continue;
1567
+ }
1568
+ if (nodeArgs.length > 1 && params.get('value').length === 1 && requiredPositions === 1) {
1569
+ continue;
1570
+ }
1571
+ if (match) {
1572
+ mixinCandidates.push(mixin);
1573
+ }
1574
+ }
1575
+ }
1576
+
1577
+ return mixinCandidates;
1578
+ }
1579
+
1580
+ // -- Candidate filtering and sorting --
1581
+
1582
+ /**
1583
+ * Walk a node tree looking for `default()` / `DefaultGuard` / `??` calls.
1584
+ */
1585
+ function guardContainsDefault(node: Node | undefined): boolean {
1586
+ if (!node) {
1587
+ return false;
1588
+ }
1589
+ if (node.type === 'DefaultGuard') {
1590
+ return true;
1591
+ }
1592
+ if (node.type === 'Call') {
1593
+ const callName = String((node as unknown as { name?: { valueOf?: () => string } }).name?.valueOf?.()
1594
+ ?? (node as unknown as { name?: string }).name ?? '');
1595
+ if (callName === 'default' || callName === '??') {
1596
+ return true;
1597
+ }
1598
+ }
1599
+ const value = (node as unknown as { value: unknown }).value;
1600
+ if (Array.isArray(value)) {
1601
+ for (const item of value) {
1602
+ if (isNode(item) && guardContainsDefault(item)) {
1603
+ return true;
1604
+ }
1605
+ }
1606
+ return false;
1607
+ }
1608
+ if (isPlainObject(value)) {
1609
+ for (const item of Object.values(value as Record<string, unknown>)) {
1610
+ if (isNode(item as Node) && guardContainsDefault(item as Node)) {
1611
+ return true;
1612
+ }
1613
+ if (Array.isArray(item)) {
1614
+ for (const child of item) {
1615
+ if (isNode(child) && guardContainsDefault(child)) {
1616
+ return true;
1617
+ }
1618
+ }
1619
+ }
1620
+ }
1621
+ }
1622
+ return false;
1623
+ }
1624
+
1625
+ /**
1626
+ * Check whether a node has a failed-guard ancestor (Ruleset with Nil guard).
1627
+ */
1628
+ function hasFailedGuardAncestor(node: Node<any, any>, context: Context): boolean {
1629
+ let current: Node | undefined = getParent(node as Node, context);
1630
+ while (current) {
1631
+ if (isNode(current, N.Ruleset)) {
1632
+ const guardNode = getCurrentRulesetGuard(current as Ruleset, context);
1633
+ if (guardNode instanceof Nil) {
1634
+ return true;
1635
+ }
1636
+ }
1637
+ current = getParent(current, context);
1638
+ }
1639
+ return false;
1640
+ }
1641
+
1642
+ /**
1643
+ * Filter matched candidates by eval-stack guard and sort default-guard
1644
+ * candidates to the end.
1645
+ *
1646
+ * Returns `{ evalCandidates, hasDefault }`.
1647
+ */
1648
+ export function filterAndSortMixinEvalCandidates(
1649
+ mixinCandidates: MixinEntry[],
1650
+ context: Context
1651
+ ): { evalCandidates: MixinEntry[]; hasDefault: boolean } {
1652
+ let hasDefault = false;
1653
+ let evalCandidates = mixinCandidates
1654
+ .filter((candidate) => {
1655
+ const blockedByFailedGuard = hasFailedGuardAncestor(candidate, context);
1656
+ return !blockedByFailedGuard;
1657
+ })
1658
+ .map<MixinEntry>((candidate) => {
1659
+ const hasDefaultGuard = Boolean(candidate.options?.hasDefault)
1660
+ || guardContainsDefault((candidate as Mixin).get('guard') as Node | undefined);
1661
+ if (hasDefaultGuard) {
1662
+ candidate.options ??= {};
1663
+ candidate.options.hasDefault = true;
1664
+ hasDefault = true;
1665
+ }
1666
+ return candidate;
1667
+ });
1668
+
1669
+ if (hasDefault) {
1670
+ evalCandidates = evalCandidates.slice(0).sort((a, b) => {
1671
+ const aDefault = a.options?.hasDefault;
1672
+ const bDefault = b.options?.hasDefault;
1673
+ if (!aDefault && !bDefault) {
1674
+ return 0;
1675
+ }
1676
+ if (!aDefault) {
1677
+ return -1;
1678
+ }
1679
+ if (!bDefault) {
1680
+ return 1;
1681
+ }
1682
+ return 0;
1683
+ });
1684
+ }
1685
+
1686
+ if (evalCandidates.length === 0) {
1687
+ throw new ReferenceError('No matching mixins found.');
1688
+ }
1689
+
1690
+ return { evalCandidates, hasDefault };
1691
+ }
1692
+
1693
+ // -- Candidate output evaluation --
1694
+
1695
+ export type EvaluateCandidateOutputOptions = {
1696
+ sourceParent: Node | undefined;
1697
+ invocationParent: Node | undefined;
1698
+ restrictMixinOutputLookup: boolean;
1699
+ outputRules: Rules[];
1700
+ getCandidateParent: (node: Node<any, any>) => Node;
1701
+ };
1702
+
1703
+ /**
1704
+ * Evaluate a single mixin candidate's body and push the result to outputRules.
1705
+ *
1706
+ * Handles explicit recursion guarding, position creation, body eval, output
1707
+ * finalization, param scope projection, and eval state subtree association.
1708
+ */
1709
+ export async function evaluateCandidateOutput(
1710
+ candidate: MixinEntry,
1711
+ rules: Rules,
1712
+ outerRules: Rules | undefined,
1713
+ params: List<Node> | undefined,
1714
+ context: Context,
1715
+ opts: EvaluateCandidateOutputOptions
1716
+ ): Promise<void> {
1717
+ const {
1718
+ sourceParent,
1719
+ invocationParent,
1720
+ restrictMixinOutputLookup,
1721
+ outputRules,
1722
+ getCandidateParent: getParentFn
1723
+ } = opts;
1724
+ const currentCall = context.callStack.at(-1);
1725
+ if (currentCall) {
1726
+ const isRecursive = context.callMap.add(currentCall, params, context);
1727
+ if (isRecursive) {
1728
+ return;
1729
+ }
1730
+ }
1731
+ try {
1732
+ const callerParent = getParentFn(candidate);
1733
+ const candidateSourceParent = getUsableInvocationSourceParent(
1734
+ getCanonicalSourceParent(candidate as Node),
1735
+ getUsableInvocationSourceParent(
1736
+ getCanonicalSourceParent(callerParent),
1737
+ sourceParent
1738
+ )
1739
+ );
1740
+ const lexicalSourceParent = candidateSourceParent ?? callerParent;
1741
+ const rulesContext = {
1742
+ ...context,
1743
+ renderKey: rules.renderKey,
1744
+ rulesContext: rules
1745
+ } as Context;
1746
+ setParent(rules, outerRules ?? callerParent, rulesContext);
1747
+ rules.sourceParent = candidateSourceParent ?? lexicalSourceParent;
1748
+ const previousRenderKey = context.renderKey;
1749
+ context.renderKey = rules.renderKey;
1750
+ let newRules: Rules;
1751
+ try {
1752
+ newRules = await rules.eval(context);
1753
+ } finally {
1754
+ context.renderKey = previousRenderKey;
1755
+ }
1756
+ void outerRules;
1757
+ if (newRules.renderKey === CANONICAL) {
1758
+ newRules.renderKey = rules.renderKey;
1759
+ }
1760
+ const newRulesContext = {
1761
+ ...context,
1762
+ renderKey: newRules.renderKey,
1763
+ rulesContext: newRules
1764
+ } as Context;
1765
+ setParent(newRules, invocationParent ?? callerParent, newRulesContext);
1766
+ projectParentChainForRenderKey(invocationParent ?? callerParent, context, newRulesContext);
1767
+ newRules.sourceParent = candidateSourceParent ?? lexicalSourceParent;
1768
+ newRules.index = candidate.index;
1769
+ finalizeInvocationOutputRules(newRules, newRulesContext);
1770
+
1771
+ if (outerRules) {
1772
+ const previousRenderKey = context.renderKey;
1773
+ const previousRulesContext = context.rulesContext;
1774
+ context.renderKey = newRules.renderKey;
1775
+ context.rulesContext = newRules;
1776
+ let outputChildren: readonly Node[];
1777
+ try {
1778
+ outputChildren = newRules.getRegistryChildren(context);
1779
+ } finally {
1780
+ context.renderKey = previousRenderKey;
1781
+ context.rulesContext = previousRulesContext;
1782
+ }
1783
+ const outputContext = {
1784
+ ...context,
1785
+ renderKey: newRules.renderKey,
1786
+ rulesContext: newRules
1787
+ } as Context;
1788
+ for (const child of outputChildren) {
1789
+ if (isNode(child, N.Rules)) {
1790
+ projectCurrentRenderParents(
1791
+ child as Rules,
1792
+ {
1793
+ ...outputContext,
1794
+ renderKey: (child as Rules).renderKey,
1795
+ rulesContext: child as Rules
1796
+ } as Context
1797
+ );
1798
+ }
1799
+ }
1800
+ bindStructuralSourceParent(newRules, outerRules, outputContext);
1801
+ setParent(newRules, outerRules, outputContext);
1802
+ if (invocationParent ?? callerParent) {
1803
+ addParentEdge(newRules, CALLER, (invocationParent ?? callerParent)!);
1804
+ }
1805
+ newRules.sourceParent = outerRules;
1806
+ newRules.index = candidate.index;
1807
+ newRules.options.isMixinOutput = false;
1808
+ if (process.env.JESS_DEBUG_LOCK === 'throw-output') {
1809
+ const candidateName = String((candidate as Mixin).get('name')?.valueOf?.() ?? '');
1810
+ if (candidateName.includes('lock-mixin')) {
1811
+ throw new Error(`[lock-output] ${JSON.stringify({
1812
+ candidateName,
1813
+ hasOuterRules: Boolean(outerRules),
1814
+ outerRulesChildren: outerRules?.value?.map((child: Node) => child.type) ?? [],
1815
+ candidateSourceParent: candidateSourceParent?.type,
1816
+ lexicalSourceParent: lexicalSourceParent?.type,
1817
+ outputContainerSourceParent: newRules.sourceParent?.type,
1818
+ outputContainerSourceParentChildren: newRules.sourceParent?.value?.map((child: Node) => child.type) ?? []
1819
+ })}`);
1820
+ }
1821
+ }
1822
+ outputRules.push(newRules);
1823
+ return;
1824
+ }
1825
+
1826
+ newRules.options.isMixinOutput = restrictMixinOutputLookup;
1827
+ if (context.treeContext?.file) {
1828
+ newRules.options.rulesVisibility ??= {};
1829
+ newRules.options.rulesVisibility.VarDeclaration = 'private';
1830
+ }
1831
+ outputRules.push(newRules);
1832
+ } finally {
1833
+ if (currentCall) {
1834
+ context.callMap.delete(currentCall);
1835
+ }
1836
+ }
1837
+ }
1838
+
1839
+ // -- Dispatch orchestration --
1840
+
1841
+ export type MixinDispatchContext = {
1842
+ evalCandidates: MixinEntry[];
1843
+ hasDefault: boolean;
1844
+ nodeArgs: Node[];
1845
+ sourceParent: Node | undefined;
1846
+ invocationParent: Node | undefined;
1847
+ caller: Node | undefined;
1848
+ restrictMixinOutputLookup: boolean;
1849
+ outputRules: Rules[];
1850
+ getCandidateParent: (node: Node<any, any>) => Node;
1851
+ evaluateCandidateOutput: (
1852
+ candidate: MixinEntry,
1853
+ rules: Rules,
1854
+ outerRules: Rules | undefined,
1855
+ params: List<Node> | undefined,
1856
+ ) => Promise<void>;
1857
+ };
1858
+
1859
+ /**
1860
+ * Dispatch all mixin eval candidates — the main candidate loop.
1861
+ *
1862
+ * Handles Ruleset candidates, detached rulesets, and normal parameterized
1863
+ * mixins. Includes default guard replay and output assembly.
1864
+ */
1865
+ export async function dispatchMixinEvalCandidates(
1866
+ dispatch: MixinDispatchContext,
1867
+ context: Context
1868
+ ): Promise<Rules> {
1869
+ const {
1870
+ evalCandidates,
1871
+ hasDefault,
1872
+ nodeArgs,
1873
+ sourceParent,
1874
+ invocationParent,
1875
+ restrictMixinOutputLookup,
1876
+ outputRules,
1877
+ getCandidateParent,
1878
+ evaluateCandidateOutput
1879
+ } = dispatch;
1880
+ const pendingDefaultCandidates: PendingMixinDefaultCandidate<any>[] = [];
1881
+ let skippedByRecursion = false;
1882
+ for (const candidate of evalCandidates) {
1883
+ if (isNode(candidate, N.Ruleset)) {
1884
+ if ((candidate as Ruleset).get('guard') instanceof Nil) {
1885
+ continue;
1886
+ }
1887
+ const currentRules = (candidate as Ruleset).enterRules(context);
1888
+ const sourceRules = getRootSourceRules(currentRules);
1889
+ if (context.rulesEvalStack.includes(sourceRules)) {
1890
+ skippedByRecursion = true;
1891
+ continue;
1892
+ }
1893
+ const definitionParent = getCandidateParent(candidate);
1894
+ const placementParent = invocationParent ?? definitionParent;
1895
+ const rules = await evaluateRulesetMixinCandidateOutput(
1896
+ sourceRules,
1897
+ definitionParent,
1898
+ placementParent,
1899
+ candidate.index,
1900
+ restrictMixinOutputLookup,
1901
+ context
1902
+ );
1903
+ outputRules.push(rules);
1904
+ continue;
1905
+ }
1906
+
1907
+ // After the Ruleset branch above, candidate must be a Mixin
1908
+ if (!isNode(candidate, N.Mixin)) {
1909
+ continue;
1910
+ }
1911
+ if (process.env.JESS_DEBUG_LOCK) {
1912
+ const candidateName = String(candidate.get('name')?.valueOf?.() ?? '');
1913
+ if (candidateName.includes('inner-locked-mixin')) {
1914
+ const candidateParent = getParent(candidate, context);
1915
+ const candidateSourceParent = getSourceParent(candidate, context);
1916
+ const lockInfo = {
1917
+ candidateName,
1918
+ candidateParent: candidateParent?.type,
1919
+ candidateParentIndex: candidateParent?.index,
1920
+ candidateSourceParent: candidateSourceParent?.type,
1921
+ candidateSourceParentIndex: candidateSourceParent?.index,
1922
+ candidateSourceParentName: isNode(candidateSourceParent, N.Mixin)
1923
+ ? String(candidateSourceParent.get('name')?.valueOf?.() ?? '')
1924
+ : undefined,
1925
+ sourceParent: sourceParent?.type,
1926
+ invocationParent: invocationParent?.type,
1927
+ lookupScope: context.lookupScope?.type,
1928
+ rulesContext: context.rulesContext?.type
1929
+ };
1930
+ if (process.env.JESS_DEBUG_LOCK === 'throw') {
1931
+ throw new Error(`[lock-dispatch] ${JSON.stringify(lockInfo)}`);
1932
+ }
1933
+ console.log('[lock-dispatch]', lockInfo);
1934
+ }
1935
+ }
1936
+ if (!candidate.get('name') && !candidate.get('params') && !candidate.get('guard')) {
1937
+ const sourceRules = getRootSourceRules(candidate.get('rules'));
1938
+ const definitionParent = getCandidateParent(candidate);
1939
+ const unlocked = unlockDetachedRulesetMixinCandidateOutput(
1940
+ sourceRules,
1941
+ invocationParent ?? definitionParent,
1942
+ definitionParent,
1943
+ candidate.index,
1944
+ context
1945
+ );
1946
+ outputRules.push(unlocked);
1947
+ continue;
1948
+ }
1949
+
1950
+ const candidateRenderKey = candidate.renderKey ?? context.renderKey;
1951
+ let rules = candidate
1952
+ .get('rules', candidateRenderKey ?? context)
1953
+ .withRenderOwner(candidate, candidateRenderKey, context);
1954
+ let params = getCurrentMixinParams(candidate, candidateRenderKey ?? context);
1955
+ const definitionParent = getCandidateParent(candidate);
1956
+ const candidateSourceParent = getUsableInvocationSourceParent(
1957
+ getCanonicalSourceParent(candidate as Node),
1958
+ getUsableInvocationSourceParent(
1959
+ getCanonicalSourceParent(definitionParent),
1960
+ sourceParent
1961
+ )
1962
+ );
1963
+ const prepared = prepareMixinCandidateInvocation(
1964
+ rules,
1965
+ params,
1966
+ definitionParent,
1967
+ candidateSourceParent,
1968
+ candidate.index,
1969
+ nodeArgs,
1970
+ context
1971
+ );
1972
+ rules = prepared.rules;
1973
+ params = prepared.params;
1974
+ const currentGuard = getCurrentRulesetGuard(candidate, candidateRenderKey ?? context) as Condition | Bool | undefined;
1975
+ const candidateHasDefault = Boolean(candidate.options?.hasDefault)
1976
+ || guardContainsDefault(currentGuard as Node | undefined);
1977
+ const pendingDefaultCandidate = await processPreparedMixinCandidate({
1978
+ candidate,
1979
+ rules,
1980
+ params,
1981
+ outerRules: prepared.outerRules,
1982
+ guard: currentGuard,
1983
+ parent: getCandidateParent(candidate),
1984
+ hasAnyDefault: hasDefault,
1985
+ candidateHasDefault,
1986
+ context,
1987
+ evaluateCandidateOutput
1988
+ });
1989
+ if (pendingDefaultCandidate) {
1990
+ pendingDefaultCandidates.push(pendingDefaultCandidate);
1991
+ }
1992
+ }
1993
+
1994
+ await replayWinningMixinDefaultCandidates(
1995
+ pendingDefaultCandidates,
1996
+ context,
1997
+ pending => evaluateCandidateOutput(
1998
+ pending.candidate,
1999
+ pending.rules,
2000
+ pending.outerRules,
2001
+ pending.params
2002
+ )
2003
+ );
2004
+
2005
+ if (
2006
+ skippedByRecursion
2007
+ && outputRules.length === 0
2008
+ && pendingDefaultCandidates.length === 0
2009
+ ) {
2010
+ throw new ReferenceError('No matching mixins found.');
2011
+ }
2012
+
2013
+ const output = assembleMixinInvocationOutput(
2014
+ outputRules,
2015
+ restrictMixinOutputLookup,
2016
+ context
2017
+ );
2018
+
2019
+ return output;
2020
+ }