@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.
- package/lib/index.cjs +20159 -0
- package/lib/index.d.cts +5993 -0
- package/lib/index.d.cts.map +1 -0
- package/lib/index.d.ts +5992 -21
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +19926 -22
- package/lib/index.js.map +1 -1
- package/package.json +15 -14
- package/src/__tests__/define-function-record.test.ts +58 -0
- package/src/__tests__/define-function-simple.test.ts +55 -0
- package/src/__tests__/define-function-split-sequence.test.ts +547 -0
- package/src/__tests__/define-function-type-parity.test.ts +9 -0
- package/src/__tests__/define-function.test.ts +763 -0
- package/src/__tests__/num-operations.test.ts +91 -0
- package/src/__tests__/safe-parse.test.ts +374 -0
- package/src/context.ts +896 -0
- package/src/conversions.ts +282 -0
- package/src/debug-log.ts +29 -0
- package/src/define-function.ts +1006 -0
- package/src/deprecation.ts +67 -0
- package/src/globals.d.ts +26 -0
- package/src/index.ts +31 -0
- package/src/jess-error.ts +773 -0
- package/src/logger/deprecation-processing.ts +109 -0
- package/src/logger.ts +31 -0
- package/src/plugin.ts +292 -0
- package/src/tree/LOOKUP_CHAINS.md +35 -0
- package/src/tree/README.md +18 -0
- package/src/tree/__tests__/__snapshots__/extend-eval-integration.test.ts.snap +1455 -0
- package/src/tree/__tests__/ampersand.test.ts +382 -0
- package/src/tree/__tests__/at-rule.test.ts +2047 -0
- package/src/tree/__tests__/basic-render.test.ts +212 -0
- package/src/tree/__tests__/block.test.ts +40 -0
- package/src/tree/__tests__/call.test.ts +346 -0
- package/src/tree/__tests__/color.test.ts +537 -0
- package/src/tree/__tests__/condition.test.ts +186 -0
- package/src/tree/__tests__/control.test.ts +564 -0
- package/src/tree/__tests__/declaration.test.ts +253 -0
- package/src/tree/__tests__/dependency-graph.test.ts +177 -0
- package/src/tree/__tests__/detached-rulesets.test.ts +213 -0
- package/src/tree/__tests__/dimension.test.ts +236 -0
- package/src/tree/__tests__/expression.test.ts +73 -0
- package/src/tree/__tests__/ext-node.test.ts +31 -0
- package/src/tree/__tests__/extend-eval-integration.test.ts +1033 -0
- package/src/tree/__tests__/extend-import-style.test.ts +929 -0
- package/src/tree/__tests__/extend-less-fixtures.test.ts +851 -0
- package/src/tree/__tests__/extend-list.test.ts +31 -0
- package/src/tree/__tests__/extend-roots.test.ts +1045 -0
- package/src/tree/__tests__/extend-rules.test.ts +740 -0
- package/src/tree/__tests__/func.test.ts +171 -0
- package/src/tree/__tests__/import-js.test.ts +33 -0
- package/src/tree/__tests__/import-style-test-helpers.ts +56 -0
- package/src/tree/__tests__/import-style.test.ts +1967 -0
- package/src/tree/__tests__/interpolated-reference.test.ts +44 -0
- package/src/tree/__tests__/interpolated.test.ts +41 -0
- package/src/tree/__tests__/list.test.ts +177 -0
- package/src/tree/__tests__/log.test.ts +83 -0
- package/src/tree/__tests__/mixin-recursion.test.ts +639 -0
- package/src/tree/__tests__/mixin.test.ts +2171 -0
- package/src/tree/__tests__/negative.test.ts +45 -0
- package/src/tree/__tests__/nesting-collapse.test.ts +519 -0
- package/src/tree/__tests__/node-flags-perf.test.ts +195 -0
- package/src/tree/__tests__/node-flags.test.ts +410 -0
- package/src/tree/__tests__/node-graph.test.ts +598 -0
- package/src/tree/__tests__/node-mutation.test.ts +182 -0
- package/src/tree/__tests__/operation.test.ts +18 -0
- package/src/tree/__tests__/paren.test.ts +90 -0
- package/src/tree/__tests__/preserve-mode-output.test.ts +50 -0
- package/src/tree/__tests__/quoted.test.ts +72 -0
- package/src/tree/__tests__/range.test.ts +59 -0
- package/src/tree/__tests__/reference.test.ts +743 -0
- package/src/tree/__tests__/rest.test.ts +29 -0
- package/src/tree/__tests__/rules-raw.test.ts +14 -0
- package/src/tree/__tests__/rules.test.ts +1271 -0
- package/src/tree/__tests__/ruleset.test.ts +597 -0
- package/src/tree/__tests__/selector-attr.test.ts +50 -0
- package/src/tree/__tests__/selector-basic.test.ts +44 -0
- package/src/tree/__tests__/selector-capture.test.ts +22 -0
- package/src/tree/__tests__/selector-complex.test.ts +120 -0
- package/src/tree/__tests__/selector-compound.test.ts +74 -0
- package/src/tree/__tests__/selector-interpolated.test.ts +50 -0
- package/src/tree/__tests__/selector-list.test.ts +59 -0
- package/src/tree/__tests__/selector-pseudo.test.ts +23 -0
- package/src/tree/__tests__/selector.test.ts +182 -0
- package/src/tree/__tests__/sequence.test.ts +226 -0
- package/src/tree/__tests__/serialize-types.test.ts +529 -0
- package/src/tree/__tests__/spaced.test.ts +8 -0
- package/src/tree/__tests__/url.test.ts +72 -0
- package/src/tree/__tests__/var-declaration.test.ts +90 -0
- package/src/tree/ampersand.ts +538 -0
- package/src/tree/any.ts +169 -0
- package/src/tree/at-rule.ts +760 -0
- package/src/tree/block.ts +72 -0
- package/src/tree/bool.ts +46 -0
- package/src/tree/call.ts +593 -0
- package/src/tree/collection.ts +52 -0
- package/src/tree/color.ts +629 -0
- package/src/tree/combinator.ts +30 -0
- package/src/tree/comment.ts +36 -0
- package/src/tree/condition.ts +194 -0
- package/src/tree/control.ts +452 -0
- package/src/tree/declaration-custom.ts +56 -0
- package/src/tree/declaration-var.ts +87 -0
- package/src/tree/declaration.ts +742 -0
- package/src/tree/default-guard.ts +35 -0
- package/src/tree/dimension.ts +392 -0
- package/src/tree/expression.ts +97 -0
- package/src/tree/extend-list.ts +51 -0
- package/src/tree/extend.ts +391 -0
- package/src/tree/function.ts +254 -0
- package/src/tree/import-js.ts +130 -0
- package/src/tree/import-style.ts +875 -0
- package/{lib/tree/index.js → src/tree/index.ts} +49 -22
- package/src/tree/interpolated.ts +346 -0
- package/src/tree/js-array.ts +21 -0
- package/src/tree/js-expr.ts +50 -0
- package/src/tree/js-function.ts +31 -0
- package/src/tree/js-object.ts +22 -0
- package/src/tree/list.ts +415 -0
- package/src/tree/log.ts +89 -0
- package/src/tree/mixin.ts +331 -0
- package/src/tree/negative.ts +58 -0
- package/src/tree/nil.ts +57 -0
- package/src/tree/node-base.ts +1716 -0
- package/src/tree/node-type.ts +122 -0
- package/src/tree/node.ts +118 -0
- package/src/tree/number.ts +54 -0
- package/src/tree/operation.ts +187 -0
- package/src/tree/paren.ts +132 -0
- package/src/tree/query-condition.ts +47 -0
- package/src/tree/quoted.ts +119 -0
- package/src/tree/range.ts +101 -0
- package/src/tree/reference.ts +1099 -0
- package/src/tree/rest.ts +55 -0
- package/src/tree/rules-raw.ts +52 -0
- package/src/tree/rules.ts +2896 -0
- package/src/tree/ruleset.ts +1217 -0
- package/src/tree/selector-attr.ts +172 -0
- package/src/tree/selector-basic.ts +75 -0
- package/src/tree/selector-capture.ts +85 -0
- package/src/tree/selector-complex.ts +189 -0
- package/src/tree/selector-compound.ts +205 -0
- package/src/tree/selector-interpolated.ts +95 -0
- package/src/tree/selector-list.ts +245 -0
- package/src/tree/selector-pseudo.ts +173 -0
- package/src/tree/selector-simple.ts +10 -0
- package/src/tree/selector.ts +152 -0
- package/src/tree/sequence.ts +463 -0
- package/src/tree/tree.ts +130 -0
- package/src/tree/url.ts +95 -0
- package/src/tree/util/EXTEND_ARCHITECTURE_ANALYSIS.md +215 -0
- package/src/tree/util/EXTEND_AUDIT.md +233 -0
- package/src/tree/util/EXTEND_BASELINE.md +64 -0
- package/src/tree/util/EXTEND_CALL_GRAPH_ANALYSIS.md +244 -0
- package/src/tree/util/EXTEND_DOCS.md +24 -0
- package/src/tree/util/EXTEND_FINAL_SUMMARY.md +95 -0
- package/src/tree/util/EXTEND_FUNCTION_AUDIT.md +1433 -0
- package/src/tree/util/EXTEND_OPTIMIZATION_PLAN.md +114 -0
- package/src/tree/util/EXTEND_REFACTORING_SUMMARY.md +152 -0
- package/src/tree/util/EXTEND_RULES.md +74 -0
- package/src/tree/util/EXTEND_UNUSED_FUNCTIONS.md +127 -0
- package/src/tree/util/EXTEND_UNUSED_FUNCTIONS_ANALYSIS.md +227 -0
- package/src/tree/util/NODE_COPY_REDUCTION_PLAN.md +12 -0
- package/src/tree/util/__tests__/EXTEND_TEST_INDEX.md +59 -0
- package/src/tree/util/__tests__/OPTIMIZATION-ANALYSIS.md +130 -0
- package/src/tree/util/__tests__/WALK_AND_CONSUME_DESIGN.md +138 -0
- package/src/tree/util/__tests__/_archive/2026-02-09__OPTIMIZATION-ANALYSIS.md +9 -0
- package/src/tree/util/__tests__/_archive/README.md +4 -0
- package/src/tree/util/__tests__/bitset.test.ts +142 -0
- package/src/tree/util/__tests__/debug-log.ts +50 -0
- package/src/tree/util/__tests__/extend-comment-handling.test.ts +187 -0
- package/src/tree/util/__tests__/extend-core-unit.test.ts +941 -0
- package/src/tree/util/__tests__/extend-pipeline-bench.test.ts +154 -0
- package/src/tree/util/__tests__/extend-pipeline-bench.ts +190 -0
- package/src/tree/util/__tests__/fast-reject.test.ts +377 -0
- package/src/tree/util/__tests__/is-node.test.ts +63 -0
- package/src/tree/util/__tests__/list-like.test.ts +63 -0
- package/src/tree/util/__tests__/outputwriter.test.ts +523 -0
- package/src/tree/util/__tests__/print.test.ts +183 -0
- package/src/tree/util/__tests__/process-extends.test.ts +226 -0
- package/src/tree/util/__tests__/process-leading-is.test.ts +205 -0
- package/src/tree/util/__tests__/recursion-helper.test.ts +184 -0
- package/src/tree/util/__tests__/selector-match-unit.test.ts +1427 -0
- package/src/tree/util/__tests__/sourcemap.test.ts +117 -0
- package/src/tree/util/ampersand-template.ts +9 -0
- package/src/tree/util/bitset.ts +194 -0
- package/src/tree/util/calculate.ts +11 -0
- package/src/tree/util/cast.ts +89 -0
- package/src/tree/util/cloning.ts +8 -0
- package/src/tree/util/collections.ts +299 -0
- package/src/tree/util/compare.ts +90 -0
- package/src/tree/util/cursor.ts +171 -0
- package/src/tree/util/extend-core.ts +2139 -0
- package/src/tree/util/extend-roots.ts +1108 -0
- package/src/tree/util/field-helpers.ts +354 -0
- package/src/tree/util/is-node.ts +43 -0
- package/src/tree/util/list-like.ts +93 -0
- package/src/tree/util/mixin-instance-primitives.ts +2020 -0
- package/src/tree/util/print.ts +303 -0
- package/src/tree/util/process-leading-is.ts +421 -0
- package/src/tree/util/recursion-helper.ts +54 -0
- package/src/tree/util/regex.ts +2 -0
- package/src/tree/util/registry-utils.ts +1953 -0
- package/src/tree/util/ruleset-trace.ts +17 -0
- package/src/tree/util/scoped-body-eval.ts +320 -0
- package/src/tree/util/selector-match-core.ts +2005 -0
- package/src/tree/util/selector-utils.ts +757 -0
- package/src/tree/util/serialize-helper.ts +535 -0
- package/src/tree/util/serialize-types.ts +318 -0
- package/src/tree/util/should-operate.ts +78 -0
- package/src/tree/util/sourcemap.ts +37 -0
- package/src/types/config.ts +247 -0
- package/src/types/index.ts +12 -0
- package/{lib/types/modes.d.ts → src/types/modes.ts} +2 -1
- package/src/types.d.ts +9 -0
- package/src/types.ts +68 -0
- package/src/use-webpack-resolver.ts +56 -0
- package/src/visitor/__tests__/visitor.test.ts +136 -0
- package/src/visitor/index.ts +263 -0
- package/{lib/visitor/less-visitor.js → src/visitor/less-visitor.ts} +3 -2
- package/lib/context.d.ts +0 -352
- package/lib/context.d.ts.map +0 -1
- package/lib/context.js +0 -636
- package/lib/context.js.map +0 -1
- package/lib/conversions.d.ts +0 -73
- package/lib/conversions.d.ts.map +0 -1
- package/lib/conversions.js +0 -253
- package/lib/conversions.js.map +0 -1
- package/lib/debug-log.d.ts +0 -2
- package/lib/debug-log.d.ts.map +0 -1
- package/lib/debug-log.js +0 -27
- package/lib/debug-log.js.map +0 -1
- package/lib/define-function.d.ts +0 -587
- package/lib/define-function.d.ts.map +0 -1
- package/lib/define-function.js +0 -726
- package/lib/define-function.js.map +0 -1
- package/lib/deprecation.d.ts +0 -34
- package/lib/deprecation.d.ts.map +0 -1
- package/lib/deprecation.js +0 -57
- package/lib/deprecation.js.map +0 -1
- package/lib/jess-error.d.ts +0 -343
- package/lib/jess-error.d.ts.map +0 -1
- package/lib/jess-error.js +0 -508
- package/lib/jess-error.js.map +0 -1
- package/lib/logger/deprecation-processing.d.ts +0 -41
- package/lib/logger/deprecation-processing.d.ts.map +0 -1
- package/lib/logger/deprecation-processing.js +0 -81
- package/lib/logger/deprecation-processing.js.map +0 -1
- package/lib/logger.d.ts +0 -10
- package/lib/logger.d.ts.map +0 -1
- package/lib/logger.js +0 -20
- package/lib/logger.js.map +0 -1
- package/lib/plugin.d.ts +0 -94
- package/lib/plugin.d.ts.map +0 -1
- package/lib/plugin.js +0 -174
- package/lib/plugin.js.map +0 -1
- package/lib/tree/ampersand.d.ts +0 -98
- package/lib/tree/ampersand.d.ts.map +0 -1
- package/lib/tree/ampersand.js +0 -319
- package/lib/tree/ampersand.js.map +0 -1
- package/lib/tree/any.d.ts +0 -58
- package/lib/tree/any.d.ts.map +0 -1
- package/lib/tree/any.js +0 -104
- package/lib/tree/any.js.map +0 -1
- package/lib/tree/at-rule.d.ts +0 -53
- package/lib/tree/at-rule.d.ts.map +0 -1
- package/lib/tree/at-rule.js +0 -503
- package/lib/tree/at-rule.js.map +0 -1
- package/lib/tree/block.d.ts +0 -22
- package/lib/tree/block.d.ts.map +0 -1
- package/lib/tree/block.js +0 -24
- package/lib/tree/block.js.map +0 -1
- package/lib/tree/bool.d.ts +0 -18
- package/lib/tree/bool.d.ts.map +0 -1
- package/lib/tree/bool.js +0 -28
- package/lib/tree/bool.js.map +0 -1
- package/lib/tree/call.d.ts +0 -66
- package/lib/tree/call.d.ts.map +0 -1
- package/lib/tree/call.js +0 -306
- package/lib/tree/call.js.map +0 -1
- package/lib/tree/collection.d.ts +0 -30
- package/lib/tree/collection.d.ts.map +0 -1
- package/lib/tree/collection.js +0 -37
- package/lib/tree/collection.js.map +0 -1
- package/lib/tree/color.d.ts +0 -101
- package/lib/tree/color.d.ts.map +0 -1
- package/lib/tree/color.js +0 -513
- package/lib/tree/color.js.map +0 -1
- package/lib/tree/combinator.d.ts +0 -13
- package/lib/tree/combinator.d.ts.map +0 -1
- package/lib/tree/combinator.js +0 -12
- package/lib/tree/combinator.js.map +0 -1
- package/lib/tree/comment.d.ts +0 -20
- package/lib/tree/comment.d.ts.map +0 -1
- package/lib/tree/comment.js +0 -19
- package/lib/tree/comment.js.map +0 -1
- package/lib/tree/condition.d.ts +0 -31
- package/lib/tree/condition.d.ts.map +0 -1
- package/lib/tree/condition.js +0 -103
- package/lib/tree/condition.js.map +0 -1
- package/lib/tree/control.d.ts +0 -104
- package/lib/tree/control.d.ts.map +0 -1
- package/lib/tree/control.js +0 -430
- package/lib/tree/control.js.map +0 -1
- package/lib/tree/declaration-custom.d.ts +0 -18
- package/lib/tree/declaration-custom.d.ts.map +0 -1
- package/lib/tree/declaration-custom.js +0 -24
- package/lib/tree/declaration-custom.js.map +0 -1
- package/lib/tree/declaration-var.d.ts +0 -35
- package/lib/tree/declaration-var.d.ts.map +0 -1
- package/lib/tree/declaration-var.js +0 -63
- package/lib/tree/declaration-var.js.map +0 -1
- package/lib/tree/declaration.d.ts +0 -78
- package/lib/tree/declaration.d.ts.map +0 -1
- package/lib/tree/declaration.js +0 -286
- package/lib/tree/declaration.js.map +0 -1
- package/lib/tree/default-guard.d.ts +0 -15
- package/lib/tree/default-guard.d.ts.map +0 -1
- package/lib/tree/default-guard.js +0 -19
- package/lib/tree/default-guard.js.map +0 -1
- package/lib/tree/dimension.d.ts +0 -34
- package/lib/tree/dimension.d.ts.map +0 -1
- package/lib/tree/dimension.js +0 -294
- package/lib/tree/dimension.js.map +0 -1
- package/lib/tree/expression.d.ts +0 -25
- package/lib/tree/expression.d.ts.map +0 -1
- package/lib/tree/expression.js +0 -32
- package/lib/tree/expression.js.map +0 -1
- package/lib/tree/extend-list.d.ts +0 -23
- package/lib/tree/extend-list.d.ts.map +0 -1
- package/lib/tree/extend-list.js +0 -23
- package/lib/tree/extend-list.js.map +0 -1
- package/lib/tree/extend.d.ts +0 -47
- package/lib/tree/extend.d.ts.map +0 -1
- package/lib/tree/extend.js +0 -296
- package/lib/tree/extend.js.map +0 -1
- package/lib/tree/function.d.ts +0 -48
- package/lib/tree/function.d.ts.map +0 -1
- package/lib/tree/function.js +0 -74
- package/lib/tree/function.js.map +0 -1
- package/lib/tree/import-js.d.ts +0 -35
- package/lib/tree/import-js.d.ts.map +0 -1
- package/lib/tree/import-js.js +0 -45
- package/lib/tree/import-js.js.map +0 -1
- package/lib/tree/import-style.d.ts +0 -156
- package/lib/tree/import-style.d.ts.map +0 -1
- package/lib/tree/import-style.js +0 -566
- package/lib/tree/import-style.js.map +0 -1
- package/lib/tree/index.d.ts +0 -71
- package/lib/tree/index.d.ts.map +0 -1
- package/lib/tree/index.js.map +0 -1
- package/lib/tree/interpolated-reference.d.ts +0 -24
- package/lib/tree/interpolated-reference.d.ts.map +0 -1
- package/lib/tree/interpolated-reference.js +0 -37
- package/lib/tree/interpolated-reference.js.map +0 -1
- package/lib/tree/interpolated.d.ts +0 -62
- package/lib/tree/interpolated.d.ts.map +0 -1
- package/lib/tree/interpolated.js +0 -204
- package/lib/tree/interpolated.js.map +0 -1
- package/lib/tree/js-array.d.ts +0 -10
- package/lib/tree/js-array.d.ts.map +0 -1
- package/lib/tree/js-array.js +0 -10
- package/lib/tree/js-array.js.map +0 -1
- package/lib/tree/js-expr.d.ts +0 -23
- package/lib/tree/js-expr.d.ts.map +0 -1
- package/lib/tree/js-expr.js +0 -28
- package/lib/tree/js-expr.js.map +0 -1
- package/lib/tree/js-function.d.ts +0 -20
- package/lib/tree/js-function.d.ts.map +0 -1
- package/lib/tree/js-function.js +0 -16
- package/lib/tree/js-function.js.map +0 -1
- package/lib/tree/js-object.d.ts +0 -10
- package/lib/tree/js-object.d.ts.map +0 -1
- package/lib/tree/js-object.js +0 -10
- package/lib/tree/js-object.js.map +0 -1
- package/lib/tree/list.d.ts +0 -38
- package/lib/tree/list.d.ts.map +0 -1
- package/lib/tree/list.js +0 -83
- package/lib/tree/list.js.map +0 -1
- package/lib/tree/log.d.ts +0 -29
- package/lib/tree/log.d.ts.map +0 -1
- package/lib/tree/log.js +0 -56
- package/lib/tree/log.js.map +0 -1
- package/lib/tree/mixin.d.ts +0 -87
- package/lib/tree/mixin.d.ts.map +0 -1
- package/lib/tree/mixin.js +0 -112
- package/lib/tree/mixin.js.map +0 -1
- package/lib/tree/negative.d.ts +0 -17
- package/lib/tree/negative.d.ts.map +0 -1
- package/lib/tree/negative.js +0 -22
- package/lib/tree/negative.js.map +0 -1
- package/lib/tree/nil.d.ts +0 -30
- package/lib/tree/nil.d.ts.map +0 -1
- package/lib/tree/nil.js +0 -35
- package/lib/tree/nil.js.map +0 -1
- package/lib/tree/node-base.d.ts +0 -361
- package/lib/tree/node-base.d.ts.map +0 -1
- package/lib/tree/node-base.js +0 -930
- package/lib/tree/node-base.js.map +0 -1
- package/lib/tree/node.d.ts +0 -10
- package/lib/tree/node.d.ts.map +0 -1
- package/lib/tree/node.js +0 -45
- package/lib/tree/node.js.map +0 -1
- package/lib/tree/number.d.ts +0 -21
- package/lib/tree/number.d.ts.map +0 -1
- package/lib/tree/number.js +0 -27
- package/lib/tree/number.js.map +0 -1
- package/lib/tree/operation.d.ts +0 -26
- package/lib/tree/operation.d.ts.map +0 -1
- package/lib/tree/operation.js +0 -103
- package/lib/tree/operation.js.map +0 -1
- package/lib/tree/paren.d.ts +0 -19
- package/lib/tree/paren.d.ts.map +0 -1
- package/lib/tree/paren.js +0 -92
- package/lib/tree/paren.js.map +0 -1
- package/lib/tree/query-condition.d.ts +0 -17
- package/lib/tree/query-condition.d.ts.map +0 -1
- package/lib/tree/query-condition.js +0 -39
- package/lib/tree/query-condition.js.map +0 -1
- package/lib/tree/quoted.d.ts +0 -28
- package/lib/tree/quoted.d.ts.map +0 -1
- package/lib/tree/quoted.js +0 -75
- package/lib/tree/quoted.js.map +0 -1
- package/lib/tree/range.d.ts +0 -33
- package/lib/tree/range.d.ts.map +0 -1
- package/lib/tree/range.js +0 -47
- package/lib/tree/range.js.map +0 -1
- package/lib/tree/reference.d.ts +0 -76
- package/lib/tree/reference.d.ts.map +0 -1
- package/lib/tree/reference.js +0 -521
- package/lib/tree/reference.js.map +0 -1
- package/lib/tree/rest.d.ts +0 -15
- package/lib/tree/rest.d.ts.map +0 -1
- package/lib/tree/rest.js +0 -32
- package/lib/tree/rest.js.map +0 -1
- package/lib/tree/rules-raw.d.ts +0 -17
- package/lib/tree/rules-raw.d.ts.map +0 -1
- package/lib/tree/rules-raw.js +0 -37
- package/lib/tree/rules-raw.js.map +0 -1
- package/lib/tree/rules.d.ts +0 -262
- package/lib/tree/rules.d.ts.map +0 -1
- package/lib/tree/rules.js +0 -2359
- package/lib/tree/rules.js.map +0 -1
- package/lib/tree/ruleset.d.ts +0 -92
- package/lib/tree/ruleset.d.ts.map +0 -1
- package/lib/tree/ruleset.js +0 -528
- package/lib/tree/ruleset.js.map +0 -1
- package/lib/tree/selector-attr.d.ts +0 -31
- package/lib/tree/selector-attr.d.ts.map +0 -1
- package/lib/tree/selector-attr.js +0 -99
- package/lib/tree/selector-attr.js.map +0 -1
- package/lib/tree/selector-basic.d.ts +0 -24
- package/lib/tree/selector-basic.d.ts.map +0 -1
- package/lib/tree/selector-basic.js +0 -38
- package/lib/tree/selector-basic.js.map +0 -1
- package/lib/tree/selector-capture.d.ts +0 -23
- package/lib/tree/selector-capture.d.ts.map +0 -1
- package/lib/tree/selector-capture.js +0 -34
- package/lib/tree/selector-capture.js.map +0 -1
- package/lib/tree/selector-complex.d.ts +0 -40
- package/lib/tree/selector-complex.d.ts.map +0 -1
- package/lib/tree/selector-complex.js +0 -143
- package/lib/tree/selector-complex.js.map +0 -1
- package/lib/tree/selector-compound.d.ts +0 -16
- package/lib/tree/selector-compound.d.ts.map +0 -1
- package/lib/tree/selector-compound.js +0 -114
- package/lib/tree/selector-compound.js.map +0 -1
- package/lib/tree/selector-interpolated.d.ts +0 -23
- package/lib/tree/selector-interpolated.d.ts.map +0 -1
- package/lib/tree/selector-interpolated.js +0 -27
- package/lib/tree/selector-interpolated.js.map +0 -1
- package/lib/tree/selector-list.d.ts +0 -17
- package/lib/tree/selector-list.d.ts.map +0 -1
- package/lib/tree/selector-list.js +0 -174
- package/lib/tree/selector-list.js.map +0 -1
- package/lib/tree/selector-pseudo.d.ts +0 -42
- package/lib/tree/selector-pseudo.d.ts.map +0 -1
- package/lib/tree/selector-pseudo.js +0 -204
- package/lib/tree/selector-pseudo.js.map +0 -1
- package/lib/tree/selector-simple.d.ts +0 -5
- package/lib/tree/selector-simple.d.ts.map +0 -1
- package/lib/tree/selector-simple.js +0 -6
- package/lib/tree/selector-simple.js.map +0 -1
- package/lib/tree/selector.d.ts +0 -43
- package/lib/tree/selector.d.ts.map +0 -1
- package/lib/tree/selector.js +0 -56
- package/lib/tree/selector.js.map +0 -1
- package/lib/tree/sequence.d.ts +0 -43
- package/lib/tree/sequence.d.ts.map +0 -1
- package/lib/tree/sequence.js +0 -151
- package/lib/tree/sequence.js.map +0 -1
- package/lib/tree/tree.d.ts +0 -87
- package/lib/tree/tree.d.ts.map +0 -1
- package/lib/tree/tree.js +0 -2
- package/lib/tree/tree.js.map +0 -1
- package/lib/tree/url.d.ts +0 -18
- package/lib/tree/url.d.ts.map +0 -1
- package/lib/tree/url.js +0 -35
- package/lib/tree/url.js.map +0 -1
- package/lib/tree/util/__tests__/debug-log.d.ts +0 -1
- package/lib/tree/util/__tests__/debug-log.d.ts.map +0 -1
- package/lib/tree/util/__tests__/debug-log.js +0 -36
- package/lib/tree/util/__tests__/debug-log.js.map +0 -1
- package/lib/tree/util/calculate.d.ts +0 -3
- package/lib/tree/util/calculate.d.ts.map +0 -1
- package/lib/tree/util/calculate.js +0 -10
- package/lib/tree/util/calculate.js.map +0 -1
- package/lib/tree/util/cast.d.ts +0 -10
- package/lib/tree/util/cast.d.ts.map +0 -1
- package/lib/tree/util/cast.js +0 -87
- package/lib/tree/util/cast.js.map +0 -1
- package/lib/tree/util/cloning.d.ts +0 -4
- package/lib/tree/util/cloning.d.ts.map +0 -1
- package/lib/tree/util/cloning.js +0 -8
- package/lib/tree/util/cloning.js.map +0 -1
- package/lib/tree/util/collections.d.ts +0 -57
- package/lib/tree/util/collections.d.ts.map +0 -1
- package/lib/tree/util/collections.js +0 -136
- package/lib/tree/util/collections.js.map +0 -1
- package/lib/tree/util/compare.d.ts +0 -11
- package/lib/tree/util/compare.d.ts.map +0 -1
- package/lib/tree/util/compare.js +0 -89
- package/lib/tree/util/compare.js.map +0 -1
- package/lib/tree/util/extend-helpers.d.ts +0 -2
- package/lib/tree/util/extend-helpers.d.ts.map +0 -1
- package/lib/tree/util/extend-helpers.js +0 -2
- package/lib/tree/util/extend-helpers.js.map +0 -1
- package/lib/tree/util/extend-roots.d.ts +0 -37
- package/lib/tree/util/extend-roots.d.ts.map +0 -1
- package/lib/tree/util/extend-roots.js +0 -700
- package/lib/tree/util/extend-roots.js.map +0 -1
- package/lib/tree/util/extend-roots.old.d.ts +0 -132
- package/lib/tree/util/extend-roots.old.d.ts.map +0 -1
- package/lib/tree/util/extend-roots.old.js +0 -2272
- package/lib/tree/util/extend-roots.old.js.map +0 -1
- package/lib/tree/util/extend-trace-debug.d.ts +0 -13
- package/lib/tree/util/extend-trace-debug.d.ts.map +0 -1
- package/lib/tree/util/extend-trace-debug.js +0 -34
- package/lib/tree/util/extend-trace-debug.js.map +0 -1
- package/lib/tree/util/extend-walk.d.ts +0 -53
- package/lib/tree/util/extend-walk.d.ts.map +0 -1
- package/lib/tree/util/extend-walk.js +0 -881
- package/lib/tree/util/extend-walk.js.map +0 -1
- package/lib/tree/util/extend.d.ts +0 -218
- package/lib/tree/util/extend.d.ts.map +0 -1
- package/lib/tree/util/extend.js +0 -3182
- package/lib/tree/util/extend.js.map +0 -1
- package/lib/tree/util/find-extendable-locations.d.ts +0 -2
- package/lib/tree/util/find-extendable-locations.d.ts.map +0 -1
- package/lib/tree/util/find-extendable-locations.js +0 -2
- package/lib/tree/util/find-extendable-locations.js.map +0 -1
- package/lib/tree/util/format.d.ts +0 -20
- package/lib/tree/util/format.d.ts.map +0 -1
- package/lib/tree/util/format.js +0 -67
- package/lib/tree/util/format.js.map +0 -1
- package/lib/tree/util/is-node.d.ts +0 -13
- package/lib/tree/util/is-node.d.ts.map +0 -1
- package/lib/tree/util/is-node.js +0 -43
- package/lib/tree/util/is-node.js.map +0 -1
- package/lib/tree/util/print.d.ts +0 -80
- package/lib/tree/util/print.d.ts.map +0 -1
- package/lib/tree/util/print.js +0 -205
- package/lib/tree/util/print.js.map +0 -1
- package/lib/tree/util/process-leading-is.d.ts +0 -25
- package/lib/tree/util/process-leading-is.d.ts.map +0 -1
- package/lib/tree/util/process-leading-is.js +0 -364
- package/lib/tree/util/process-leading-is.js.map +0 -1
- package/lib/tree/util/recursion-helper.d.ts +0 -15
- package/lib/tree/util/recursion-helper.d.ts.map +0 -1
- package/lib/tree/util/recursion-helper.js +0 -43
- package/lib/tree/util/recursion-helper.js.map +0 -1
- package/lib/tree/util/regex.d.ts +0 -4
- package/lib/tree/util/regex.d.ts.map +0 -1
- package/lib/tree/util/regex.js +0 -4
- package/lib/tree/util/regex.js.map +0 -1
- package/lib/tree/util/registry-utils.d.ts +0 -192
- package/lib/tree/util/registry-utils.d.ts.map +0 -1
- package/lib/tree/util/registry-utils.js +0 -1214
- package/lib/tree/util/registry-utils.js.map +0 -1
- package/lib/tree/util/ruleset-trace.d.ts +0 -4
- package/lib/tree/util/ruleset-trace.d.ts.map +0 -1
- package/lib/tree/util/ruleset-trace.js +0 -14
- package/lib/tree/util/ruleset-trace.js.map +0 -1
- package/lib/tree/util/selector-compare.d.ts +0 -2
- package/lib/tree/util/selector-compare.d.ts.map +0 -1
- package/lib/tree/util/selector-compare.js +0 -2
- package/lib/tree/util/selector-compare.js.map +0 -1
- package/lib/tree/util/selector-match-core.d.ts +0 -184
- package/lib/tree/util/selector-match-core.d.ts.map +0 -1
- package/lib/tree/util/selector-match-core.js +0 -1603
- package/lib/tree/util/selector-match-core.js.map +0 -1
- package/lib/tree/util/selector-utils.d.ts +0 -30
- package/lib/tree/util/selector-utils.d.ts.map +0 -1
- package/lib/tree/util/selector-utils.js +0 -100
- package/lib/tree/util/selector-utils.js.map +0 -1
- package/lib/tree/util/serialize-helper.d.ts +0 -13
- package/lib/tree/util/serialize-helper.d.ts.map +0 -1
- package/lib/tree/util/serialize-helper.js +0 -387
- package/lib/tree/util/serialize-helper.js.map +0 -1
- package/lib/tree/util/serialize-types.d.ts +0 -9
- package/lib/tree/util/serialize-types.d.ts.map +0 -1
- package/lib/tree/util/serialize-types.js +0 -216
- package/lib/tree/util/serialize-types.js.map +0 -1
- package/lib/tree/util/should-operate.d.ts +0 -23
- package/lib/tree/util/should-operate.d.ts.map +0 -1
- package/lib/tree/util/should-operate.js +0 -46
- package/lib/tree/util/should-operate.js.map +0 -1
- package/lib/tree/util/sourcemap.d.ts +0 -7
- package/lib/tree/util/sourcemap.d.ts.map +0 -1
- package/lib/tree/util/sourcemap.js +0 -25
- package/lib/tree/util/sourcemap.js.map +0 -1
- package/lib/types/config.d.ts +0 -205
- package/lib/types/config.d.ts.map +0 -1
- package/lib/types/config.js +0 -2
- package/lib/types/config.js.map +0 -1
- package/lib/types/index.d.ts +0 -15
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/index.js +0 -3
- package/lib/types/index.js.map +0 -1
- package/lib/types/modes.d.ts.map +0 -1
- package/lib/types/modes.js +0 -2
- package/lib/types/modes.js.map +0 -1
- package/lib/types.d.ts +0 -61
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/lib/use-webpack-resolver.d.ts +0 -9
- package/lib/use-webpack-resolver.d.ts.map +0 -1
- package/lib/use-webpack-resolver.js +0 -41
- package/lib/use-webpack-resolver.js.map +0 -1
- package/lib/visitor/index.d.ts +0 -136
- package/lib/visitor/index.d.ts.map +0 -1
- package/lib/visitor/index.js +0 -135
- package/lib/visitor/index.js.map +0 -1
- package/lib/visitor/less-visitor.d.ts +0 -7
- package/lib/visitor/less-visitor.d.ts.map +0 -1
- 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
|
+
});
|