@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,1271 @@
1
+ import {
2
+ ruleset,
3
+ sel,
4
+ el,
5
+ sellist,
6
+ rules,
7
+ decl,
8
+ vardecl,
9
+ spaced,
10
+ any,
11
+ call,
12
+ ref,
13
+ mixin,
14
+ fn,
15
+ condition,
16
+ expr,
17
+ Node,
18
+ type Rules,
19
+ List,
20
+ AssignmentType,
21
+ VarDeclaration,
22
+ style,
23
+ quoted,
24
+ atrule,
25
+ amp,
26
+ type Declaration,
27
+ type Selector
28
+ } from '../index.js';
29
+ import { vi } from 'vitest';
30
+ import { Context, TreeContext } from '../../context.js';
31
+ import type { FindOptions } from '../util/registry-utils.js';
32
+ import { isNode } from '../util/is-node.js';
33
+ import { getChildren, getParent, getSourceParent, markScopeDirty, setChildren, setParent, setSourceParent } from '../util/field-helpers.js';
34
+ import { N } from '../node-type.js';
35
+ import { EVAL } from '../node.js';
36
+ import { addEdgeAt, getParentEdge } from '../util/cursor.js';
37
+
38
+ let context: Context;
39
+
40
+ function getPropWithContext(context: Context, n: Rules, key: string, opts: FindOptions = {}) {
41
+ context.rulesContext = n;
42
+ opts.searchParents = true;
43
+ opts.context = context;
44
+ return n.find('declaration', key, 'Declaration', opts);
45
+ }
46
+
47
+ function getVarWithContext(context: Context, n: Rules, key: string, opts: FindOptions = {}) {
48
+ context.rulesContext = n;
49
+ opts.searchParents = true;
50
+ opts.context = context;
51
+ let decl = n.find('declaration', key, 'VarDeclaration', opts);
52
+ return decl;
53
+ }
54
+
55
+ // function getSelectorWithContext(context: Context, n: Rules, key: Selector, opts: FindOptions = {}, start?: number) {
56
+ // context.rulesContext = n;
57
+ // opts.searchParents = true;
58
+ // let decl = n.findDeclaration(key, 'VarDeclaration', opts);
59
+ // return decl;
60
+ // }
61
+
62
+ describe('Rules', () => {
63
+ beforeAll(() => {
64
+ Node.prototype.fullRender = true;
65
+ });
66
+
67
+ afterAll(() => {
68
+ Node.prototype.fullRender = false;
69
+ });
70
+
71
+ let getProp = getPropWithContext.bind(context, context);
72
+ let getVar = getVarWithContext.bind(context, context);
73
+ // let getSelector = getSelectorWithContext.bind(context, context);
74
+ beforeEach(() => {
75
+ context = new Context();
76
+ getProp = getPropWithContext.bind(context, context);
77
+ getVar = getVarWithContext.bind(context, context);
78
+ // getSelector = getSelectorWithContext.bind(context, context);
79
+ context.id = 'testing';
80
+ });
81
+
82
+ it.skip('assigns position linearly for nested rules', async () => {
83
+ let node = rules([
84
+ vardecl({ name: 'one', value: any('one') }),
85
+ vardecl({ name: 'root', value: any('value') }),
86
+ rules([
87
+ vardecl({ name: 'foo', value: any('bar') }),
88
+ vardecl({ name: 'one', value: any('two') }),
89
+ rules([
90
+ vardecl({ name: 'one', value: any('three') })
91
+ ])
92
+ ])
93
+ ]);
94
+ node = await node.eval(context);
95
+ let index = node.index;
96
+ expect(index).toBe(0);
97
+ expect(node.at(1, context)?.index).toBeGreaterThan(index);
98
+ index = node.at(1, context)?.index ?? index;
99
+ expect(node.at(2, context)?.index).toBeGreaterThan(index);
100
+ index = node.at(2, context)?.index ?? index;
101
+ expect((node.at(2, context) as Rules).at(0, context)?.index).toBeGreaterThan(index);
102
+ index = (node.at(2, context) as Rules).at(1, context)?.index ?? index;
103
+ expect((node.at(2, context) as Rules).at(2, context)?.index).toBeGreaterThan(index);
104
+ expect(((node.at(2, context) as Rules).at(2, context) as Rules).at(0, context)?.index).toBeGreaterThan(index);
105
+ });
106
+
107
+ describe('Scope / lookups', () => {
108
+ describe('set / get vars & props', () => {
109
+ it('can do a normal get / set of properties', async () => {
110
+ let node = rules([
111
+ decl({ name: 'foo', value: any('bar') })
112
+ ]);
113
+ node = await node.eval(context);
114
+
115
+ expect(`${getProp(node, 'foo')}`).toBe('foo: bar');
116
+ });
117
+
118
+ it('can do a normal get / set of variables', async () => {
119
+ let node = rules([
120
+ vardecl({ name: 'foo', value: any('bar') })
121
+ ]);
122
+ node = await node.eval(context);
123
+ expect(`${getVar(node, 'foo')}`).toBe('$foo: bar');
124
+ });
125
+
126
+ it('replaces variable values', async () => {
127
+ let node = rules([
128
+ vardecl({ name: 'foo', value: any('one') }),
129
+ vardecl({ name: 'foo', value: any('two') })
130
+ ]);
131
+ node = await node.eval(context);
132
+ expect(`${getVar(node, 'foo')}`).toBe('$foo: two');
133
+ });
134
+
135
+ it.skip('will not set if defined', async () => {
136
+ let decl1 = vardecl({ name: 'first', value: any('one') }, { assign: AssignmentType.CondAssign });
137
+ let decl2 = vardecl({ name: 'first', value: any('two') }, { assign: AssignmentType.CondAssign });
138
+ let node = rules([
139
+ decl1,
140
+ decl2
141
+ ]);
142
+ node = await node.eval(context);
143
+ /** This won't have been resolved, so we need to evaluate it. */
144
+ let result = await getVar(node, 'first')!.eval(context);
145
+ expect(result.render(context)).toBe('$first: one');
146
+ });
147
+
148
+ // it('will skip normalization', () => {
149
+ // scope.setVar('one', 'one', { isNormalized: true, protected: true })
150
+ // expect(scope.getVar('one')).toEqual('one')
151
+ // })
152
+
153
+ it('throws if undefined', async () => {
154
+ let node = rules([
155
+ decl({ name: 'foo', value: ref({ key: 'first' }, { type: 'variable' }) })
156
+ ]);
157
+ expect(() => {
158
+ const result = node.eval(context);
159
+ if (result instanceof Promise) {
160
+ // This shouldn't happen for this test case
161
+ throw new Error('Expected synchronous evaluation');
162
+ }
163
+ return result;
164
+ }).toThrow('\'first\' is not defined');
165
+ });
166
+
167
+ it('doesn\'t throw error if there\'s a fallback', async () => {
168
+ let node = rules([
169
+ decl({ name: 'foo', value: ref({ key: 'first' }, { type: 'variable', fallbackValue: true }) })
170
+ ]);
171
+ const result = node.eval(context);
172
+ if (result instanceof Promise) {
173
+ await expect(result).resolves.not.toThrow();
174
+ } else {
175
+ // Synchronous result, no error thrown
176
+ expect(result).toBeDefined();
177
+ }
178
+ });
179
+
180
+ it('does not retry style imports when content evaluation fails', async () => {
181
+ let attempts = 0;
182
+ let node = rules([
183
+ style({ path: quoted(any('retry-target.jess')) }, { type: 'import' })
184
+ ]);
185
+ const target = node.at(0, context);
186
+ if (!target) {
187
+ throw new Error('Expected first rule to exist');
188
+ }
189
+ // Simulate a content evaluation error (not a path resolution error).
190
+ // Only path resolution errors (tagged with _isPathResolutionError)
191
+ // should be retried — content errors mean the tree was already cloned
192
+ // and retrying would wastefully re-clone it.
193
+ target.eval = (() => {
194
+ attempts += 1;
195
+ throw new Error('content-eval-failure');
196
+ }) as typeof target.eval;
197
+
198
+ await expect(async () => {
199
+ await node.eval(context);
200
+ }).rejects.toThrow('content-eval-failure');
201
+
202
+ // Content evaluation errors are not retried — only path resolution errors are
203
+ expect(attempts).toBe(1);
204
+ });
205
+ });
206
+
207
+ describe('scope inheritance', () => {
208
+ it('looks up parent scope', async () => {
209
+ let inherited = rules([]);
210
+ let node = rules([
211
+ vardecl({ name: 'foo', value: any('bar') }),
212
+ inherited
213
+ ]);
214
+
215
+ node = await node.eval(context);
216
+ expect(`${getVar(inherited, 'foo')}`).toBe('$foo: bar');
217
+ });
218
+
219
+ it('inherits values when set after', async () => {
220
+ let inherited = rules([]);
221
+ let node = rules([
222
+ inherited
223
+ ]);
224
+ node.push(vardecl({ name: 'foo', value: any('bar') }));
225
+
226
+ node = await node.eval(context);
227
+ expect(`${getVar(inherited, 'foo')}`).toBe('$foo: bar');
228
+ });
229
+
230
+ it('characterizes shallow Rules clones as keeping canonical parentage while exposing shared top-level children through render-key parent edges', () => {
231
+ const ctx = new Context();
232
+ const nestedBody = rules([
233
+ decl({ name: 'color', value: any('red') })
234
+ ]);
235
+ const nested = ruleset({
236
+ selector: sel([el('.item')]),
237
+ rules: nestedBody
238
+ });
239
+ const node = rules([nested]);
240
+
241
+ const cloned = node.clone(false, undefined, ctx);
242
+ const clonedRuleset = cloned.at(0, context) as typeof nested;
243
+ const activeCtx = new Context();
244
+ activeCtx.renderKey = cloned.renderKey;
245
+
246
+ expect(clonedRuleset).toBe(nested);
247
+ expect(getParent(clonedRuleset, ctx)).toBe(node);
248
+ expect(getParent(clonedRuleset, activeCtx)).toBe(cloned);
249
+ expect(clonedRuleset.parent).toBe(node);
250
+ expect(clonedRuleset.get('rules')).toBe(nestedBody);
251
+ expect(clonedRuleset.get('rules').parent).toBe(clonedRuleset);
252
+ expect(clonedRuleset.get('rules').at(0, context)).toBe(nestedBody.at(0, context));
253
+ });
254
+
255
+ it('cloneDetachedUnlockWrapper keeps canonical parentage while giving shared top-level children an unlock-wrapper state parent', () => {
256
+ const ctx = new Context();
257
+ const nestedBody = rules([
258
+ decl({ name: 'color', value: any('red') })
259
+ ]);
260
+ const nested = ruleset({
261
+ selector: sel([el('.item')]),
262
+ rules: nestedBody
263
+ });
264
+ const node = rules([nested]);
265
+
266
+ const wrapper = node.cloneDetachedUnlockWrapper(ctx);
267
+ const wrappedRuleset = wrapper.at(0, context) as typeof nested;
268
+
269
+ expect(wrapper).not.toBe(node);
270
+ expect(wrappedRuleset).toBe(nested);
271
+ expect(wrappedRuleset.parent).toBe(node);
272
+ expect(getParentEdge({ node: wrappedRuleset, renderKey: wrapper.renderKey })?.node).toBe(wrapper);
273
+ expect(wrappedRuleset.get('rules')).toBe(nestedBody);
274
+ expect(wrappedRuleset.get('rules').parent).toBe(wrappedRuleset);
275
+ });
276
+
277
+ it('cloneVisibilityIsolationWrapper isolates rulesVisibility writes while keeping shared top-level children canonically parented', () => {
278
+ const ctx = new Context();
279
+ const nestedBody = rules([
280
+ decl({ name: 'color', value: any('red') })
281
+ ]);
282
+ const nested = ruleset({
283
+ selector: sel([el('.item')]),
284
+ rules: nestedBody
285
+ });
286
+ const node = rules([nested]);
287
+ node.options.rulesVisibility.VarDeclaration = 'optional';
288
+
289
+ const wrapper = node.cloneVisibilityIsolationWrapper(ctx);
290
+ const wrappedRuleset = wrapper.at(0, context) as typeof nested;
291
+
292
+ wrapper.options.rulesVisibility.VarDeclaration = 'private';
293
+
294
+ expect(wrapper.options).not.toBe(node.options);
295
+ expect(wrapper.options.rulesVisibility).not.toBe(node.options.rulesVisibility);
296
+ expect(wrapper.options.rulesVisibility.VarDeclaration).toBe('private');
297
+ expect(node.options.rulesVisibility.VarDeclaration).toBe('optional');
298
+ expect(wrappedRuleset.parent).toBe(node);
299
+ expect(getParentEdge({ node: wrappedRuleset, renderKey: wrapper.renderKey })?.node).toBe(wrapper);
300
+ expect(wrappedRuleset.get('rules')).toBe(nestedBody);
301
+ expect(wrappedRuleset.get('rules').parent).toBe(wrappedRuleset);
302
+ });
303
+
304
+ it('createShallowBodyWrapper reuses the same top-level child array', () => {
305
+ const ctx = new Context();
306
+ const nestedBody = rules([
307
+ decl({ name: 'color', value: any('red') })
308
+ ]);
309
+ const nested = ruleset({
310
+ selector: sel([el('.item')]),
311
+ rules: nestedBody
312
+ });
313
+ const node = rules([nested]);
314
+
315
+ const wrapper = node.createShallowBodyWrapper(ctx);
316
+
317
+ expect(wrapper).not.toBe(node);
318
+ expect(wrapper.value).toBe(node.value);
319
+ expect(wrapper.renderKey).toBe(EVAL);
320
+ expect(wrapper.at(0, context)).toBe(nested);
321
+ expect(getParentEdge({ node: nested, renderKey: wrapper.renderKey })?.node).toBe(wrapper);
322
+ expect(nested.parent).toBe(node);
323
+ });
324
+
325
+ it('render-key child replacement on a shallow wrapper updates parentEdges without corrupting canonical parentage', () => {
326
+ const ctx = new Context();
327
+ const nested = ruleset({
328
+ selector: sel([el('.item')]),
329
+ rules: rules([
330
+ decl({ name: 'color', value: any('red') })
331
+ ])
332
+ });
333
+ const node = rules([nested]);
334
+ const wrapper = node.createShallowBodyWrapper(ctx);
335
+ const replacement = ruleset({
336
+ selector: sel([el('.other')]),
337
+ rules: rules([
338
+ decl({ name: 'color', value: any('blue') })
339
+ ])
340
+ });
341
+
342
+ ctx.renderKey = wrapper.renderKey;
343
+ setChildren(wrapper, [replacement], ctx, { markDirty: false });
344
+
345
+ expect(wrapper.value[0]).toBe(replacement);
346
+ expect(getParent(replacement, ctx)).toBe(wrapper);
347
+ expect(getParent(nested, ctx)).toBe(node);
348
+ expect(node.value[0]).toBe(nested);
349
+ });
350
+
351
+ it('characterizes returned param-mixin nested bodies as correctly parented and already source-rooted in provenance', async () => {
352
+ const paramMixin = mixin({
353
+ name: any('.with-param'),
354
+ params: new List([
355
+ vardecl({ name: 'shade', value: any('red') }, { paramVar: true })
356
+ ]),
357
+ rules: rules([
358
+ ruleset({
359
+ selector: sel([el('.item')]),
360
+ rules: rules([
361
+ decl({ name: 'color', value: ref({ key: 'shade' }, { type: 'variable' }) })
362
+ ])
363
+ })
364
+ ])
365
+ });
366
+ const node = rules([
367
+ paramMixin,
368
+ call({
369
+ name: ref({ key: '.with-param' }, { type: 'mixin' }),
370
+ args: new List([any('blue')])
371
+ })
372
+ ]);
373
+
374
+ const evald = await node.eval(context);
375
+ expect(evald.toTrimmedString({ context })).toContain('.item {\n color: blue;\n}');
376
+ });
377
+
378
+ it('guarded mixin passes guard check and produces correct output', async () => {
379
+ const root = rules([
380
+ mixin({
381
+ name: any('.guarded'),
382
+ params: new List([
383
+ any('color', { role: 'property' })
384
+ ]),
385
+ guard: condition([
386
+ expr(ref({ key: 'color' }, { type: 'variable' })),
387
+ '=',
388
+ any('blue')
389
+ ]),
390
+ rules: rules([
391
+ ruleset({
392
+ selector: sel([el('.inner')]),
393
+ rules: rules([
394
+ decl({ name: 'color', value: ref({ key: 'color' }, { type: 'variable' }) })
395
+ ])
396
+ })
397
+ ])
398
+ }),
399
+ call({
400
+ name: ref({ key: '.guarded' }, { type: 'mixin' }),
401
+ args: new List([any('blue')])
402
+ })
403
+ ]);
404
+ const ctx = new Context();
405
+ const evald = await root.eval(ctx);
406
+
407
+ expect(evald.toTrimmedString({ context: ctx })).toContain('.inner');
408
+ expect(evald.toTrimmedString({ context: ctx })).toContain('color: blue');
409
+ });
410
+
411
+ it('peeks into optional child scope', async () => {
412
+ let node = rules([
413
+ rules([
414
+ vardecl({ name: 'one', value: any('two') })
415
+ ], {
416
+ rulesVisibility: {
417
+ VarDeclaration: 'optional'
418
+ }
419
+ })
420
+ ]);
421
+
422
+ node = await node.eval(context);
423
+ expect(`${getVar(node, 'one')}`).toBe('$one: two');
424
+ });
425
+
426
+ it('fails to get private child scope', async () => {
427
+ let node = rules([
428
+ rules([
429
+ vardecl({ name: 'one', value: any('two') })
430
+ ], {
431
+ rulesVisibility: {
432
+ VarDeclaration: 'private'
433
+ }
434
+ })
435
+ ]);
436
+
437
+ node = await node.eval(context);
438
+ expect(getVar(node, 'one')).toBeUndefined();
439
+ });
440
+
441
+ it('skips an optional value', async () => {
442
+ let node = rules([
443
+ vardecl({ name: 'one', value: any('one') }),
444
+ rules([
445
+ vardecl({ name: 'one', value: any('two') })
446
+ ], {
447
+ rulesVisibility: {
448
+ VarDeclaration: 'optional'
449
+ }
450
+ })
451
+ ]);
452
+
453
+ node = await node.eval(context);
454
+ expect(`${getVar(node, 'one')}`).toBe('$one: one');
455
+ });
456
+
457
+ it('returns optional value when no public value found', async () => {
458
+ let node = rules([
459
+ rules([
460
+ vardecl({ name: 'one', value: any('optional-value') })
461
+ ], {
462
+ rulesVisibility: {
463
+ VarDeclaration: 'optional'
464
+ }
465
+ }),
466
+ rules([
467
+ vardecl({ name: 'two', value: any('public-value') })
468
+ ])
469
+ ]);
470
+
471
+ node = await node.eval(context);
472
+ // Should find optional value since no public value exists
473
+ expect(`${getVar(node, 'one')}`).toBe('$one: optional-value');
474
+ // Should find public value
475
+ expect(`${getVar(node, 'two')}`).toBe('$two: public-value');
476
+ });
477
+
478
+ it('handles optional values with mixed positions and start parameter', async () => {
479
+ let node = rules([
480
+ vardecl({ name: 'var', value: any('first') }),
481
+ vardecl({ name: 'var', value: any('second') }),
482
+ rules([
483
+ vardecl({ name: 'var', value: any('optional-early') })
484
+ ], {
485
+ rulesVisibility: {
486
+ VarDeclaration: 'optional'
487
+ }
488
+ }),
489
+ vardecl({ name: 'var', value: any('third') }),
490
+ rules([
491
+ vardecl({ name: 'var', value: any('optional-late') })
492
+ ], {
493
+ rulesVisibility: {
494
+ VarDeclaration: 'optional'
495
+ }
496
+ })
497
+ ]);
498
+
499
+ node = await node.eval(context);
500
+ // Should find the last public value (third), not optional values
501
+ expect(`${getVar(node, 'var')}`).toBe('$var: third');
502
+
503
+ // Test with start parameter - should find value before start position
504
+ const thirdVar = node.value.find(n => isNode(n, N.VarDeclaration) && n.get('name').valueOf() === 'var' && n.get('value').valueOf() === 'third');
505
+ if (thirdVar && 'index' in thirdVar) {
506
+ const result = getVar(node, 'var', { start: thirdVar.index });
507
+ expect(result).toBeDefined();
508
+ expect(result.render(context)).toBe('$var: second');
509
+ }
510
+ });
511
+
512
+ it('handles nested optional Rules with different indexing', async () => {
513
+ let node = rules([
514
+ rules([
515
+ vardecl({ name: 'nested', value: any('nested-optional') }),
516
+ rules([
517
+ vardecl({ name: 'deep', value: any('deep-optional') })
518
+ ], {
519
+ rulesVisibility: {
520
+ VarDeclaration: 'optional'
521
+ }
522
+ })
523
+ ], {
524
+ rulesVisibility: {
525
+ VarDeclaration: 'optional'
526
+ }
527
+ }),
528
+ vardecl({ name: 'nested', value: any('public-nested') }),
529
+ vardecl({ name: 'deep', value: any('public-deep') })
530
+ ]);
531
+
532
+ node = await node.eval(context);
533
+ // Should find public value, not optional nested value
534
+ expect(`${getVar(node, 'nested')}`).toBe('$nested: public-nested');
535
+ // Should find public value, not optional deep value
536
+ expect(`${getVar(node, 'deep')}`).toBe('$deep: public-deep');
537
+ });
538
+
539
+ it('selects last optional value when multiple optionals found and no public', async () => {
540
+ let node = rules([
541
+ rules([
542
+ vardecl({ name: 'var', value: any('optional-first') })
543
+ ], {
544
+ rulesVisibility: {
545
+ VarDeclaration: 'optional'
546
+ }
547
+ }),
548
+ rules([
549
+ vardecl({ name: 'var', value: any('optional-second') })
550
+ ], {
551
+ rulesVisibility: {
552
+ VarDeclaration: 'optional'
553
+ }
554
+ }),
555
+ rules([
556
+ vardecl({ name: 'var', value: any('optional-third') })
557
+ ], {
558
+ rulesVisibility: {
559
+ VarDeclaration: 'optional'
560
+ }
561
+ })
562
+ ]);
563
+
564
+ node = await node.eval(context);
565
+ // Should find the last optional value by source order (comparePosition)
566
+ expect(`${getVar(node, 'var')}`).toBe('$var: optional-third');
567
+ });
568
+
569
+ it('handles optional values with start parameter in different Rules', async () => {
570
+ let node = rules([
571
+ vardecl({ name: 'var', value: any('root-first') }),
572
+ rules([
573
+ vardecl({ name: 'var', value: any('optional-in-child') })
574
+ ], {
575
+ rulesVisibility: {
576
+ VarDeclaration: 'optional'
577
+ }
578
+ }),
579
+ vardecl({ name: 'var', value: any('root-second') }),
580
+ vardecl({ name: 'var', value: any('root-third') })
581
+ ]);
582
+
583
+ node = await node.eval(context);
584
+ // Find the last public value
585
+ expect(`${getVar(node, 'var')}`).toBe('$var: root-third');
586
+
587
+ // Test with start parameter pointing to root-third
588
+ const thirdVar = node.value.find(n => isNode(n, N.VarDeclaration) && n.get('name').valueOf() === 'var' && n.get('value').valueOf() === 'root-third');
589
+ if (thirdVar && 'index' in thirdVar) {
590
+ const result = getVar(node, 'var', { start: thirdVar.index });
591
+ expect(result).toBeDefined();
592
+ // Should find root-second (before start), not optional value
593
+ expect(result.render(context)).toBe('$var: root-second');
594
+ }
595
+ });
596
+
597
+ it('handles complex scenario: public, optional, then public again', async () => {
598
+ let node = rules([
599
+ vardecl({ name: 'var', value: any('public-1') }),
600
+ rules([
601
+ vardecl({ name: 'var', value: any('optional-1') }),
602
+ vardecl({ name: 'var', value: any('optional-2') })
603
+ ], {
604
+ rulesVisibility: {
605
+ VarDeclaration: 'optional'
606
+ }
607
+ }),
608
+ vardecl({ name: 'var', value: any('public-2') })
609
+ ]);
610
+
611
+ node = await node.eval(context);
612
+ // Should find the last public value, ignoring optional values
613
+ expect(`${getVar(node, 'var')}`).toBe('$var: public-2');
614
+ });
615
+
616
+ it('handles optional values in Rules with different indices from parent', async () => {
617
+ // Create a scenario where child Rules have different indexing
618
+ let childRules = rules([
619
+ vardecl({ name: 'var', value: any('child-optional') })
620
+ ], {
621
+ rulesVisibility: {
622
+ VarDeclaration: 'optional'
623
+ }
624
+ });
625
+
626
+ let node = rules([
627
+ vardecl({ name: 'var', value: any('parent-1') }),
628
+ childRules,
629
+ vardecl({ name: 'var', value: any('parent-2') })
630
+ ]);
631
+
632
+ node = await node.eval(context);
633
+ // Should find parent-2 (last public), not child-optional
634
+ expect(`${getVar(node, 'var')}`).toBe('$var: parent-2');
635
+
636
+ // Test lookup from within child Rules - should find its own value
637
+ // Optional declarations are fallback-only and should not overtake public declarations
638
+ // that are reachable in the lookup chain.
639
+ const childVar = getVar(childRules, 'var');
640
+ expect(childVar).toBeDefined();
641
+ expect(`${childVar}`).toBe('$var: parent-2');
642
+ });
643
+
644
+ it('handles multiple optional Rules with declarations at different positions', async () => {
645
+ let node = rules([
646
+ rules([
647
+ vardecl({ name: 'a', value: any('optional-a-1') }),
648
+ vardecl({ name: 'b', value: any('optional-b-1') })
649
+ ], {
650
+ rulesVisibility: {
651
+ VarDeclaration: 'optional'
652
+ }
653
+ }),
654
+ vardecl({ name: 'a', value: any('public-a') }),
655
+ rules([
656
+ vardecl({ name: 'b', value: any('optional-b-2') }),
657
+ vardecl({ name: 'c', value: any('optional-c') })
658
+ ], {
659
+ rulesVisibility: {
660
+ VarDeclaration: 'optional'
661
+ }
662
+ }),
663
+ vardecl({ name: 'b', value: any('public-b') })
664
+ ]);
665
+
666
+ node = await node.eval(context);
667
+ // Should find public-a, ignoring optional-a-1
668
+ expect(`${getVar(node, 'a')}`).toBe('$a: public-a');
669
+ // Should find public-b, ignoring optional-b-1 and optional-b-2
670
+ expect(`${getVar(node, 'b')}`).toBe('$b: public-b');
671
+ // Should find optional-c since no public c exists
672
+ expect(`${getVar(node, 'c')}`).toBe('$c: optional-c');
673
+ });
674
+
675
+ it('shadows variables #1', async () => {
676
+ let node = rules([
677
+ vardecl({ name: 'one', value: any('one') }),
678
+ rules([
679
+ vardecl({ name: 'one', value: any('three') })
680
+ ])
681
+ ]);
682
+
683
+ node = await node.eval(context);
684
+ let inherited = node.at(1, context);
685
+ expect(`${getVar(inherited as Rules, 'one')}`).toBe('$one: three');
686
+ });
687
+
688
+ it('shadows variables #2', async () => {
689
+ let node = rules([
690
+ vardecl({ name: 'one', value: any('one') }),
691
+ rules([
692
+ vardecl({ name: 'one', value: any('two') }),
693
+ vardecl({ name: 'one', value: any('three') })
694
+ ])
695
+ ]);
696
+
697
+ node = await node.eval(context);
698
+ let inherited = node.at(1, context);
699
+ expect(`${getVar(inherited as Rules, 'one')}`).toBe('$one: three');
700
+ });
701
+
702
+ it.skip('sets existing variables', async () => {
703
+ let node = rules([
704
+ vardecl({ name: 'one', value: any('one') }),
705
+ rules([
706
+ vardecl({ name: 'one', value: any('three') }, { setDefined: true })
707
+ ])
708
+ ]);
709
+
710
+ node = await node.eval(context);
711
+ // With registry-based setDefined, the Rules node stays at index 1 (no array changes)
712
+ let inherited = node.at(1, context);
713
+ expect(`${getVar(node, 'one')}`).toBe('$one: three');
714
+ expect(`${getVar(inherited as Rules, 'one')}`).toBe('$one := three');
715
+ });
716
+
717
+ it.skip('demonstrates setDefined behavior like Sass !global', async () => {
718
+ let node = rules([
719
+ // Original variable declaration
720
+ vardecl({ name: 'color', value: any('red') }),
721
+
722
+ // First rule that uses the original value
723
+ rules([
724
+ decl({ name: 'background', value: ref('color', { type: 'variable', resolution: 'linear' }) })
725
+ ]),
726
+
727
+ // Nested rule that sets the variable with setDefined
728
+ rules([
729
+ vardecl({ name: 'color', value: any('blue') }, { setDefined: true })
730
+ ]),
731
+
732
+ // Subsequent rule that should use the updated value
733
+ rules([
734
+ decl({ name: 'border-color', value: ref('color', { type: 'variable', resolution: 'linear' }) })
735
+ ])
736
+ ]);
737
+
738
+ node = await node.eval(context);
739
+
740
+ // The first rule should use the original value (red) - setDefined shouldn't affect earlier references
741
+ let firstRule = node.at(1, context) as Rules; // First rule (background)
742
+ let firstDecl = firstRule.at(0, context) as Declaration;
743
+ let firstResult = await firstDecl.eval(context);
744
+ expect(`${firstResult}`).toBe('background: red');
745
+
746
+ // The last rule should also use the updated value (blue)
747
+ let lastRule = node.at(3, context) as Rules; // Last rule (border-color)
748
+ let lastDecl = lastRule.at(0, context) as Declaration;
749
+ let lastResult = await lastDecl.eval(context);
750
+ expect(`${lastResult}`).toBe('border-color: blue');
751
+
752
+ // The root should have the updated value
753
+ expect(`${getVar(node, 'color')}`).toBe('$color: blue');
754
+ });
755
+
756
+ it.skip('demonstrates Sass !global behavior with mixins - mixin resolves variables at include time', async () => {
757
+ // This test demonstrates the Sass behavior where:
758
+ // 1. A mixin is defined that uses a variable
759
+ // 2. The mixin is included before a !global assignment - it uses the original value
760
+ // 3. The mixin is included after a !global assignment - it uses the new value
761
+ //
762
+ // In Sass:
763
+ // $color: red;
764
+ // @mixin my-mixin() { color: $color; }
765
+ // .box { color: $color; @include my-mixin(); }
766
+ // .box2 { $color: blue !global; }
767
+ // .box3 { color: $color; @include my-mixin(); }
768
+ //
769
+ // Output:
770
+ // .box { color: red; color: red; }
771
+ // .box3 { color: blue; color: blue; }
772
+ //
773
+ // This test demonstrates Sass !global behavior with mixins using call-time resolution.
774
+ //
775
+ // Solution implemented: `$~color` syntax for call-time resolution.
776
+ // - `$color` = scoped lookup (Less-style)
777
+ // - `$^color` = linear lookup from definition position (Sass-style for regular code)
778
+ // - `$~color` = linear lookup from call site position (Sass-style for mixins/functions)
779
+ //
780
+ // When a mixin uses `$~color`, the variable is resolved at the call site, allowing
781
+ // !global assignments to affect mixin behavior correctly.
782
+
783
+ let node = rules([
784
+ // Global variable declaration
785
+ vardecl({ name: 'color', value: any('red') }),
786
+
787
+ // Mixin definition that uses the variable with call-time resolution
788
+ // In Jess, this would be: my-mixin() { color: $~color; }
789
+ // This makes the mixin resolve the variable at call time, not definition time
790
+ mixin({
791
+ name: any('my-mixin'),
792
+ rules: rules([
793
+ decl({ name: 'color', value: ref('color', { type: 'variable', resolution: 'call-time' }) })
794
+ ], { rulesVisibility: { VarDeclaration: 'optional' } })
795
+ }),
796
+
797
+ // .box uses the variable directly and includes the mixin (both should be red)
798
+ ruleset({
799
+ selector: sellist([sel([el('.box')])]),
800
+ rules: rules([
801
+ decl({ name: 'color', value: ref('color', { type: 'variable', resolution: 'linear' }) }),
802
+ call({ name: ref('my-mixin', { type: 'mixin' }) })
803
+ ])
804
+ }),
805
+
806
+ // .box2 sets the variable with !global (setDefined)
807
+ ruleset({
808
+ selector: sellist([sel([el('.box2')])]),
809
+ rules: rules([
810
+ vardecl({ name: 'color', value: any('blue') }, { setDefined: true })
811
+ ])
812
+ }),
813
+
814
+ // .box3 uses the variable directly and includes the mixin (both should be blue)
815
+ ruleset({
816
+ selector: sellist([sel([el('.box3')])]),
817
+ rules: rules([
818
+ decl({ name: 'color', value: ref('color', { type: 'variable', resolution: 'linear' }) }),
819
+ call({ name: ref('my-mixin', { type: 'mixin' }) })
820
+ ])
821
+ })
822
+ ]);
823
+
824
+ node = await node.eval(context);
825
+
826
+ // Structure after eval: [vardecl (0), mixin (1), boxRuleset (2), box2Ruleset (3), box3Ruleset (4)]
827
+ // Access rulesets directly by index
828
+ let boxRuleset = node.at(2, context);
829
+ if (!boxRuleset || !isNode(boxRuleset, N.Ruleset)) {
830
+ throw new Error(`Expected Ruleset at index 2, got ${boxRuleset?.type || 'undefined'}`);
831
+ }
832
+ // After evaluation, rulesets are still Rulesets, access via .value.rules
833
+ let boxRules = boxRuleset.get('rules');
834
+ if (!boxRules) {
835
+ throw new Error('Expected .box ruleset to have rules');
836
+ }
837
+ // Rules is a Node with a value array, so use .value.length or check if it's a Rules node
838
+ if (!isNode(boxRules, N.Rules)) {
839
+ throw new Error(`Expected Rules, got ${(boxRules as any)?.type || 'undefined'}`);
840
+ }
841
+ expect(boxRules.value.length).toBe(2);
842
+
843
+ // First declaration: color: $color
844
+ let boxDecl1 = await boxRules.at(0, context)!.eval(context);
845
+ expect(`${boxDecl1}`).toBe('color: red');
846
+
847
+ // Second: mixin call
848
+ let boxMixinCall = boxRules.at(1, context);
849
+ if (!boxMixinCall) {
850
+ throw new Error('Expected mixin call at index 1');
851
+ }
852
+ let boxMixinResult = await boxMixinCall.eval(context);
853
+ // Mixin call returns Rules containing the mixin's rules
854
+ if (!isNode(boxMixinResult, N.Rules)) {
855
+ throw new Error('Expected mixin call to return Rules');
856
+ }
857
+ let boxMixinRules = boxMixinResult;
858
+ expect(boxMixinRules.value.length).toBeGreaterThan(0);
859
+ let boxMixinDecl = await boxMixinRules.at(0, context)!.eval(context);
860
+ expect(`${boxMixinDecl}`).toBe('color: red');
861
+
862
+ // Find the .box3 ruleset (index 4)
863
+ let box3Ruleset = node.at(4, context);
864
+ if (!box3Ruleset || !isNode(box3Ruleset, N.Ruleset)) {
865
+ throw new Error(`Expected Ruleset at index 4, got ${box3Ruleset?.type || 'undefined'}`);
866
+ }
867
+ let box3Rules = box3Ruleset.get('rules');
868
+ if (!box3Rules) {
869
+ throw new Error('Expected .box3 ruleset to have rules');
870
+ }
871
+ if (!isNode(box3Rules, N.Rules)) {
872
+ throw new Error(`Expected Rules, got ${(box3Rules as any)?.type || 'undefined'}`);
873
+ }
874
+ expect(box3Rules.value.length).toBe(2);
875
+
876
+ // First declaration: color: $color
877
+ let box3Decl1 = await box3Rules.at(0, context)!.eval(context);
878
+ expect(`${box3Decl1}`).toBe('color: blue');
879
+
880
+ // Second: mixin call
881
+ let box3MixinCall = box3Rules.at(1, context);
882
+ if (!box3MixinCall) {
883
+ throw new Error('Expected mixin call at index 1');
884
+ }
885
+ let box3MixinResult = await box3MixinCall.eval(context);
886
+ if (!isNode(box3MixinResult, N.Rules)) {
887
+ throw new Error('Expected mixin call to return Rules');
888
+ }
889
+ let box3MixinRules = box3MixinResult;
890
+ expect(box3MixinRules.value.length).toBeGreaterThan(0);
891
+ let box3MixinDecl = await box3MixinRules.at(0, context)!.eval(context);
892
+ // With call-time resolution ($~color), the mixin should resolve the variable
893
+ // at the call site, so it should be 'blue' (the value after !global assignment)
894
+ expect(`${box3MixinDecl}`).toBe('color: blue');
895
+
896
+ // The root should have the updated value
897
+ let rootColor = getVar(node, 'color');
898
+ if (!rootColor) {
899
+ throw new Error('Expected color variable to be defined');
900
+ }
901
+ expect(`${rootColor}`).toBe('$color: blue');
902
+ });
903
+
904
+ it('fails to set if existing variable is readonly', async () => {
905
+ let node = rules([
906
+ vardecl({ name: 'one', value: any('one') }, { readonly: true }),
907
+ rules([
908
+ vardecl({ name: 'one', value: any('three') }, { setDefined: true })
909
+ ])
910
+ ]);
911
+
912
+ await expect(async () => {
913
+ await node.eval(context);
914
+ }).rejects.toThrowError('"one" is readonly');
915
+ });
916
+
917
+ // @todo: Fix nested readonly rules inheritance - variables in nested readonly Rules aren't being found
918
+ it.skip('fails to set if existing variable is in readonly rules', async () => {
919
+ let node = rules([
920
+ rules([
921
+ vardecl({ name: 'one', value: any('one') })
922
+ ], {
923
+ readonly: true,
924
+ rulesVisibility: { VarDeclaration: 'public' }
925
+ }),
926
+ rules([
927
+ vardecl({ name: 'one', value: any('three') }, { setDefined: true })
928
+ ])
929
+ ]);
930
+
931
+ await expect(async () => {
932
+ await node.eval(context);
933
+ }).rejects.toThrowError('"one" is readonly');
934
+ });
935
+
936
+ // @todo: Fix nested readonly rules inheritance - variables in nested readonly Rules aren't being found
937
+ it.skip('fails to set if existing variable is in nested readonly rules #1', async () => {
938
+ let node = rules([
939
+ rules([
940
+ rules([
941
+ vardecl({ name: 'one', value: any('one') })
942
+ ], {
943
+ readonly: true,
944
+ rulesVisibility: { VarDeclaration: 'public' }
945
+ })
946
+ ], {
947
+ rulesVisibility: { VarDeclaration: 'public' }
948
+ }),
949
+ rules([
950
+ vardecl({ name: 'one', value: any('three') }, { setDefined: true })
951
+ ])
952
+ ]);
953
+
954
+ await expect(async () => {
955
+ await node.eval(context);
956
+ }).rejects.toThrowError('"one" is readonly');
957
+ });
958
+
959
+ // @todo: Fix nested readonly rules inheritance - variables in nested readonly Rules aren't being found
960
+ it.skip('fails to set if existing variable is in nested readonly rules #2', async () => {
961
+ let node = rules([
962
+ rules([
963
+ rules([
964
+ vardecl({ name: 'one', value: any('one') })
965
+ ], {
966
+ rulesVisibility: { VarDeclaration: 'public' }
967
+ })
968
+ ], {
969
+ readonly: true,
970
+ rulesVisibility: { VarDeclaration: 'public' }
971
+ }),
972
+ rules([
973
+ vardecl({ name: 'one', value: any('three') }, { setDefined: true })
974
+ ])
975
+ ]);
976
+
977
+ await expect(async () => {
978
+ await node.eval(context);
979
+ }).rejects.toThrowError('"one" is readonly');
980
+ });
981
+
982
+ it('doesn\'t preserve readonly later', async () => {
983
+ let node = rules([
984
+ rules([
985
+ vardecl({ name: 'one', value: any('one') })
986
+ ], {
987
+ readonly: true,
988
+ rulesVisibility: { VarDeclaration: 'public' }
989
+ }),
990
+ rules([
991
+ vardecl({ name: 'one', value: any('two') })
992
+ ], {
993
+ rulesVisibility: { VarDeclaration: 'public' }
994
+ }),
995
+ rules([
996
+ /** This will set after the second rules value */
997
+ vardecl({ name: 'one', value: any('three') }, { setDefined: true })
998
+ ])
999
+ ]);
1000
+
1001
+ const result = node.eval(context);
1002
+ if (result instanceof Promise) {
1003
+ await expect(result).resolves.not.toThrow();
1004
+ } else {
1005
+ // Synchronous result, no error thrown
1006
+ expect(result).toBeDefined();
1007
+ }
1008
+ });
1009
+
1010
+ it('looks upwards from position', async () => {
1011
+ let node = rules([
1012
+ vardecl({ name: 'one', value: any('one') }),
1013
+ vardecl({ name: 'one', value: any('two') }),
1014
+ vardecl({ name: 'one', value: any('three') })
1015
+ ]);
1016
+ node = await node.eval(context);
1017
+
1018
+ expect(`${getVar(node, 'one', { start: node.at(1, context)?.index })}`).toBe('$one: one');
1019
+ expect(`${getVar(node, 'one', { start: node.at(2, context)?.index })}`).toBe('$one: two');
1020
+ expect(`${getVar(node, 'one', { start: 10 })}`).toBe('$one: three');
1021
+ });
1022
+
1023
+ it('won\'t find variables in sub-rules of local rules', async () => {
1024
+ let node = rules([ // root.jess
1025
+ rules([ // @-compose('child1.jess')
1026
+ vardecl({ name: 'foo', value: any('bar') }),
1027
+ rules([ // @-compose('child2.jess')
1028
+ vardecl({ name: 'one', value: any('two') })
1029
+ ], {
1030
+ local: true,
1031
+ rulesVisibility: { VarDeclaration: 'public' }
1032
+ })
1033
+ ], {
1034
+ local: true,
1035
+ rulesVisibility: { VarDeclaration: 'public' }
1036
+ })
1037
+ ]);
1038
+ node = await node.eval(context);
1039
+
1040
+ // child1.jess should see child2.jess's vars because it owns the `@use`
1041
+ expect(`${getVar(node.at(0, context) as Rules, 'one')}`).toBe('$one: two');
1042
+ // child1.jess can still see its own vars
1043
+ expect(`${getVar(node.at(0, context) as Rules, 'foo')}`).toBe('$foo: bar');
1044
+ // root.jess can see child1.jess's vars but not child2.jess's
1045
+ expect(`${getVar(node, 'foo')}`).toBe('$foo: bar');
1046
+ expect(getVar(node, 'one')).toBeUndefined();
1047
+ });
1048
+ });
1049
+ });
1050
+
1051
+ /** IT IS TIME */
1052
+ // describe('lookup selectors', () => {
1053
+ // it('can lookup a simple ruleset', async () => {
1054
+ // let node = rules([
1055
+ // ruleset({
1056
+ // selector: el('.foo'),
1057
+ // rules: rules([
1058
+ // decl({ name: 'foo', value: any('bar') })
1059
+ // ])
1060
+ // })
1061
+ // ]);
1062
+ // node = await node.eval(context);
1063
+
1064
+ // expect(getSelector(node, 'foo')).toBe('foo: bar');
1065
+ // });
1066
+ // });
1067
+
1068
+ it('should flatten rules when serializing', async () => {
1069
+ let node = rules([
1070
+ ruleset({
1071
+ selector: sellist([sel([el('.collapse')])]),
1072
+ rules: rules([
1073
+ decl({ name: 'chungus', value: spaced([any('foo'), any('bar')]) }),
1074
+ rules([
1075
+ decl({ name: 'bird', value: spaced([any('in'), any('hand')]) })
1076
+ ])
1077
+ ])
1078
+ })
1079
+ ]);
1080
+ let evald = await node.eval(context);
1081
+ expect(evald.render(context)).toBe('.collapse {\n chungus: foo bar;\n bird: in hand;\n}\n');
1082
+ });
1083
+
1084
+ // Deleted: registry cache tests — tested globalRegistryCache infrastructure
1085
+ // which was removed in the NodeState-scoped registry refactor.
1086
+
1087
+ describe.skip('eval state registry delta — registry deltas removed with EvalSession', () => {
1088
+ it('prefers state-only declaration entries over the canonical cache', () => {
1089
+ const root = rules([
1090
+ vardecl({ name: 'foo', value: any('bar') })
1091
+ ]);
1092
+ const ctx = new Context();
1093
+ const injected = vardecl({ name: 'bar', value: any('baz') });
1094
+
1095
+ root.getRegistry('declaration');
1096
+ root.register('declaration', injected, ctx);
1097
+
1098
+ expect(root.find('declaration', 'bar', 'VarDeclaration', {
1099
+ context: ctx,
1100
+ searchParents: false
1101
+ })).toBe(injected);
1102
+ expect(root.find('declaration', 'bar', 'VarDeclaration', {
1103
+ searchParents: false
1104
+ })).toBeUndefined();
1105
+ });
1106
+
1107
+ it('clears state-only registry entries when the scope is marked dirty', () => {
1108
+ const root = rules([
1109
+ vardecl({ name: 'foo', value: any('bar') })
1110
+ ]);
1111
+ const ctx = new Context();
1112
+ const injected = vardecl({ name: 'bar', value: any('baz') });
1113
+
1114
+ root.getRegistry('declaration');
1115
+ root.register('declaration', injected, ctx);
1116
+ markScopeDirty(root, ctx);
1117
+
1118
+ expect(root.find('declaration', 'bar', 'VarDeclaration', {
1119
+ context: ctx,
1120
+ searchParents: false
1121
+ })).toBeUndefined();
1122
+ });
1123
+
1124
+ it('keeps canonical parent pointers intact when unshifting shared nodes', () => {
1125
+ const shared = vardecl({ name: 'foo', value: any('bar') });
1126
+ const source = rules([shared]);
1127
+ const target = rules([]);
1128
+ const ctx = new Context();
1129
+ target.unshift(ctx, shared);
1130
+
1131
+ expect(shared.parent).toBe(source);
1132
+ expect(getParent(shared, ctx)).toBe(target);
1133
+ });
1134
+
1135
+ it('keeps canonical parent pointers intact when splicing shared nodes', () => {
1136
+ const shared = vardecl({ name: 'foo', value: any('bar') });
1137
+ const source = rules([shared]);
1138
+ const target = rules([]);
1139
+ const ctx = new Context();
1140
+ target.splice(ctx, 0, 0, shared);
1141
+
1142
+ expect(shared.parent).toBe(source);
1143
+ expect(getParent(shared, ctx)).toBe(target);
1144
+ });
1145
+
1146
+ it('keeps canonical children intact when unshifting', () => {
1147
+ const original = vardecl({ name: 'foo', value: any('bar') });
1148
+ const inserted = vardecl({ name: 'bar', value: any('baz') });
1149
+ const target = rules([original]);
1150
+ const ctx = new Context();
1151
+ target.unshift(ctx, inserted);
1152
+
1153
+ expect(target.value).toHaveLength(1);
1154
+ expect(target.at(0, context)).toBe(original);
1155
+ expect(target.at(0, ctx)).toBe(inserted);
1156
+ expect(target.at(1, ctx)).toBe(original);
1157
+ expect(inserted.parent).toBeUndefined();
1158
+ expect(getParent(inserted, ctx)).toBe(target);
1159
+ });
1160
+
1161
+ it('keeps canonical children intact when splicing', () => {
1162
+ const original = vardecl({ name: 'foo', value: any('bar') });
1163
+ const replacement = vardecl({ name: 'bar', value: any('baz') });
1164
+ const target = rules([original]);
1165
+ const ctx = new Context();
1166
+ const removed = target.splice(ctx, 0, 1, replacement);
1167
+
1168
+ expect(removed).toEqual([original]);
1169
+ expect(target.value).toHaveLength(1);
1170
+ expect(target.at(0, context)).toBe(original);
1171
+ expect(target.at(0, ctx)).toBe(replacement);
1172
+ expect(replacement.parent).toBeUndefined();
1173
+ expect(getParent(replacement, ctx)).toBe(target);
1174
+ });
1175
+
1176
+ it('preEval keeps charset replacement parented only in the eval state', async () => {
1177
+ const charset = any('@charset "utf-8"', { role: 'charset' });
1178
+ const root = rules([charset]);
1179
+ const ctx = new Context();
1180
+ const preEvald = await root.preEval(ctx);
1181
+ const replaced = preEvald.at(0, ctx);
1182
+
1183
+ expect(isNode(replaced as Node, N.Nil)).toBe(true);
1184
+ expect(getParent(replaced as Node, ctx)).toBe(root);
1185
+ expect((replaced as Node).parent).toBeUndefined();
1186
+ expect(preEvald.at(0, context)).toBe(charset);
1187
+ expect(root.value[0]).toBe(charset);
1188
+ expect(ctx.currentCharset).toBe(charset);
1189
+ });
1190
+ it('coalesces merged declarations using state-parent scope boundaries', async () => {
1191
+ const base = decl({ name: 'color', value: any('red') });
1192
+ const merged = decl(
1193
+ { name: 'color', value: any('blue') },
1194
+ { assign: AssignmentType.Add }
1195
+ );
1196
+ const root = rules([base, merged]);
1197
+ const foreignScope = rules([]);
1198
+ const ctx = new Context();
1199
+ const preEvald = await root.preEval(ctx);
1200
+ const mergedPreEvald = preEvald.at(1, ctx);
1201
+ if (!mergedPreEvald) {
1202
+ throw new Error('Expected merged declaration at index 1');
1203
+ }
1204
+ setParent(mergedPreEvald, foreignScope, ctx);
1205
+
1206
+ const evald = await preEvald.eval(ctx);
1207
+ const mergedEvald = evald.at(1, ctx);
1208
+
1209
+ expect(mergedEvald?.toTrimmedString({ context: ctx })).toBe('color: red, blue');
1210
+ expect(merged.toTrimmedString()).toContain('+:');
1211
+ });
1212
+
1213
+ it('preEval keeps cloned Rules parents in the eval state on eval reset', async () => {
1214
+ const child = rules([
1215
+ decl({ name: 'color', value: any('red') })
1216
+ ]);
1217
+ const root = rules([child]);
1218
+ const ctx = new Context();
1219
+ const preEvald = await child.preEval(ctx);
1220
+
1221
+ expect(preEvald).not.toBe(child);
1222
+ expect(getParent(preEvald as Rules, ctx)).toBe(root);
1223
+ expect((preEvald as Rules).parent).toBeUndefined();
1224
+ });
1225
+
1226
+ it('preEval detects nestable at-rule wrappers through the state parent chain', async () => {
1227
+ const wrapper = rules([
1228
+ ruleset({
1229
+ selector: amp(),
1230
+ rules: rules([
1231
+ decl({ name: 'color', value: any('red') })
1232
+ ])
1233
+ })
1234
+ ]);
1235
+ const media = atrule({
1236
+ name: any('@media', { role: 'atkeyword' }),
1237
+ prelude: any('screen')
1238
+ });
1239
+ const ctx = new Context();
1240
+ setParent(wrapper, media, ctx);
1241
+ const preEvald = await wrapper.preEval(ctx);
1242
+
1243
+ expect(preEvald).toBe(wrapper);
1244
+ expect(getParent(wrapper, ctx)).toBe(media);
1245
+ expect(wrapper.parent).toBeUndefined();
1246
+ });
1247
+
1248
+ it('reorders call-produced declaration-only Rules using the state sourceParent without mutating canonical children', () => {
1249
+ const nested = ruleset({
1250
+ selector: sellist([sel([el('.nested')])]),
1251
+ rules: rules([
1252
+ decl({ name: any('color'), value: any('red') })
1253
+ ])
1254
+ });
1255
+ const callProduced = rules([
1256
+ decl({ name: any('margin'), value: any('0') })
1257
+ ]);
1258
+ const root = rules([nested, callProduced]);
1259
+ const caller = call({ name: any('each') });
1260
+ const ctx = new Context();
1261
+ setSourceParent(callProduced, caller, ctx);
1262
+ (root as any)._normalizeCallDeclarationRulesOrder(root, ctx);
1263
+
1264
+ expect(getSourceParent(callProduced, ctx)).toBe(caller);
1265
+ expect(callProduced.sourceParent).toBeUndefined();
1266
+ expect(getChildren(root, ctx)[0]).toBe(callProduced);
1267
+ expect(root.value[0]).toBe(nested);
1268
+ expect(root.value[1]).toBe(callProduced);
1269
+ });
1270
+ });
1271
+ });