@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,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;
|