@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,1716 @@
1
+ import { NodeTraversalCursor } from './util/collections.js';
2
+ import {
3
+ type TreeContext,
4
+ type Context
5
+ } from '../context.js';
6
+ import { type Visitor } from '../visitor/index.js';
7
+ import { type Operator } from './util/calculate.js';
8
+ import type { AbstractClass, Tagged } from 'type-fest';
9
+ import type { Comment } from './comment.js';
10
+ import { type PrintOptions, getPrintOptions } from './util/print.js';
11
+ import { type MaybePromise, pipe, isThenable, serialForEach } from '@jesscss/awaitable-pipe';
12
+ import type { Rules } from './rules.js';
13
+ import type { Nil } from './nil.js';
14
+ import { N, nodeTypeBits } from './node-type.js';
15
+ import { addParentEdge } from './util/cursor.js';
16
+ export type { TreeContext };
17
+
18
+ const { isArray } = Array;
19
+ type AllNodeOptions = {
20
+ /**
21
+ * This seems harder to implement. For now, for anything that needs
22
+ * to be flattened, we hoist it to the root.
23
+ */
24
+ // hoistToParent?: boolean
25
+
26
+ /**
27
+ * For statements with optional semis,
28
+ * we flag this for accurate re-serialization.
29
+ *
30
+ * @todo - Not sure if we actually need this, but it's here
31
+ * if we wanted a concrete syntax tree.
32
+ */
33
+ semi?: boolean;
34
+ };
35
+
36
+ /**
37
+ * @todo - Clean up and delete these types and symbols, if not used.
38
+ */
39
+ export type Primitive = undefined | boolean | string | number;
40
+ export type PrimitiveOrFunc = Primitive | ((...args: any[]) => any);
41
+
42
+ export const ABORT: unique symbol = Symbol('ABORT');
43
+ export const REMOVE: unique symbol = Symbol('REMOVE');
44
+ export const IS_PROXY: unique symbol = Symbol('IS_PROXY');
45
+ export type NodeVisitReturn = void | Node | symbol;
46
+ export type NodeOptions = Record<string, any> & AllNodeOptions;
47
+ export const DEFAULT_DATA = 'data';
48
+
49
+ type BasicNodeTypes = PrimitiveOrFunc | Node;
50
+ type NodeRecordValue = BasicNodeTypes | Array<BasicNodeTypes | PrimitiveOrFunc[]> | Record<string, any>;
51
+ export type NodeValueObject = Record<string, NodeRecordValue>;
52
+ export type NodeValue = BasicNodeTypes | BasicNodeTypes[] | NodeValueObject;
53
+
54
+ export type NodeMapArray<
55
+ T extends NodeValueObject = NodeValueObject,
56
+ K = keyof T,
57
+ V = T[string]
58
+ > = Array<[K, V]>;
59
+
60
+ export type LocationInfo = [
61
+ startOffset: number,
62
+ startLine: number,
63
+ startColumn: number,
64
+ endOffset: number,
65
+ endLine: number,
66
+ endColumn: number
67
+ ];
68
+
69
+ /**
70
+ * Values returned by {@link Node.location}: a full six-number span, or `[]` when unknown.
71
+ * The empty tuple is the lazy default assigned by the `location` getter.
72
+ */
73
+ export type LocationInfoOrEmpty = LocationInfo | [];
74
+
75
+ /**
76
+ * Location argument for node construction and APIs that accept another node's `location`.
77
+ * Same shape as {@link LocationInfoOrEmpty}, or `undefined` to defer to the empty default.
78
+ */
79
+ export type OptionalLocation = LocationInfoOrEmpty | undefined;
80
+
81
+ export type RenderKey = number | symbol;
82
+
83
+ export const CANONICAL: unique symbol = Symbol('CANONICAL');
84
+ export const EVAL: unique symbol = Symbol('EVAL');
85
+ export const CALLER: unique symbol = Symbol('CALLER');
86
+
87
+ export type NodeEdge<T> = Map<RenderKey, T>;
88
+
89
+ export type Cursor = {
90
+ node: Node;
91
+ renderKey: RenderKey;
92
+ };
93
+
94
+ function isContextArg(value: Context | RenderKey | undefined): value is Context {
95
+ return typeof value === 'object' && value !== null && 'rulesetFrames' in value;
96
+ }
97
+
98
+ function getActiveParentFromContext(
99
+ node: Node,
100
+ context?: Context
101
+ ): Node | undefined {
102
+ if (!context) {
103
+ return node.parent;
104
+ }
105
+ const keys: RenderKey[] = [];
106
+ const push = (key: RenderKey | undefined): void => {
107
+ if (key === undefined || key === CANONICAL || keys.includes(key)) {
108
+ return;
109
+ }
110
+ keys.push(key);
111
+ };
112
+ push(context.renderKey);
113
+ push(context.rulesContext?.renderKey);
114
+ push(node.renderKey);
115
+ if (node.parentEdges?.has(EVAL)) {
116
+ push(EVAL);
117
+ }
118
+ for (const key of keys) {
119
+ const parent = node.parentEdges?.get(key);
120
+ if (parent !== undefined) {
121
+ return parent;
122
+ }
123
+ }
124
+ return node.parent;
125
+ }
126
+
127
+ function hasTypeProperty(value: unknown): value is { type?: string } {
128
+ return (typeof value === 'object' || typeof value === 'function')
129
+ && value !== null
130
+ && 'type' in value;
131
+ }
132
+
133
+ function getNodeChildKeys(node: Node): readonly string[] | null | undefined {
134
+ const childKeys: readonly string[] | null | undefined = Reflect.get(node.constructor, 'childKeys');
135
+ return childKeys;
136
+ }
137
+
138
+ function getNodeField<T = unknown>(node: Node, key: string): T {
139
+ const value: T = Reflect.get(node, key);
140
+ return value;
141
+ }
142
+
143
+ function setNodeField(node: Node, key: string, value: unknown): void {
144
+ Reflect.set(node, key, value);
145
+ }
146
+
147
+ function getNodeEdge<T>(node: Node, key: string): NodeEdge<T> | undefined {
148
+ const edge = Reflect.get(node, key);
149
+ return edge instanceof Map ? edge : undefined;
150
+ }
151
+
152
+ function getNodeEdgeList(node: Node, key: string): Array<NodeEdge<unknown> | undefined> | undefined {
153
+ const edges = Reflect.get(node, key);
154
+ return Array.isArray(edges) ? edges : undefined;
155
+ }
156
+
157
+ function isRecord(value: unknown): value is Record<string, unknown> {
158
+ return typeof value === 'object' && value !== null;
159
+ }
160
+
161
+ function getNodeValue(node: Node): unknown {
162
+ return getNodeField(node, 'value');
163
+ }
164
+
165
+ export function canReuseEvalState(node: Node, context?: Context): boolean {
166
+ const renderKey = context?.renderKey;
167
+ if (renderKey === undefined || renderKey === CANONICAL) {
168
+ return true;
169
+ }
170
+ return node.renderKey === renderKey;
171
+ }
172
+
173
+ function setNodeEvaluated(node: Node, context?: Context): void {
174
+ if (!canReuseEvalState(node, context)) {
175
+ return;
176
+ }
177
+ setNodeField(node, 'evaluated', true);
178
+ }
179
+
180
+ function getNodeKeySetLibrary(node: Node): unknown {
181
+ return Reflect.get(node, 'keySetLibrary');
182
+ }
183
+
184
+ function setNodeKeySetLibrary(node: Node, library: unknown): void {
185
+ Reflect.set(node, 'keySetLibrary', library);
186
+ }
187
+
188
+ function isRulesNode(node: Node | undefined): node is Rules {
189
+ return node?.type === 'Rules';
190
+ }
191
+
192
+ function toPrimitiveValue(value: unknown): Primitive {
193
+ return (
194
+ value === undefined
195
+ || typeof value === 'string'
196
+ || typeof value === 'number'
197
+ || typeof value === 'boolean'
198
+ )
199
+ ? value
200
+ : String(value);
201
+ }
202
+
203
+ /**
204
+ * Utility type to mark a node's value as generated
205
+ */
206
+ export type GeneratedNodeValue<T> = T extends object ? T & { generated: true } : T;
207
+
208
+ export const defineType = <
209
+ V = never,
210
+ T extends AbstractClass<Node> = AbstractClass<Node>,
211
+ P extends ConstructorParameters<T> = ConstructorParameters<T>
212
+ >(
213
+ Clazz: T,
214
+ type: string,
215
+ shortType?: string
216
+ ) => {
217
+ shortType ??= type.toLowerCase();
218
+ Reflect.set(Clazz, 'type', type);
219
+ Reflect.set(Clazz, 'shortType', shortType);
220
+
221
+ /** Build nodeType bitmask by OR-ing bits for each type in the prototype chain */
222
+ let nodeType = 0;
223
+ let proto: unknown = Clazz;
224
+ /**
225
+ * @todo - We shouldn't have to crawl the prototype at runtime.
226
+ * We should be setting this explicitly in a parameter to defineType.
227
+ */
228
+ while (hasTypeProperty(proto) && proto.type) {
229
+ const bit = nodeTypeBits[proto.type];
230
+ if (bit !== undefined) {
231
+ nodeType |= bit;
232
+ }
233
+ proto = Object.getPrototypeOf(proto);
234
+ }
235
+ /** Set on the prototype so ALL instances (including `new Foo()`) inherit it */
236
+ Clazz.prototype.nodeType = nodeType;
237
+ Clazz.prototype.type = type;
238
+ Clazz.prototype.shortType = shortType;
239
+
240
+ type Args = [value?: P[0] | V, options?: P[1], location?: P[2]];
241
+ return (...args: Args) => {
242
+ const node: InstanceType<T> = Reflect.construct(Clazz, args);
243
+ return node;
244
+ };
245
+ };
246
+
247
+ export type ConditionOperator = 'and' | 'or' | '=' | '>' | '<' | '>=' | '<=';
248
+
249
+ export type NoOverride<T> = Tagged<T, 'NoOverride'>;
250
+
251
+ // Node state flags as bitmask
252
+ export const F_VISIBLE = 0b1;
253
+ export const F_MAY_ASYNC = 0b10;
254
+ /**
255
+ * @todo - The plan is to use these as signals for evaluation. If we
256
+ * bubble these correctly, then we can exit early from evaluation for
257
+ * a speed boost. However, bubbling is not yet water-tight and needs
258
+ * test coverage.
259
+ */
260
+ export const F_STATIC = 0b100;
261
+ export const F_NON_STATIC = 0b1000;
262
+ /** Whether or not a physical ampersand is in this selector */
263
+ export const F_AMPERSAND = 0b10000;
264
+ /** Whether an ampersand was implicitly added (not written by user) */
265
+ export const F_IMPLICIT_AMPERSAND = 0b100000;
266
+ /** Selector item produced by extend and eligible for reference-mode rendering. */
267
+ export const F_EXTENDED = 0b1000000;
268
+ /** Selector item that matches an extend target and should be suppressed in reference-mode output. */
269
+ export const F_EXTEND_TARGET = 0b10000000;
270
+
271
+ // Default state: only visible is true
272
+ export const F_DEFAULT = F_VISIBLE;
273
+
274
+ export function isVisibleInContext(node: Node, context?: Context): boolean {
275
+ return context ? node._hasFlag(F_VISIBLE, context) : node.hasFlag(F_VISIBLE);
276
+ }
277
+
278
+ /** Secondary metadata flags. Keeps a pile of booleans off the instance shape. */
279
+ const M_ALLOW_ROOT = 1 << 0;
280
+ const M_ALLOW_RULE_ROOT = 1 << 1;
281
+ const M_GENERATED = 1 << 2;
282
+ const M_REQUIRED_SEMI = 1 << 3;
283
+ const M_FROZEN = 1 << 4;
284
+
285
+ // Future flags can be added here
286
+ // export const CACHED = 0b1000000;
287
+ // export const DIRTY = 0b10000000;
288
+ // export const LOCKED = 0b100000000;
289
+
290
+ // const FULLY_EVALUATED = F_EVALUATED | F_PRE_EVALUATED;
291
+
292
+ export type RestorableIterator<T> = Iterator<T> & {
293
+ mark: (key?: string) => void;
294
+ reset: (key?: string) => void;
295
+ };
296
+
297
+ type NodeMeta<O extends NodeOptions = NodeOptions> = {
298
+ treeContext?: TreeContext;
299
+ options?: O & AllNodeOptions;
300
+ sourceNode?: Node;
301
+ sourceParent?: Node;
302
+ hoistToRoot?: boolean;
303
+ };
304
+
305
+ /**
306
+ * The underlying type for all Jess nodes
307
+ */
308
+ export abstract class Node<
309
+ Data = NodeValue,
310
+ O extends NodeOptions = NodeOptions,
311
+ ChildData extends Record<string, unknown> = Record<string, unknown>
312
+ > {
313
+ _location: OptionalLocation;
314
+ get location(): LocationInfoOrEmpty {
315
+ return (this._location ??= []);
316
+ }
317
+
318
+ private _meta: NodeMeta<O> | undefined;
319
+ private _metaFlags = 0;
320
+
321
+ private _getMeta(): NodeMeta<O> {
322
+ return (this._meta ??= {});
323
+ }
324
+
325
+ /** Assigned in index to avoid circularity */
326
+ get treeContext() {
327
+ return this._meta?.treeContext;
328
+ }
329
+
330
+ get options(): O & AllNodeOptions {
331
+ const meta = this._getMeta();
332
+ if (meta.options === undefined) {
333
+ meta.options = Reflect.construct(Object, []);
334
+ }
335
+ return meta.options;
336
+ }
337
+
338
+ set options(options: O & AllNodeOptions) {
339
+ this._getMeta().options = options;
340
+ }
341
+
342
+ /**
343
+ * Assigned on the prototype by defineType — do NOT initialize in subclasses
344
+ * (an `= 'X'` would create an own property that shadows the prototype value).
345
+ * Use interface merging to declare the literal type per node class.
346
+ */
347
+ declare type: string;
348
+ declare shortType: string;
349
+
350
+ /**
351
+ * Bitmask of this node's type and all ancestor types.
352
+ * Set on the prototype by defineType. Used by isNode for O(1) type checking.
353
+ * DO NOT initialize here — an `= 0` would create an own property that
354
+ * shadows the prototype value set by defineType.
355
+ */
356
+ declare nodeType: number;
357
+
358
+ /**
359
+ * Whitespace or comments before or after a Node.
360
+ *
361
+ * If this is `1`, it represents a single space character (' ').
362
+ * If it's 0, it means there were no pre/post tokens when parsed.
363
+ * If undefined, it means this was created using the API, and default
364
+ * formatting can be used.
365
+ * In a NodeList, any whitespace tokens outside of comments are individually represented,
366
+ * because they are preserved while the comment may not be.
367
+ */
368
+ /** Nil type is resolved at runtime via prototype patching */
369
+ pre: Array<Comment | Node | string> | 1 | 0 | undefined;
370
+ post: Array<Comment | Node | string> | 1 | 0 | undefined;
371
+
372
+ /** Will be copied during inherit */
373
+ state = F_DEFAULT;
374
+
375
+ preEvaluated = false;
376
+ evaluated = false;
377
+ declare stateEdges: Map<RenderKey, number> | undefined;
378
+
379
+ get visible() {
380
+ return this.hasFlag(F_VISIBLE);
381
+ }
382
+
383
+ declare fullRender: boolean;
384
+
385
+ get allowRoot() {
386
+ return (this._metaFlags & M_ALLOW_ROOT) !== 0;
387
+ }
388
+
389
+ set allowRoot(value: boolean) {
390
+ this._metaFlags = value ? (this._metaFlags | M_ALLOW_ROOT) : (this._metaFlags & ~M_ALLOW_ROOT);
391
+ }
392
+
393
+ get allowRuleRoot() {
394
+ return (this._metaFlags & M_ALLOW_RULE_ROOT) !== 0;
395
+ }
396
+
397
+ set allowRuleRoot(value: boolean) {
398
+ this._metaFlags = value ? (this._metaFlags | M_ALLOW_RULE_ROOT) : (this._metaFlags & ~M_ALLOW_RULE_ROOT);
399
+ }
400
+
401
+ get hoistToRoot() {
402
+ return this._meta?.hoistToRoot;
403
+ }
404
+
405
+ set hoistToRoot(value: boolean | undefined) {
406
+ if (value === undefined) {
407
+ if (this._meta) {
408
+ this._meta.hoistToRoot = undefined;
409
+ }
410
+ return;
411
+ }
412
+ this._getMeta().hoistToRoot = value;
413
+ }
414
+
415
+ /**
416
+ * Code internally should call .create() when making new
417
+ * nodes, which will automatically mark the node as generated.
418
+ */
419
+ get generated() {
420
+ return (this._metaFlags & M_GENERATED) !== 0;
421
+ }
422
+
423
+ set generated(value: boolean) {
424
+ this._metaFlags = value ? (this._metaFlags | M_GENERATED) : (this._metaFlags & ~M_GENERATED);
425
+ }
426
+
427
+ /**
428
+ * If the node must have a semi separator before
429
+ * the next node when in a declaration list or main
430
+ * rules list.
431
+ */
432
+ get requiredSemi() {
433
+ return (this._metaFlags & M_REQUIRED_SEMI) !== 0;
434
+ }
435
+
436
+ set requiredSemi(value: boolean) {
437
+ this._metaFlags = value ? (this._metaFlags | M_REQUIRED_SEMI) : (this._metaFlags & ~M_REQUIRED_SEMI);
438
+ }
439
+
440
+ /**
441
+ * Track the original source when cloned / copied,
442
+ * rather than keeping the entire tree
443
+ */
444
+ get sourceNode() {
445
+ return this._meta?.sourceNode ?? this;
446
+ }
447
+
448
+ set sourceNode(node: Node) {
449
+ this._getMeta().sourceNode = node;
450
+ }
451
+
452
+ /**
453
+ * When evaluating, nodes are assigned an index and depth by the Rules node.
454
+ * This is used for lookup order. Note, this _will_ be undefined
455
+ * initially, but we assign it in the Rules node, which is also
456
+ * where we read it, so this makes the type easier.
457
+ */
458
+ index!: number;
459
+
460
+ /** @todo - Is there a reliable way to cache this? */
461
+ get depth() {
462
+ let node = this.rulesParent;
463
+ let depth = 0;
464
+ while (node) {
465
+ depth++;
466
+ node = node.rulesParent;
467
+ }
468
+ return depth;
469
+ }
470
+
471
+ /**
472
+ * If true, prevents re-parenting of this node.
473
+ * This is used to maintain source lookup chains.
474
+ */
475
+ get frozen() {
476
+ return (this._metaFlags & M_FROZEN) !== 0;
477
+ }
478
+
479
+ set frozen(value: boolean) {
480
+ this._metaFlags = value ? (this._metaFlags | M_FROZEN) : (this._metaFlags & ~M_FROZEN);
481
+ }
482
+
483
+ /**
484
+ * The parent node of this node. Usually, this
485
+ * shouldn't be set directly. Instead, a parent should use
486
+ * parent.adopt(thisNode);
487
+ */
488
+ declare readonly parent: Node | undefined;
489
+ declare parentEdges: NodeEdge<Node> | undefined;
490
+ declare renderKey: RenderKey;
491
+
492
+ get sourceParent() {
493
+ return this._meta?.sourceParent;
494
+ }
495
+
496
+ set sourceParent(node: Node | undefined) {
497
+ this._getMeta().sourceParent = node;
498
+ }
499
+
500
+ /** Patched at runtime in node.ts to return Nil instance */
501
+ declare nil: () => Nil;
502
+
503
+ /**
504
+ * Keys of instance fields that hold child Nodes.
505
+ * Override per node type.
506
+ * - `undefined` (default) — unmigrated
507
+ * - `null` — leaf node, no children to iterate/adopt/clone
508
+ * - `string[]` — names of instance fields holding child Node(s) or Node[]
509
+ */
510
+ static childKeys: readonly string[] | null | undefined = undefined;
511
+
512
+ /**
513
+ * The internal data of the node.
514
+ * Prefer setData() for mutations to ensure proper parent adoption.
515
+ */
516
+ // Note to LLM - STOP removing Readonly to try to fix type errors. Make
517
+ // this a strong readonly contract. Otherwise we will miss type errors
518
+ // for things like code mutating arrays that are assigned to data.
519
+ // Uses `declare` to avoid emitting a class field initializer that would
520
+ // shadow prototype getters on migrated subclasses.
521
+
522
+ private _adoptValue(value: unknown): void {
523
+ if (value instanceof Node) {
524
+ this.adopt(value);
525
+ return;
526
+ }
527
+ if (isArray(value)) {
528
+ for (let i = 0; i < value.length; i++) {
529
+ const item = value[i];
530
+ if (item instanceof Node) {
531
+ this.adopt(item);
532
+ }
533
+ }
534
+ }
535
+ }
536
+
537
+ protected _invalidateValueOf(): void {
538
+ if (Reflect.has(this, '_valueOf')) {
539
+ Reflect.set(this, '_valueOf', undefined);
540
+ }
541
+ if (Reflect.has(this, '_keySet')) {
542
+ Reflect.set(this, '_keySet', undefined);
543
+ Reflect.set(this, '_visibleKeySet', undefined);
544
+ Reflect.set(this, '_requiredKeySet', undefined);
545
+ }
546
+ }
547
+
548
+ /**
549
+ * Set the whole child payload, a named child field, or an indexed child item.
550
+ * This is a canonical mutation compatibility seam; non-canonical mutation
551
+ * should happen through derived nodes and keyed edges.
552
+ */
553
+ setData(val: NodeValue): void;
554
+ setData(key: string | number, val: unknown): void;
555
+ setData(...args: unknown[]): void {
556
+ const childKeys = getNodeChildKeys(this);
557
+
558
+ if (args.length === 1) {
559
+ const val = args[0];
560
+ if (Array.isArray(childKeys) && childKeys.length === 1) {
561
+ setNodeField(this, childKeys[0]!, val);
562
+ } else if (Array.isArray(childKeys) && val !== null && typeof val === 'object') {
563
+ for (const key of childKeys) {
564
+ if (Reflect.has(val, key)) {
565
+ setNodeField(this, key, Reflect.get(val, key));
566
+ }
567
+ }
568
+ } else {
569
+ setNodeField(this, 'value', val);
570
+ }
571
+ this._adoptValue(val);
572
+ this._invalidateValueOf();
573
+ return;
574
+ }
575
+
576
+ const key = args[0];
577
+ if (typeof key !== 'string' && typeof key !== 'number') {
578
+ throw new TypeError('setData key must be a string or number');
579
+ }
580
+ const val = args[1];
581
+ if (typeof key === 'number') {
582
+ const arr = this._getArrayField();
583
+ if (arr[key] === val) {
584
+ return;
585
+ }
586
+ arr[key] = val;
587
+ } else {
588
+ const fields = this;
589
+ if (fields[key] === val) {
590
+ return;
591
+ }
592
+ fields[key] = val;
593
+ }
594
+ this._adoptValue(val);
595
+ this._invalidateValueOf();
596
+ }
597
+
598
+ private _getArrayField(): unknown[] {
599
+ const childKeys = getNodeChildKeys(this);
600
+ if (!Array.isArray(childKeys) || childKeys.length === 0) {
601
+ throw new Error(`${this.type} has no array child field`);
602
+ }
603
+ const key = childKeys[0]!;
604
+ const value = getNodeField(this, key);
605
+ if (!isArray(value)) {
606
+ throw new Error(`${this.type}.${key} is not an array child field`);
607
+ }
608
+ return value;
609
+ }
610
+
611
+ push(ctx: Context, ...items: Node[]): void;
612
+ push(...items: Node[]): void;
613
+ push(ctxOrFirst: Context | Node, ...rest: Node[]): void {
614
+ let ctx: Context | undefined;
615
+ let items: Node[];
616
+ if (ctxOrFirst instanceof Node) {
617
+ items = [ctxOrFirst, ...rest];
618
+ } else {
619
+ ctx = ctxOrFirst;
620
+ items = rest;
621
+ }
622
+ const arr = this._getArrayField();
623
+ arr.push(...items);
624
+ for (const item of items) {
625
+ if (item instanceof Node) {
626
+ this.adopt(item, ctx);
627
+ }
628
+ }
629
+ this._invalidateValueOf();
630
+ }
631
+
632
+ splice(start: number, deleteCount: number, ...items: unknown[]): unknown[] {
633
+ const arr = this._getArrayField();
634
+ const removed = arr.splice(start, deleteCount, ...items);
635
+ for (const item of items) {
636
+ if (item instanceof Node) {
637
+ this.adopt(item);
638
+ }
639
+ }
640
+ this._invalidateValueOf();
641
+ return removed;
642
+ }
643
+
644
+ unshift(...items: unknown[]): void {
645
+ const arr = this._getArrayField();
646
+ arr.unshift(...items);
647
+ for (const item of items) {
648
+ if (item instanceof Node) {
649
+ this.adopt(item);
650
+ }
651
+ }
652
+ this._invalidateValueOf();
653
+ }
654
+
655
+ /**
656
+ * Add a flag to the node's state
657
+ * Handles STATIC/NON_STATIC exclusivity automatically
658
+ */
659
+ addFlag(flag: number) {
660
+ // NON_STATIC takes precedence over STATIC
661
+ if (flag === F_STATIC && this.hasFlag(F_NON_STATIC)) {
662
+ return;
663
+ }
664
+ this.state |= flag;
665
+ // Handle STATIC/NON_STATIC exclusivity
666
+ if (flag === F_NON_STATIC) {
667
+ this.state &= ~F_STATIC;
668
+ }
669
+ }
670
+
671
+ /**
672
+ * Remove a flag from the node's state
673
+ */
674
+ removeFlag(flag: number) {
675
+ this.state &= ~flag;
676
+ }
677
+
678
+ /**
679
+ * Check if the node has a specific flag
680
+ */
681
+ hasFlag(flag: number): boolean {
682
+ return (this.state & flag) !== 0;
683
+ }
684
+
685
+ /**
686
+ * Add multiple flags to the node's state
687
+ */
688
+ addFlags(...flags: number[]) {
689
+ for (let i = 0; i < flags.length; i++) {
690
+ this.addFlag(flags[i]!);
691
+ }
692
+ }
693
+
694
+ private _resolveRuntimeRenderKey(context: Context): RenderKey {
695
+ if (context.renderKey !== undefined) {
696
+ return context.renderKey;
697
+ }
698
+ if (this.renderKey !== CANONICAL) {
699
+ return this.renderKey;
700
+ }
701
+ if (this.stateEdges?.has(EVAL)) {
702
+ return EVAL;
703
+ }
704
+ return this.renderKey;
705
+ }
706
+
707
+ _hasFlag(flag: number, context: Context): boolean {
708
+ const renderKey = this._resolveRuntimeRenderKey(context);
709
+ if (renderKey === this.renderKey) {
710
+ return this.hasFlag(flag);
711
+ }
712
+ const flags = this.stateEdges?.get(renderKey) ?? this.state;
713
+ return (flags & flag) !== 0;
714
+ }
715
+
716
+ _addFlag(flag: number, context: Context): void {
717
+ const renderKey = this._resolveRuntimeRenderKey(context);
718
+ if (renderKey === this.renderKey) {
719
+ this.addFlag(flag);
720
+ return;
721
+ }
722
+ const stateEdges = (this.stateEdges ??= new Map());
723
+ let nextFlags = (stateEdges.get(renderKey) ?? this.state) | flag;
724
+ if (flag === F_NON_STATIC) {
725
+ nextFlags &= ~F_STATIC;
726
+ }
727
+ stateEdges.set(renderKey, nextFlags);
728
+ }
729
+
730
+ _removeFlag(flag: number, context: Context): void {
731
+ const renderKey = this._resolveRuntimeRenderKey(context);
732
+ if (renderKey === this.renderKey) {
733
+ this.removeFlag(flag);
734
+ return;
735
+ }
736
+ const stateEdges = (this.stateEdges ??= new Map());
737
+ stateEdges.set(renderKey, (stateEdges.get(renderKey) ?? this.state) & ~flag);
738
+ }
739
+
740
+ adopt(node: Node, ctx?: Context) {
741
+ if (!node.frozen) {
742
+ const renderKey = ctx?.renderKey;
743
+ if (renderKey !== undefined && renderKey !== CANONICAL) {
744
+ const edge = node.parentEdges ?? new Map<RenderKey, Node>();
745
+ edge.set(renderKey, this);
746
+ node.parentEdges = edge;
747
+ } else {
748
+ setNodeField(node, 'parent', this);
749
+ }
750
+ }
751
+ if (node.hasFlag(F_NON_STATIC)) {
752
+ this.addFlag(F_NON_STATIC);
753
+ this.removeFlag(F_STATIC);
754
+ } else if (node.hasFlag(F_STATIC)) {
755
+ this.addFlag(F_STATIC);
756
+ }
757
+ if (node.hasFlag(F_MAY_ASYNC)) {
758
+ this.addFlag(F_MAY_ASYNC);
759
+ }
760
+ if (node.hasFlag(F_AMPERSAND) && this.type !== 'Rules') {
761
+ this.addFlag(F_AMPERSAND);
762
+ }
763
+ }
764
+
765
+ constructor(
766
+ value: Data,
767
+ options?: O,
768
+ location?: OptionalLocation,
769
+ treeContext?: TreeContext
770
+ ) {
771
+ setNodeField(this, 'parent', undefined);
772
+ setNodeField(this, 'renderKey', CANONICAL);
773
+ this.index = undefined!;
774
+ this._location = location;
775
+ if (options !== undefined || treeContext !== undefined) {
776
+ this._meta = {
777
+ sourceNode: this,
778
+ sourceParent: undefined,
779
+ options,
780
+ treeContext
781
+ };
782
+ } else {
783
+ this._meta = {
784
+ sourceNode: this,
785
+ sourceParent: undefined
786
+ };
787
+ }
788
+ }
789
+
790
+ /**
791
+ * Type-safe access to child data fields.
792
+ * Without a second arg: returns canonical (parse-time) value.
793
+ * With a renderKey: returns edge-selected value if one exists.
794
+ * With a context: returns edge-selected value first, then any eval-state-patched value.
795
+ *
796
+ * @example
797
+ * url.get('value') // canonical, typed
798
+ * url.get('value', renderKey) // render-path aware, typed
799
+ * url.get('value', ctx) // render + eval-state aware, typed
800
+ * url.get('name') // TS error if 'name' not in ChildData
801
+ */
802
+ get<K extends keyof ChildData & string>(key: K): ChildData[K];
803
+ get<K extends keyof ChildData & string>(key: K, renderKey: RenderKey | undefined): ChildData[K];
804
+ get<K extends keyof ChildData & string>(key: K, ctx: Context | undefined): ChildData[K];
805
+ get<K extends keyof ChildData & string>(key: K, ctxOrRenderKey?: Context | RenderKey | undefined): ChildData[K] {
806
+ const ctx = isContextArg(ctxOrRenderKey) ? ctxOrRenderKey : undefined;
807
+ const explicitRenderKey = !isContextArg(ctxOrRenderKey)
808
+ ? ctxOrRenderKey
809
+ : undefined;
810
+ const renderKeys: RenderKey[] = [];
811
+ const pushRenderKey = (renderKey: RenderKey | undefined) => {
812
+ if (renderKey === undefined || renderKey === CANONICAL || renderKeys.includes(renderKey)) {
813
+ return;
814
+ }
815
+ renderKeys.push(renderKey);
816
+ };
817
+ pushRenderKey(explicitRenderKey);
818
+ pushRenderKey(ctx?.renderKey);
819
+ pushRenderKey(ctx?.rulesContext?.renderKey);
820
+ pushRenderKey(this.renderKey !== CANONICAL ? this.renderKey : undefined);
821
+ const singularEdge = getNodeEdge<ChildData[K]>(this, `${key}Edge`);
822
+ const canonicalValue = getNodeField(this, key);
823
+ if (
824
+ ctx
825
+ && (singularEdge?.has(EVAL) || getNodeEdgeList(this, `${key}Edges`)?.some(edge => edge?.has(EVAL))
826
+ )) {
827
+ pushRenderKey(EVAL);
828
+ }
829
+
830
+ for (const renderKey of renderKeys) {
831
+ const overridden = singularEdge?.get(renderKey);
832
+ if (overridden !== undefined) {
833
+ return overridden;
834
+ }
835
+ if (isArray(canonicalValue)) {
836
+ const indexedEdges = getNodeEdgeList(this, `${key}Edges`);
837
+ if (indexedEdges) {
838
+ let resolved: ChildData[K] | undefined;
839
+ for (let i = 0; i < canonicalValue.length; i++) {
840
+ const item = indexedEdges[i]?.get(renderKey);
841
+ if (item !== undefined) {
842
+ if (!resolved) {
843
+ const nextResolved: ChildData[K] = [...canonicalValue];
844
+ resolved = nextResolved;
845
+ }
846
+ resolved[i] = item;
847
+ }
848
+ }
849
+ if (resolved) {
850
+ return resolved;
851
+ }
852
+ }
853
+ }
854
+ }
855
+
856
+ return getNodeField<ChildData[K]>(this, key);
857
+ }
858
+
859
+ /**
860
+ * Static factory method to create a generated node.
861
+ * Has the exact same signature as the constructor but automatically marks the node as generated.
862
+ *
863
+ * @param value - The node's value data
864
+ * @param options - Node options
865
+ * @param location - Location information
866
+ * @param treeContext - Tree context
867
+ * @returns A new node instance with generated flag set if applicable
868
+ */
869
+ static create<T extends new (...args: any[]) => Node>(
870
+ this: T,
871
+ value: ConstructorParameters<T>[0],
872
+ options?: ConstructorParameters<T>[1],
873
+ location?: ConstructorParameters<T>[2],
874
+ treeContext?: ConstructorParameters<T>[3]
875
+ ): InstanceType<T> {
876
+ // Create the instance with the same signature as constructor
877
+ const instance: InstanceType<T> = Reflect.construct(this, [value, options, location, treeContext]);
878
+
879
+ // Mark as generated if the value is an object that can be marked
880
+ if (instance instanceof Node) {
881
+ instance.generated = true;
882
+ }
883
+
884
+ return instance;
885
+ }
886
+
887
+ get rulesParent(): Rules | undefined {
888
+ let possibleRules: Node | undefined = this.parent;
889
+ while (possibleRules && possibleRules.type !== 'Rules') {
890
+ possibleRules = possibleRules.parent;
891
+ }
892
+ return isRulesNode(possibleRules) ? possibleRules : undefined;
893
+ }
894
+
895
+ get sourceRulesParent(): Rules | undefined {
896
+ let node = this.parent;
897
+ let sourceParent = this.sourceParent;
898
+ while (node && !sourceParent) {
899
+ node = node.parent;
900
+ sourceParent = node?.sourceParent;
901
+ }
902
+ return sourceParent?.rulesParent;
903
+ }
904
+
905
+ /**
906
+ * Mutates node children in place. Used by eval()?
907
+ *
908
+ * Processed nodes must always return a Node.
909
+ */
910
+ forEachNode(func: (n: Node, idx?: number) => MaybePromise<Node>, context?: Context) {
911
+ if (!this.hasFlag(F_MAY_ASYNC)) {
912
+ return this._forEachNodeSync(func, context);
913
+ }
914
+ const entries = this._collectChildEntries();
915
+ return serialForEach(entries, ([value, key, collection]: [unknown, string | number, any], idx: number) => {
916
+ if (!(value instanceof Node)) {
917
+ return;
918
+ }
919
+ const out = func(value, idx);
920
+ if (isThenable(out)) {
921
+ return (out as Promise<Node>).then((result) => {
922
+ if (result !== value) {
923
+ collection[key] = result;
924
+ if (result instanceof Node) {
925
+ this.adopt(result);
926
+ }
927
+ this._invalidateValueOf();
928
+ }
929
+ });
930
+ }
931
+ if (out !== value) {
932
+ collection[key] = out as Node;
933
+ this.adopt(out as Node);
934
+ this._invalidateValueOf();
935
+ }
936
+ });
937
+ }
938
+
939
+ private _collectChildEntries(): [unknown, string | number, any][] {
940
+ const ck = getNodeChildKeys(this);
941
+ if (!ck) {
942
+ return [];
943
+ }
944
+ const entries: [unknown, string | number, any][] = [];
945
+ for (const key of ck) {
946
+ const field = getNodeField(this, key);
947
+ if (isArray(field)) {
948
+ for (let i = 0; i < field.length; i++) {
949
+ entries.push([field[i], i, field]);
950
+ }
951
+ } else {
952
+ entries.push([field, key!, this]);
953
+ }
954
+ }
955
+ return entries;
956
+ }
957
+
958
+ private _forEachNodeSync(func: (n: Node, idx?: number) => Node, _context?: Context) {
959
+ const ck = getNodeChildKeys(this);
960
+
961
+ if (Array.isArray(ck)) {
962
+ let idx = 0;
963
+ for (const key of ck) {
964
+ const field = getNodeField(this, key);
965
+ if (isArray(field)) {
966
+ for (let i = 0; i < field.length; i++) {
967
+ const item = field[i];
968
+ if (!(item instanceof Node)) {
969
+ continue;
970
+ }
971
+ const result = func(item, idx++);
972
+ if (result !== item) {
973
+ field[i] = result;
974
+ this.adopt(result);
975
+ this._invalidateValueOf();
976
+ }
977
+ }
978
+ } else if (field instanceof Node) {
979
+ const result = func(field, idx++);
980
+ if (result !== field) {
981
+ setNodeField(this, key, result);
982
+ this.adopt(result);
983
+ this._invalidateValueOf();
984
+ }
985
+ }
986
+ }
987
+ }
988
+ }
989
+
990
+ * nodeAndPrePost(): IterableIterator<Node> {
991
+ const node = this;
992
+ if (isArray(node.pre)) {
993
+ for (let i = 0; i < node.pre.length; i++) {
994
+ const n = node.pre[i];
995
+ if (n instanceof Node) {
996
+ yield n;
997
+ }
998
+ }
999
+ }
1000
+ yield node;
1001
+ if (isArray(node.post)) {
1002
+ for (let i = 0; i < node.post.length; i++) {
1003
+ const n = node.post[i];
1004
+ if (n instanceof Node) {
1005
+ yield n;
1006
+ }
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ /**
1012
+ * Return an iterator for all nodes / children nodes, including this one
1013
+ */
1014
+ nodes(
1015
+ reverse?: boolean,
1016
+ includePrePost?: boolean
1017
+ ): NodeTraversalCursor {
1018
+ return new NodeTraversalCursor(this, {
1019
+ includeSelf: true,
1020
+ deep: true,
1021
+ reverse,
1022
+ includePrePost
1023
+ });
1024
+ }
1025
+
1026
+ /**
1027
+ * An iterator for all node children
1028
+ */
1029
+ children(
1030
+ deep?: boolean,
1031
+ reverse?: boolean,
1032
+ includePrePost?: boolean
1033
+ ): NodeTraversalCursor {
1034
+ return new NodeTraversalCursor(this, {
1035
+ includeSelf: false,
1036
+ deep,
1037
+ reverse,
1038
+ includePrePost
1039
+ });
1040
+ }
1041
+
1042
+ /**
1043
+ * Accept a visitor (classic visitor pattern).
1044
+ *
1045
+ * Visits the node itself first, then recursively visits children.
1046
+ * This matches the Less.js visitor pattern and allows nodes to control
1047
+ * their own traversal if needed by overriding this method.
1048
+ *
1049
+ * @param visitor - The visitor to accept
1050
+ * @returns The result from visiting this node (may be a replacement node)
1051
+ */
1052
+ accept(visitor: Visitor): Node {
1053
+ // Visit self first (like Less.js pattern).
1054
+ // Support both Visitor class instances (visit()) and plain visitor objects.
1055
+ let result: Node | NodeVisitReturn = this;
1056
+ const treeVisitMethod = Reflect.get(visitor, '_visit');
1057
+ const hasTreeVisitorState = Reflect.get(visitor, 'visitedNodes') instanceof Set;
1058
+ const visitMethod = Reflect.get(visitor, 'visit');
1059
+ if (typeof treeVisitMethod === 'function' && hasTreeVisitorState) {
1060
+ const visited: NodeVisitReturn = treeVisitMethod.call(visitor, this, {});
1061
+ result = visited;
1062
+ } else if (typeof visitMethod === 'function') {
1063
+ const visited: Node = visitMethod.call(visitor, this);
1064
+ result = visited;
1065
+ } else {
1066
+ const maybeAbort = visitor.enter?.(this);
1067
+ if (maybeAbort === ABORT) {
1068
+ return this;
1069
+ }
1070
+ const methodName = this.type.charAt(0).toLowerCase() + this.type.slice(1);
1071
+ const typeMethod = Reflect.get(visitor, methodName);
1072
+ if (typeof typeMethod === 'function') {
1073
+ const visited: NodeVisitReturn = typeMethod.call(visitor, this);
1074
+ if (visited) {
1075
+ result = visited;
1076
+ }
1077
+ }
1078
+ result = visitor.exit?.(result) ?? result;
1079
+ }
1080
+
1081
+ // Visit children recursively (Less.js pattern)
1082
+ // Note: If TreeVisitor is using accept(), it will skip auto-visiting children
1083
+ // to avoid double-visiting. See TreeVisitor._visit() implementation.
1084
+ for (const child of this.children()) {
1085
+ if (child.accept) {
1086
+ child.accept(visitor);
1087
+ } else {
1088
+ // Fallback: if child doesn't have accept, visit directly
1089
+ visitor.visit(child);
1090
+ }
1091
+ }
1092
+
1093
+ // Return the result (may be a replacement node)
1094
+ return result instanceof Node ? result : this;
1095
+ }
1096
+
1097
+ clone(deep?: boolean, cloneFn?: (n: Node) => Node, ctx?: Context): this {
1098
+ const ck = getNodeChildKeys(this);
1099
+
1100
+ // Leaf node — no children to iterate or deep-clone
1101
+ if (ck === null) {
1102
+ const options = this._meta?.options;
1103
+ const newNode: this = Reflect.construct(this.constructor, [getNodeValue(this), options ? { ...options } : undefined, this.location, this.treeContext]);
1104
+ newNode.inherit(this);
1105
+ return newNode;
1106
+ }
1107
+
1108
+ // Container — build constructor value from childKeys
1109
+ let cloneData: unknown;
1110
+ let cloneRecord: Record<string, unknown> | undefined;
1111
+ if (ck!.length === 1) {
1112
+ const field = getNodeField(this, ck![0]!);
1113
+ cloneData = isArray(field) ? [...field] : field;
1114
+ } else {
1115
+ cloneRecord = {};
1116
+ cloneData = cloneRecord;
1117
+ for (const key of ck!) {
1118
+ const field = getNodeField(this, key);
1119
+ cloneRecord[key] = isArray(field) ? [...field] : field;
1120
+ }
1121
+ }
1122
+
1123
+ if (deep) {
1124
+ cloneFn ??= n => n.clone(deep);
1125
+ if (ck!.length === 1) {
1126
+ if (isArray(cloneData)) {
1127
+ for (let i = 0; i < cloneData.length; i++) {
1128
+ if (cloneData[i] instanceof Node) {
1129
+ cloneData[i] = cloneFn(cloneData[i]);
1130
+ }
1131
+ }
1132
+ } else if (cloneData instanceof Node) {
1133
+ cloneData = cloneFn(cloneData);
1134
+ }
1135
+ } else {
1136
+ const cloneObject = cloneRecord;
1137
+ for (const key of ck!) {
1138
+ const val = cloneObject?.[key];
1139
+ if (isArray(val)) {
1140
+ for (let i = 0; i < val.length; i++) {
1141
+ if (val[i] instanceof Node) {
1142
+ val[i] = cloneFn(val[i]);
1143
+ }
1144
+ }
1145
+ } else if (val instanceof Node) {
1146
+ if (cloneObject) {
1147
+ cloneObject[key] = cloneFn(val);
1148
+ }
1149
+ }
1150
+ }
1151
+ }
1152
+ }
1153
+
1154
+ // When eval state is active and this is a shallow clone, the constructor will call
1155
+ // adopt() for all child nodes without ctx, which directly mutates their parent fields.
1156
+ // Save the pre-construction parent values so we can restore them after, routing the
1157
+ // new parent assignment through the eval state instead.
1158
+ let priorChildParents: [Node, Node | undefined][] | undefined;
1159
+ if (!deep && ctx) {
1160
+ priorChildParents = [];
1161
+ if (isArray(cloneData)) {
1162
+ for (const item of cloneData) {
1163
+ if (item instanceof Node) {
1164
+ priorChildParents.push([item, item.parent]);
1165
+ }
1166
+ }
1167
+ } else if (cloneData instanceof Node) {
1168
+ priorChildParents.push([cloneData, cloneData.parent]);
1169
+ } else if (isRecord(cloneData)) {
1170
+ for (const key of ck!) {
1171
+ const field = cloneData[key];
1172
+ if (field instanceof Node) {
1173
+ priorChildParents.push([field, field.parent]);
1174
+ } else if (isArray(field)) {
1175
+ for (const item of field) {
1176
+ if (item instanceof Node) {
1177
+ priorChildParents.push([item, item.parent]);
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ }
1183
+ }
1184
+
1185
+ const options = this._meta?.options;
1186
+ const newNode: this = Reflect.construct(this.constructor, [cloneData, options ? { ...options } : undefined, this.location, this.treeContext]);
1187
+
1188
+ // Reconnect shallow-cloned children on the active render path without
1189
+ // mutating canonical parent pointers.
1190
+ if (priorChildParents) {
1191
+ const renderKey = ctx!.renderKey ?? this.renderKey;
1192
+ for (const [child, priorParent] of priorChildParents) {
1193
+ if (renderKey !== undefined) {
1194
+ addParentEdge(child, renderKey, newNode);
1195
+ }
1196
+ setNodeField(child, 'parent', priorParent);
1197
+ }
1198
+ }
1199
+
1200
+ newNode.inherit(this);
1201
+ Node._inheritDerivedParent(this, newNode, ctx);
1202
+ return newNode;
1203
+ }
1204
+
1205
+ /** Remove comments from pre/post */
1206
+ stripPrePost(n: Node, preOrPost: 'pre' | 'post') {
1207
+ const prePost = n[preOrPost];
1208
+ if (isArray(prePost)) {
1209
+ n[preOrPost] = [...prePost];
1210
+ for (let [key, node] of prePost.entries()) {
1211
+ if (node instanceof Node && node.type === 'Comment') {
1212
+ /** Replace comment with a nil node that inherits location */
1213
+ const nilNode = this.nil?.() || this._createMinimalNil();
1214
+ prePost[key] = nilNode.inherit(node);
1215
+ }
1216
+ }
1217
+ }
1218
+ }
1219
+
1220
+ /** Minimal nil fallback for edge cases where prototype method isn't attached yet */
1221
+ private _createMinimalNil(): Node {
1222
+ // @ts-expect-error - normally an abstract class
1223
+ const nilish = new Node();
1224
+ nilish.type = 'Nil';
1225
+ nilish.shortType = 'nil';
1226
+ nilish.nodeType = nodeTypeBits['Nil']!;
1227
+ nilish.removeFlag(F_VISIBLE);
1228
+ return nilish;
1229
+ }
1230
+
1231
+ /**
1232
+ * Same as clone except comments are stripped.
1233
+ * This is used for variable referencing and
1234
+ * selector extending.
1235
+ */
1236
+ copy(deep?: boolean, cloneFn?: (n: Node) => Node): this {
1237
+ const newNode = this.clone(
1238
+ deep,
1239
+ (n) => {
1240
+ if (n.type !== 'Comment') {
1241
+ const copy = n.copy(deep, cloneFn);
1242
+ return copy;
1243
+ }
1244
+ const nilNode = this.nil?.() || this._createMinimalNil();
1245
+ return nilNode.inherit(n);
1246
+ }
1247
+ );
1248
+ if (this.hasFlag(F_AMPERSAND)) {
1249
+ newNode.addFlag(F_AMPERSAND);
1250
+ }
1251
+ if (this.hasFlag(F_IMPLICIT_AMPERSAND)) {
1252
+ newNode.addFlag(F_IMPLICIT_AMPERSAND);
1253
+ }
1254
+ // Strip comments from pre/post, preserving whitespace
1255
+ newNode.stripPrePost(newNode, 'pre');
1256
+ newNode.stripPrePost(newNode, 'post');
1257
+ return newNode;
1258
+ }
1259
+
1260
+ /**
1261
+ * `preEval` takes the following steps, which are extended in subclasses:
1262
+ * 1. Clone the node (if the source node is wanted/needed)
1263
+ * 2. Set `preEvaluated` to true
1264
+ * 3. pre-evaluate all children
1265
+ * 4. Return the node
1266
+ *
1267
+ * Mostly this is overridden to resolve names before registering.
1268
+ *
1269
+ * @todo - Update preEval / eval to use static evaluation based on flags.
1270
+ */
1271
+ preEval(context: Context): MaybePromise<Node> {
1272
+ if (!this.preEvaluated) {
1273
+ let node = this.clone();
1274
+ node.preEvaluated = true;
1275
+
1276
+ // Note: Rules nodes handle index assignment for themselves and their children
1277
+ // Other nodes will get indices assigned by their parent Rules
1278
+ let out: MaybePromise<void>;
1279
+ try {
1280
+ out = node.forEachNode(n => n.preEval(context), context);
1281
+ } catch (error: unknown) {
1282
+ throw error;
1283
+ }
1284
+ if (isThenable(out)) {
1285
+ return Promise.resolve(out).then(() => node).catch((error: unknown) => {
1286
+ throw error;
1287
+ });
1288
+ }
1289
+ return node;
1290
+ }
1291
+ return this;
1292
+ }
1293
+
1294
+ /**
1295
+ * This is the method all nodes will override.
1296
+ * Individual nodes will specify / narrow return type
1297
+ *
1298
+ * By default, evals all children
1299
+ */
1300
+ protected evalNode(context: Context): MaybePromise<Node> {
1301
+ if (this.hasFlag(F_STATIC)) {
1302
+ return this;
1303
+ }
1304
+ let out = this.forEachNode((n: Node) => {
1305
+ return n.eval(context);
1306
+ }, context);
1307
+ if (isThenable(out)) {
1308
+ return Promise.resolve(out).then(() => {
1309
+ return this;
1310
+ });
1311
+ }
1312
+ return this;
1313
+ }
1314
+
1315
+ static evalStatic(node: Node, context: Context): MaybePromise<Node> {
1316
+ const reusableState = canReuseEvalState(node, context);
1317
+ if (node.hasFlag(F_STATIC) && node.evaluated && reusableState) {
1318
+ return node;
1319
+ }
1320
+
1321
+ if (!node.hasFlag(F_MAY_ASYNC)) {
1322
+ return Node._evalStaticSync(node, context);
1323
+ }
1324
+
1325
+ let preEvaluatedNode: Node;
1326
+
1327
+ return pipe(
1328
+ () => {
1329
+ if (!node.preEvaluated || !reusableState) {
1330
+ return node.preEval(context);
1331
+ }
1332
+ return node;
1333
+ },
1334
+ (preEvald) => {
1335
+ preEvaluatedNode = preEvald;
1336
+ if (canReuseEvalState(preEvaluatedNode, context)) {
1337
+ preEvaluatedNode.preEvaluated = true;
1338
+ }
1339
+ if (preEvald !== node) {
1340
+ Node._inheritDerivedRenderKey(node, preEvaluatedNode, context);
1341
+ preEvaluatedNode.inherit(node);
1342
+ Node._inheritDerivedParent(node, preEvaluatedNode, context);
1343
+ }
1344
+ if (!preEvaluatedNode.evaluated || !canReuseEvalState(preEvaluatedNode, context)) {
1345
+ return preEvaluatedNode.evalNode(context);
1346
+ }
1347
+ return preEvaluatedNode;
1348
+ },
1349
+ (evald) => {
1350
+ setNodeEvaluated(evald, context);
1351
+ if (preEvaluatedNode !== evald && typeof evald.inherit === 'function') {
1352
+ Node._inheritDerivedRenderKey(preEvaluatedNode, evald, context);
1353
+ if (Node._shouldInheritEvalResult(preEvaluatedNode, evald)) {
1354
+ evald.inherit(preEvaluatedNode);
1355
+ Node._inheritDerivedParent(preEvaluatedNode, evald, context);
1356
+ }
1357
+ }
1358
+ return evald;
1359
+ }
1360
+ );
1361
+ }
1362
+
1363
+ private static _evalStaticSync(node: Node, context: Context): Node {
1364
+ let preEvaluatedNode: Node;
1365
+ const reusableState = canReuseEvalState(node, context);
1366
+
1367
+ if (!node.preEvaluated || !reusableState) {
1368
+ preEvaluatedNode = node.preEval(context);
1369
+ } else {
1370
+ preEvaluatedNode = node;
1371
+ }
1372
+ if (canReuseEvalState(preEvaluatedNode, context)) {
1373
+ preEvaluatedNode.preEvaluated = true;
1374
+ }
1375
+ if (preEvaluatedNode !== node) {
1376
+ Node._inheritDerivedRenderKey(node, preEvaluatedNode, context);
1377
+ preEvaluatedNode.inherit(node);
1378
+ Node._inheritDerivedParent(node, preEvaluatedNode, context);
1379
+ }
1380
+
1381
+ let evald: Node;
1382
+ if (!preEvaluatedNode.evaluated || !canReuseEvalState(preEvaluatedNode, context)) {
1383
+ evald = preEvaluatedNode.evalNode(context);
1384
+ } else {
1385
+ evald = preEvaluatedNode;
1386
+ }
1387
+ setNodeEvaluated(evald, context);
1388
+ if (preEvaluatedNode !== evald && typeof evald.inherit === 'function') {
1389
+ Node._inheritDerivedRenderKey(preEvaluatedNode, evald, context);
1390
+ if (Node._shouldInheritEvalResult(preEvaluatedNode, evald)) {
1391
+ evald.inherit(preEvaluatedNode);
1392
+ Node._inheritDerivedParent(preEvaluatedNode, evald, context);
1393
+ }
1394
+ }
1395
+ return evald;
1396
+ }
1397
+
1398
+ private static _shouldInheritEvalResult(source: Node, result: Node): boolean {
1399
+ if (source.type !== 'Reference') {
1400
+ return true;
1401
+ }
1402
+ return (result.nodeType & (N.Mixin | N.Ruleset | N.Rules | N.Func | N.JsFunction)) === 0;
1403
+ }
1404
+
1405
+ private static _inheritDerivedRenderKey(source: Node, derived: Node, context?: Context): void {
1406
+ if (source === derived || derived.renderKey !== CANONICAL) {
1407
+ return;
1408
+ }
1409
+ derived.renderKey = source.renderKey === CANONICAL
1410
+ ? (context?.renderKey ?? EVAL)
1411
+ : source.renderKey;
1412
+ }
1413
+
1414
+ private static _inheritDerivedParent(source: Node, derived: Node, context?: Context): void {
1415
+ if (source === derived) {
1416
+ return;
1417
+ }
1418
+ setNodeField(derived, 'parent', getActiveParentFromContext(source, context));
1419
+ }
1420
+
1421
+ /**
1422
+ * @note - Make sure you don't call super.eval while evaluating a node. Call it indirectly
1423
+ * from another node.
1424
+ */
1425
+ eval(context: Context): MaybePromise<Node> {
1426
+ if (Object.getPrototypeOf(this).eval !== Node.prototype.eval) {
1427
+ throw new Error('Do not call super.eval() from a subclass.');
1428
+ }
1429
+ return Node.evalStatic(this, context);
1430
+ }
1431
+
1432
+ /**
1433
+ * This is used when a Node will replace another node.
1434
+ */
1435
+ inherit(node: Node) {
1436
+ /**
1437
+ * Frozen nodes inherit the parent only if they don't have a parent yet.
1438
+ */
1439
+ if (!this.frozen) {
1440
+ setNodeField(this, 'parent', node.parent);
1441
+ } else {
1442
+ if (this.parent === undefined) {
1443
+ setNodeField(this, 'parent', node.parent);
1444
+ }
1445
+ }
1446
+ this._location = node.location;
1447
+ if (this._meta?.treeContext === undefined) {
1448
+ this._getMeta().treeContext = node.treeContext;
1449
+ }
1450
+ /** Copy state exactly (not OR, to preserve removed flags) */
1451
+ // Only sync F_VISIBLE flag, preserve all other flags
1452
+ if (!node.hasFlag(F_VISIBLE)) {
1453
+ this.removeFlag(F_VISIBLE);
1454
+ }
1455
+ // Preserve F_IMPLICIT_AMPERSAND so cloned selectors (e.g. after extend) keep invisible-ampersand
1456
+ // handling in createProcessedSelector and valueOf() remains correct for exact extend matching.
1457
+ if (node.hasFlag(F_IMPLICIT_AMPERSAND)) {
1458
+ this.addFlag(F_IMPLICIT_AMPERSAND);
1459
+ }
1460
+ if (node.hasFlag(F_EXTENDED)) {
1461
+ this.addFlag(F_EXTENDED);
1462
+ }
1463
+ if (node.hasFlag(F_EXTEND_TARGET)) {
1464
+ this.addFlag(F_EXTEND_TARGET);
1465
+ }
1466
+ // Note that we need to create new arrays if we mutate pre/post later
1467
+ this.pre ||= node.pre;
1468
+ this.post ||= node.post;
1469
+ this.sourceNode = node.sourceNode;
1470
+ this.sourceParent ??= node.sourceParent;
1471
+ if (node.hoistToRoot) {
1472
+ this.hoistToRoot = true;
1473
+ }
1474
+ if (getNodeKeySetLibrary(this) === undefined) {
1475
+ const keySetLibrary = getNodeKeySetLibrary(node);
1476
+ if (keySetLibrary !== undefined) {
1477
+ setNodeKeySetLibrary(this, keySetLibrary);
1478
+ }
1479
+ }
1480
+ // Preserve the generated flag when inheriting; never overwrite true with false
1481
+ // (e.g. Ampersand.eval returns PseudoSelector with .generated true, then evalStatic
1482
+ // calls PseudoSelector.inherit(Ampersand), which would otherwise overwrite with false)
1483
+ this.generated = this.generated || node.generated;
1484
+ /**
1485
+ * If it's replacing a node that's evaluated, it should inherit the same index.
1486
+ * Otherwise, it should be settable after cloning / copying.
1487
+ */
1488
+ this.index ??= node.index;
1489
+ return this;
1490
+ }
1491
+
1492
+ /**
1493
+ * Represents the normalized string value of the node,
1494
+ * for the purposes of comparison with other nodes,
1495
+ * regardless of type.
1496
+ *
1497
+ * Derived nodes will override this with different
1498
+ * normalization algorithms.
1499
+ */
1500
+ valueOf(_context?: Context): Primitive {
1501
+ const ck = getNodeChildKeys(this);
1502
+ if (!ck) {
1503
+ // Leaf node — value is a primitive
1504
+ return toPrimitiveValue(getNodeValue(this));
1505
+ }
1506
+ // Container — collect string values from children
1507
+ const parts: string[] = [];
1508
+ for (const key of ck) {
1509
+ const field = getNodeField(this, key);
1510
+ if (isArray(field)) {
1511
+ for (let i = 0; i < field.length; i++) {
1512
+ parts.push(`${field[i]}`);
1513
+ }
1514
+ } else if (field !== undefined) {
1515
+ parts.push(`${field}`);
1516
+ }
1517
+ }
1518
+ if (parts.length === 1) {
1519
+ return parts[0]!;
1520
+ }
1521
+ return parts.join('');
1522
+ }
1523
+
1524
+ processPrePost(key: 'pre' | 'post', defaultVal: string = '', options: PrintOptions) {
1525
+ options = getPrintOptions(options);
1526
+ const w = options.writer!;
1527
+ const mark = w.mark();
1528
+ let value = this[key];
1529
+ if (value === undefined) {
1530
+ if (defaultVal) {
1531
+ w.add(defaultVal);
1532
+ if (defaultVal === ' ') {
1533
+ w.signalBoundaryIntent(key, 'explicit_space');
1534
+ }
1535
+ }
1536
+ return w.getSince(mark);
1537
+ } else if (value === 0) {
1538
+ w.signalBoundaryIntent(key, 'explicit_none');
1539
+ return '';
1540
+ } else if (value === 1) {
1541
+ w.add(' ');
1542
+ w.signalBoundaryIntent(key, 'explicit_space');
1543
+ return w.getSince(mark);
1544
+ } else if (isArray(value)) {
1545
+ // Handle Node[] array - call toString() on each node (they will emit into writer)
1546
+ for (let i = 0; i < value.length; i++) {
1547
+ const node = value[i];
1548
+ if (node instanceof Node) {
1549
+ node.toString(options);
1550
+ } else {
1551
+ const s = String(node);
1552
+ w.add(s);
1553
+ }
1554
+ }
1555
+ return w.getSince(mark);
1556
+ } else {
1557
+ const s = String(value);
1558
+ w.add(s);
1559
+ return w.getSince(mark);
1560
+ }
1561
+ }
1562
+
1563
+ /**
1564
+ * This re-serializes the node, if needed. Will
1565
+ * likely be over-ridden in some cases.
1566
+ *
1567
+ * Note that this is the "as-is" representation of the
1568
+ * node, not the "evaluated" version.
1569
+ *
1570
+ * Note that the ToCssVisitor will be a little
1571
+ * more sophisticated, as it will re-format
1572
+ * to some extent by replacing newlines + spacing
1573
+ * with the appropriate amount of whitespace.
1574
+ *
1575
+ * @note toString() will, by default, include pre/post
1576
+ * white-space and comments, to make serialization
1577
+ * easy.
1578
+ *
1579
+ * In almost all Node cases, this should not be overriden,
1580
+ * and toTrimmedString() should be overridden instead.
1581
+ */
1582
+ toString(options?: PrintOptions, _renderKey?: RenderKey): string {
1583
+ if (!isVisibleInContext(this, options?.context) && !this.fullRender) {
1584
+ return '';
1585
+ }
1586
+ if (options?.suppressComments && this.type === 'Comment') {
1587
+ return '';
1588
+ }
1589
+ options = getPrintOptions(options);
1590
+ const w = options.writer!;
1591
+ const mark = w.mark();
1592
+ let pre = w.capture(() => this.processPrePost('pre', '', options));
1593
+ const bodyStr = w.capture(() => this.toTrimmedString(options));
1594
+ let post = w.capture(() => this.processPrePost('post', '', options));
1595
+
1596
+ let result = pre + bodyStr + post;
1597
+ // Trim output if flag is set
1598
+ w.add(result, this);
1599
+ return w.getSince(mark);
1600
+ }
1601
+
1602
+ /**
1603
+ * Serialize the evaluated tree. Requires context so position patches
1604
+ * (the virtual evaluated tree) are resolved during serialization.
1605
+ *
1606
+ * Use this instead of toString() when serializing eval results.
1607
+ * toString() serializes the canonical (parsed) tree without eval state.
1608
+ */
1609
+ render(options?: PrintOptions | Context, renderKey?: RenderKey): string {
1610
+ const normalizedOptions = isContextArg(options)
1611
+ ? { context: options }
1612
+ : options;
1613
+ return this.toString(normalizedOptions, renderKey);
1614
+ }
1615
+
1616
+ /**
1617
+ * The form of the node without pre/post comments and white-space
1618
+ *
1619
+ * @note - Internally, this still calls `toString()` on each value,
1620
+ * so that the internal spacing of the node serialization is
1621
+ * correct. This method just serializes a node without the outer
1622
+ * pre/post nodes.
1623
+ *
1624
+ * @todo - Simplify
1625
+ */
1626
+ toTrimmedString(options?: PrintOptions, _renderKey?: RenderKey) {
1627
+ options = getPrintOptions(options);
1628
+ const w = options.writer!;
1629
+ const mark = w.mark();
1630
+ const ck = getNodeChildKeys(this);
1631
+ const ctx = options.context;
1632
+ if (ck) {
1633
+ for (const key of ck) {
1634
+ // Resolve through eval state when context available
1635
+ const field = ctx
1636
+ ? this.get(key! as keyof ChildData & string, ctx)
1637
+ : getNodeField(this, key);
1638
+ if (isArray(field)) {
1639
+ for (const item of field) {
1640
+ if (item instanceof Node) {
1641
+ item.toString(options);
1642
+ } else {
1643
+ const s = item === undefined ? '' : String(item);
1644
+ if (s) {
1645
+ w.add(s, this);
1646
+ }
1647
+ }
1648
+ }
1649
+ } else if (field instanceof Node) {
1650
+ field.toString(options);
1651
+ } else {
1652
+ const s = field === undefined ? '' : String(field);
1653
+ if (s) {
1654
+ w.add(s, this);
1655
+ }
1656
+ }
1657
+ }
1658
+ } else {
1659
+ // Leaf node — render the primitive value directly
1660
+ const s = String(getNodeValue(this) ?? '');
1661
+ if (s) {
1662
+ w.add(s, this);
1663
+ }
1664
+ }
1665
+ return w.getSince(mark);
1666
+ }
1667
+
1668
+ /**
1669
+ * Individual node types will override this.
1670
+ *
1671
+ * This is just a default implementation.
1672
+ * 0 = equal (==)
1673
+ * 1 = greater than (>)
1674
+ * -1 = less than (<)
1675
+ * undefined = not comparable
1676
+ */
1677
+ compare(b: Node, context?: Context): 0 | 1 | -1 | undefined {
1678
+ let aVal = this.valueOf(context);
1679
+ let bVal = b.valueOf(context);
1680
+ if (aVal === bVal) {
1681
+ return 0;
1682
+ }
1683
+ if (aVal === undefined || bVal === undefined) {
1684
+ return undefined;
1685
+ }
1686
+ return aVal > bVal ? 1 : -1;
1687
+ }
1688
+
1689
+ /** Overridden in index.ts to avoid circularity */
1690
+ operate(_b: Node, _op: Operator, _context: Context): Node {
1691
+ return this;
1692
+ }
1693
+
1694
+ static numericCompare(a: number, b: number) {
1695
+ if (a === b) {
1696
+ return 0;
1697
+ } else if (Math.abs(a - b) < Number.EPSILON) {
1698
+ /** Close enough! Prevents floating point precision issues */
1699
+ return 0;
1700
+ } else if (a > b) {
1701
+ return 1;
1702
+ } else {
1703
+ return -1;
1704
+ }
1705
+ }
1706
+
1707
+ /**
1708
+ * Generates a .js module
1709
+ * @todo - Generate a .ts module & .js.map
1710
+ */
1711
+ /** Move to ToModuleVisitor */
1712
+ // toModule?(context: Context, out: OutputCollector): void
1713
+ }
1714
+
1715
+ /** When converting Less/Sass to Jess, we'll switch this flag temporarily */
1716
+ Node.prototype.fullRender = false;