@jesscss/core 2.0.0-alpha.4 → 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 -94
- package/lib/tree/ampersand.d.ts.map +0 -1
- package/lib/tree/ampersand.js +0 -269
- 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,2139 @@
|
|
|
1
|
+
import type { Selector } from '../selector.js';
|
|
2
|
+
import type { Context } from '../../context.js';
|
|
3
|
+
import { SelectorList } from '../selector-list.js';
|
|
4
|
+
import { CompoundSelector } from '../selector-compound.js';
|
|
5
|
+
import { ComplexSelector } from '../selector-complex.js';
|
|
6
|
+
import type { ComplexSelectorValue } from '../selector-complex.js';
|
|
7
|
+
import { PseudoSelector } from '../selector-pseudo.js';
|
|
8
|
+
import { Combinator } from '../combinator.js';
|
|
9
|
+
import { isNode } from './is-node.js';
|
|
10
|
+
import { N } from '../node-type.js';
|
|
11
|
+
import { getImplicitSelector, getParentReplacementForAmpersand, wrapParentSelectorForNestedContext } from './selector-utils.js';
|
|
12
|
+
import { selectorMatch } from './selector-match-core.js';
|
|
13
|
+
import { Node } from '../node.js';
|
|
14
|
+
import { CANONICAL, EVAL, F_AMPERSAND } from '../node-base.js';
|
|
15
|
+
import { addParentEdge } from './cursor.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @todo Once extend correctness is stabilized and the remaining suites are
|
|
19
|
+
* green, do a dedicated performance review of this file. Focus on:
|
|
20
|
+
* - repeated nested `selectorMatch()` calls
|
|
21
|
+
* - unnecessary selector copying or wrapper allocation
|
|
22
|
+
* - opportunities to thread match metadata instead of recomputing it
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Extend failure categories surfaced to callers while the rewrite remains
|
|
27
|
+
* intentionally incremental.
|
|
28
|
+
*/
|
|
29
|
+
export type ExtendErrorType =
|
|
30
|
+
'NOT_FOUND'
|
|
31
|
+
| 'ELEMENT_CONFLICT'
|
|
32
|
+
| 'ID_CONFLICT'
|
|
33
|
+
| 'AMPERSAND_BOUNDARY'
|
|
34
|
+
| 'PARTIAL_MATCH';
|
|
35
|
+
|
|
36
|
+
export const ExtendErrorType = {
|
|
37
|
+
NOT_FOUND: 'NOT_FOUND' as const,
|
|
38
|
+
ELEMENT_CONFLICT: 'ELEMENT_CONFLICT' as const,
|
|
39
|
+
ID_CONFLICT: 'ID_CONFLICT' as const,
|
|
40
|
+
AMPERSAND_BOUNDARY: 'AMPERSAND_BOUNDARY' as const,
|
|
41
|
+
PARTIAL_MATCH: 'PARTIAL_MATCH' as const
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Structured extend failure used instead of throwing during selector rewrites.
|
|
46
|
+
*/
|
|
47
|
+
export class ExtendError extends Error {
|
|
48
|
+
constructor(
|
|
49
|
+
public type: ExtendErrorType,
|
|
50
|
+
message: string
|
|
51
|
+
) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = 'ExtendError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Result of trying to extend a selector.
|
|
59
|
+
*
|
|
60
|
+
* `value` is always returned so callers can keep using the original selector on
|
|
61
|
+
* misses without additional branching.
|
|
62
|
+
*/
|
|
63
|
+
export interface ExtendResult {
|
|
64
|
+
value: Selector;
|
|
65
|
+
error?: ExtendError;
|
|
66
|
+
isChanged: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Creates a successful extend result around the mutated or rewritten selector. */
|
|
70
|
+
function createSuccessResult(value: Selector, isChanged = true): ExtendResult {
|
|
71
|
+
return { value, isChanged };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Creates a failed extend result while preserving the original selector. */
|
|
75
|
+
function createErrorResult(value: Selector, type: ExtendErrorType, message: string): ExtendResult {
|
|
76
|
+
return {
|
|
77
|
+
value,
|
|
78
|
+
error: new ExtendError(type, message),
|
|
79
|
+
isChanged: false
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function expandGeneratedIsAlternatives(selector: Selector): Selector[] {
|
|
84
|
+
if (
|
|
85
|
+
isNode(selector, N.PseudoSelector)
|
|
86
|
+
&& selector.generated === true
|
|
87
|
+
&& selector.get('name') === ':is'
|
|
88
|
+
&& getSelectorListArg(selector)
|
|
89
|
+
) {
|
|
90
|
+
return [...getSelectorListArg(selector)!.get('value')];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return [selector];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getDerivedSelectorRenderKey(source: Selector): symbol | number {
|
|
97
|
+
return source.renderKey === CANONICAL ? EVAL : source.renderKey;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function finalizeDerivedSelector<T extends Selector>(
|
|
101
|
+
source: Selector,
|
|
102
|
+
nextNode: T,
|
|
103
|
+
reusedChildren: readonly unknown[]
|
|
104
|
+
): T {
|
|
105
|
+
const priorParents = reusedChildren.flatMap(child =>
|
|
106
|
+
child instanceof Node ? [[child, child.parent] as const] : []
|
|
107
|
+
);
|
|
108
|
+
nextNode.inherit(source);
|
|
109
|
+
nextNode.renderKey = getDerivedSelectorRenderKey(source);
|
|
110
|
+
for (const [child, priorParent] of priorParents) {
|
|
111
|
+
addParentEdge(child, nextNode.renderKey, nextNode);
|
|
112
|
+
Reflect.set(child, 'parent', priorParent);
|
|
113
|
+
}
|
|
114
|
+
return nextNode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createDerivedValueContainer(source: SelectorList, value: Selector[]): SelectorList;
|
|
118
|
+
function createDerivedValueContainer(source: ComplexSelector, value: ComplexSelectorValue): ComplexSelector;
|
|
119
|
+
function createDerivedValueContainer(source: CompoundSelector, value: Selector[]): CompoundSelector;
|
|
120
|
+
function createDerivedValueContainer(
|
|
121
|
+
source: SelectorList | ComplexSelector | CompoundSelector,
|
|
122
|
+
value: Selector[] | ComplexSelectorValue
|
|
123
|
+
): SelectorList | ComplexSelector | CompoundSelector {
|
|
124
|
+
const nextNode = isNode(source, N.SelectorList)
|
|
125
|
+
? new SelectorList(value, source.options ? { ...source.options } : undefined, source.location, source.treeContext)
|
|
126
|
+
: isNode(source, N.ComplexSelector)
|
|
127
|
+
? new ComplexSelector(value, source.options ? { ...source.options } : undefined, source.location, source.treeContext)
|
|
128
|
+
: new CompoundSelector(value, source.options ? { ...source.options } : undefined, source.location, source.treeContext);
|
|
129
|
+
return finalizeDerivedSelector(source, nextNode, value);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function cloneComplexSelectorComponent(component: ComplexSelectorValue[number]): ComplexSelectorValue[number] {
|
|
133
|
+
const cloned: ComplexSelectorValue[number] = component instanceof Node
|
|
134
|
+
? component.copy(true)
|
|
135
|
+
: component;
|
|
136
|
+
return cloned;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function createDerivedSelectorListFromSource(
|
|
140
|
+
source: Selector,
|
|
141
|
+
value: Selector[]
|
|
142
|
+
): SelectorList {
|
|
143
|
+
const nextNode = new SelectorList(
|
|
144
|
+
value,
|
|
145
|
+
source.options ? { ...source.options } : undefined,
|
|
146
|
+
source.location,
|
|
147
|
+
source.treeContext
|
|
148
|
+
);
|
|
149
|
+
return finalizeDerivedSelector(source, nextNode, value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function createDerivedPseudoWithArg(
|
|
153
|
+
source: PseudoSelector,
|
|
154
|
+
arg: Selector
|
|
155
|
+
): PseudoSelector {
|
|
156
|
+
const nextNode = PseudoSelector.create(
|
|
157
|
+
{ name: source.name, arg },
|
|
158
|
+
source.options ? { ...source.options } : undefined,
|
|
159
|
+
source.location,
|
|
160
|
+
source.treeContext
|
|
161
|
+
);
|
|
162
|
+
nextNode.generated = source.generated;
|
|
163
|
+
return finalizeDerivedSelector(source, nextNode, [arg]);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getSelectorChildren(target: Selector): readonly Selector[] | undefined {
|
|
167
|
+
if (isNode(target, N.SelectorList | N.CompoundSelector | N.ComplexSelector)) {
|
|
168
|
+
return target.get('value');
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getSelectorArg(target: Selector): Selector | undefined {
|
|
174
|
+
if (!isNode(target, N.PseudoSelector)) {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
const arg = target.get('arg');
|
|
178
|
+
return isNode(arg, N.Selector) ? arg : undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getSelectorListArg(target: Selector): SelectorList | undefined {
|
|
182
|
+
if (!isNode(target, N.PseudoSelector)) {
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
const arg = target.get('arg');
|
|
186
|
+
return isNode(arg, N.SelectorList) ? arg : undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function getCompoundSelector(node: Selector): CompoundSelector | undefined {
|
|
190
|
+
return isNode(node, N.CompoundSelector) ? node : undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function getComplexSelector(node: Selector): ComplexSelector | undefined {
|
|
194
|
+
return isNode(node, N.ComplexSelector) ? node : undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function getSelectorList(node: Selector): SelectorList | undefined {
|
|
198
|
+
return isNode(node, N.SelectorList) ? node : undefined;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function normalizeSelectorListAlternatives(list: readonly Selector[]): Selector[] {
|
|
202
|
+
const flattened: Selector[] = [];
|
|
203
|
+
const seen = new Set<string>();
|
|
204
|
+
|
|
205
|
+
for (const item of list) {
|
|
206
|
+
const itemArgList = isNode(item, N.PseudoSelector) ? getSelectorListArg(item) : undefined;
|
|
207
|
+
if (isNode(item, N.PseudoSelector) && item.get('name') === ':is' && itemArgList) {
|
|
208
|
+
for (const child of itemArgList.get('value')) {
|
|
209
|
+
const key = child.valueOf();
|
|
210
|
+
if (!seen.has(key)) {
|
|
211
|
+
seen.add(key);
|
|
212
|
+
flattened.push(child);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const compound = getCompoundSelector(item);
|
|
219
|
+
if (compound && compound.get('value').length === 1) {
|
|
220
|
+
const only = compound.get('value')[0]!;
|
|
221
|
+
const onlyArgList = isNode(only, N.PseudoSelector) ? getSelectorListArg(only) : undefined;
|
|
222
|
+
if (isNode(only, N.PseudoSelector) && only.get('name') === ':is' && onlyArgList) {
|
|
223
|
+
for (const child of onlyArgList.get('value')) {
|
|
224
|
+
const key = child.valueOf();
|
|
225
|
+
if (!seen.has(key)) {
|
|
226
|
+
seen.add(key);
|
|
227
|
+
flattened.push(child);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const complex = getComplexSelector(item);
|
|
235
|
+
if (complex && complex.get('value').length === 1) {
|
|
236
|
+
const only = complex.get('value')[0]!;
|
|
237
|
+
const onlyArgList = isNode(only, N.PseudoSelector) ? getSelectorListArg(only) : undefined;
|
|
238
|
+
if (isNode(only, N.PseudoSelector) && only.get('name') === ':is' && onlyArgList) {
|
|
239
|
+
for (const child of onlyArgList.get('value')) {
|
|
240
|
+
const key = child.valueOf();
|
|
241
|
+
if (!seen.has(key)) {
|
|
242
|
+
seen.add(key);
|
|
243
|
+
flattened.push(child);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const key = item.valueOf();
|
|
251
|
+
if (seen.has(key)) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
seen.add(key);
|
|
255
|
+
flattened.push(item);
|
|
256
|
+
}
|
|
257
|
+
return flattened;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function canMutateSelectorContainer(target: Selector): boolean {
|
|
261
|
+
return target.renderKey !== CANONICAL || target.sourceNode !== target;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function setPseudoArg(target: PseudoSelector, arg: Selector): PseudoSelector {
|
|
265
|
+
if (!canMutateSelectorContainer(target)) {
|
|
266
|
+
return createDerivedPseudoWithArg(target, arg);
|
|
267
|
+
}
|
|
268
|
+
target.adopt(arg);
|
|
269
|
+
target.arg = arg;
|
|
270
|
+
target.invalidateCache();
|
|
271
|
+
return target;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function setSelectorContainerValue(
|
|
275
|
+
target: SelectorList | ComplexSelector | CompoundSelector,
|
|
276
|
+
value: Selector[]
|
|
277
|
+
): SelectorList | ComplexSelector | CompoundSelector {
|
|
278
|
+
if (!canMutateSelectorContainer(target)) {
|
|
279
|
+
return createDerivedValueContainer(target, value);
|
|
280
|
+
}
|
|
281
|
+
Reflect.set(target, 'value', value);
|
|
282
|
+
for (const item of value) {
|
|
283
|
+
target.adopt(item);
|
|
284
|
+
}
|
|
285
|
+
target.invalidateCache();
|
|
286
|
+
return target;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function setSelectorContainerValueAt(
|
|
290
|
+
target: SelectorList | ComplexSelector | CompoundSelector,
|
|
291
|
+
index: number,
|
|
292
|
+
replacement: Selector
|
|
293
|
+
): SelectorList | ComplexSelector | CompoundSelector {
|
|
294
|
+
const nextValue = target.get('value').slice();
|
|
295
|
+
nextValue[index] = replacement;
|
|
296
|
+
return setSelectorContainerValue(target, nextValue);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Appends a new alternative to an existing alternate container by returning
|
|
301
|
+
* a derived container that reuses unchanged child selectors.
|
|
302
|
+
*
|
|
303
|
+
* This is used for exact matches on selector lists and sole `:is(...)`
|
|
304
|
+
* containers, where the correct extend output is to add another alternative
|
|
305
|
+
* rather than wrap the whole selector again.
|
|
306
|
+
*/
|
|
307
|
+
function appendAlternative(
|
|
308
|
+
target: Selector,
|
|
309
|
+
extendWith: Selector
|
|
310
|
+
): Selector | undefined {
|
|
311
|
+
const next = expandGeneratedIsAlternatives(extendWith);
|
|
312
|
+
|
|
313
|
+
if (isNode(target, N.SelectorList)) {
|
|
314
|
+
const normalized = normalizeSelectorListAlternatives(target.get('value'));
|
|
315
|
+
const nextItems = normalizeSelectorListAlternatives([...normalized, ...next]);
|
|
316
|
+
return createDerivedValueContainer(target, nextItems);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (isNode(target, N.PseudoSelector) && target.get('name') === ':is' && getSelectorArg(target)) {
|
|
320
|
+
const arg = getSelectorArg(target)!;
|
|
321
|
+
if (getSelectorList(arg)) {
|
|
322
|
+
const normalized = normalizeSelectorListAlternatives(arg.get('value'));
|
|
323
|
+
const nextItems = normalizeSelectorListAlternatives([...normalized, ...next]);
|
|
324
|
+
return createDerivedPseudoWithArg(target, createDerivedValueContainer(arg, nextItems));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const list = createDerivedSelectorListFromSource(arg, normalizeSelectorListAlternatives([arg, ...next]));
|
|
328
|
+
return createDerivedPseudoWithArg(target, list);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Produces the exact-extend output for one fully matched selector.
|
|
336
|
+
*
|
|
337
|
+
* When the matched selector already is an alternate container, the new
|
|
338
|
+
* selector is appended in place. Otherwise a selector list is created around
|
|
339
|
+
* the matched selector and the extension alternative.
|
|
340
|
+
*
|
|
341
|
+
* This is the preferred output for any selector that has an exact full match
|
|
342
|
+
* route, including full-match results discovered while running in partial
|
|
343
|
+
* mode. A generated
|
|
344
|
+
* `:is(...)` wrapper should only be introduced when the rewrite genuinely has
|
|
345
|
+
* to preserve unmatched siblings around a partial match.
|
|
346
|
+
*/
|
|
347
|
+
function createAlternativeSelector(
|
|
348
|
+
target: Selector,
|
|
349
|
+
extendWith: Selector
|
|
350
|
+
): Selector {
|
|
351
|
+
if (target.valueOf() === extendWith.valueOf()) {
|
|
352
|
+
return target;
|
|
353
|
+
}
|
|
354
|
+
const appended = appendAlternative(target, extendWith);
|
|
355
|
+
if (appended) {
|
|
356
|
+
return appended;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return createDerivedSelectorListFromSource(target, [target, ...expandGeneratedIsAlternatives(extendWith)]);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Wraps one matched selector fragment in a generated `:is(original, extendWith)`
|
|
364
|
+
* pseudo so partial extends can preserve unmatched siblings around it.
|
|
365
|
+
*
|
|
366
|
+
* When `selector` and `extendWith` are identical (e.g. `.class:extend(.class all)`),
|
|
367
|
+
* no wrapper is needed — the fragment is marked visible in place and returned as-is.
|
|
368
|
+
*/
|
|
369
|
+
function wrapSelectorInIs(selector: Selector, extendWith: Selector): Selector {
|
|
370
|
+
// Flatten: if selector is already a generated :is(SelectorList), extract its items
|
|
371
|
+
// instead of nesting :is(:is(...), extension).
|
|
372
|
+
const selectorItems = expandGeneratedIsAlternatives(selector);
|
|
373
|
+
const extendItems = expandGeneratedIsAlternatives(extendWith);
|
|
374
|
+
const allItems = normalizeSelectorListAlternatives([...selectorItems, ...extendItems]);
|
|
375
|
+
|
|
376
|
+
if (allItems.length === 1) {
|
|
377
|
+
return selector;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const arg = SelectorList.create(allItems).inherit(selector);
|
|
381
|
+
arg.pre = undefined;
|
|
382
|
+
arg.post = undefined;
|
|
383
|
+
|
|
384
|
+
const wrapper = PseudoSelector.create({
|
|
385
|
+
name: ':is',
|
|
386
|
+
arg
|
|
387
|
+
});
|
|
388
|
+
wrapper.generated = true;
|
|
389
|
+
const inherited = wrapper.inherit(selector);
|
|
390
|
+
inherited.pre = undefined;
|
|
391
|
+
inherited.post = undefined;
|
|
392
|
+
return inherited;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Wraps a selector in a generated `:is(...)` without adding another alternate. */
|
|
396
|
+
function wrapSelectorAsGeneratedIs(selector: Selector): Selector {
|
|
397
|
+
const wrapper = PseudoSelector.create({
|
|
398
|
+
name: ':is',
|
|
399
|
+
arg: selector
|
|
400
|
+
});
|
|
401
|
+
wrapper.generated = true;
|
|
402
|
+
return wrapper.inherit(selector);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
type CompoundConflictInfo = {
|
|
406
|
+
elements: Set<string>;
|
|
407
|
+
ids: Set<string>;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
function createCompoundConflictInfo(): CompoundConflictInfo {
|
|
411
|
+
return {
|
|
412
|
+
elements: new Set<string>(),
|
|
413
|
+
ids: new Set<string>()
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Collects the element and id selectors that can participate in one merged
|
|
419
|
+
* compound position.
|
|
420
|
+
*
|
|
421
|
+
* This mirrors extend's validity rule, not selector serialization. Complex
|
|
422
|
+
* selectors only contribute their terminal position because that is the part
|
|
423
|
+
* that can merge with the surrounding compound. Selector lists and `:is(...)`
|
|
424
|
+
* contribute the union of their alternatives.
|
|
425
|
+
*/
|
|
426
|
+
function collectCompoundConflictInfo(
|
|
427
|
+
selector: Selector,
|
|
428
|
+
info: CompoundConflictInfo = createCompoundConflictInfo()
|
|
429
|
+
): CompoundConflictInfo {
|
|
430
|
+
if (isNode(selector, N.BasicSelector)) {
|
|
431
|
+
if (selector.isTag) {
|
|
432
|
+
info.elements.add(selector.valueOf());
|
|
433
|
+
} else if (selector.isId) {
|
|
434
|
+
info.ids.add(selector.valueOf());
|
|
435
|
+
}
|
|
436
|
+
return info;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (isNode(selector, N.CompoundSelector | N.SelectorList)) {
|
|
440
|
+
for (const child of selector.get('value')) {
|
|
441
|
+
collectCompoundConflictInfo(child, info);
|
|
442
|
+
}
|
|
443
|
+
return info;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (isNode(selector, N.ComplexSelector)) {
|
|
447
|
+
const complexData = selector.get('value');
|
|
448
|
+
for (let i = complexData.length - 1; i >= 0; i--) {
|
|
449
|
+
const child = complexData[i]!;
|
|
450
|
+
if (isNode(child, N.Combinator)) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
collectCompoundConflictInfo(child, info);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
return info;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (isNode(selector, N.PseudoSelector) && getSelectorArg(selector) && selector.get('name') === ':is') {
|
|
460
|
+
collectCompoundConflictInfo(getSelectorArg(selector)!, info);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return info;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Validates that inserting `extendWith` into the remaining members of one
|
|
468
|
+
* compound position would not create an impossible selector such as
|
|
469
|
+
* `span:is(div, .foo)` or `#first:is(#second, .foo)`.
|
|
470
|
+
*/
|
|
471
|
+
function getCompoundConflictError(
|
|
472
|
+
surroundingMembers: readonly Selector[],
|
|
473
|
+
extendWith: Selector
|
|
474
|
+
): ExtendError | undefined {
|
|
475
|
+
const surrounding = createCompoundConflictInfo();
|
|
476
|
+
for (const member of surroundingMembers) {
|
|
477
|
+
collectCompoundConflictInfo(member, surrounding);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const extension = collectCompoundConflictInfo(extendWith);
|
|
481
|
+
const surroundingElement = surrounding.elements.size > 0 ? [...surrounding.elements][0]! : undefined;
|
|
482
|
+
const conflictingElement = [...extension.elements].find(element => surroundingElement && element !== surroundingElement);
|
|
483
|
+
if (conflictingElement) {
|
|
484
|
+
return new ExtendError(ExtendErrorType.ELEMENT_CONFLICT, 'Extend would introduce a conflicting element selector');
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const surroundingId = surrounding.ids.size > 0 ? [...surrounding.ids][0]! : undefined;
|
|
488
|
+
const conflictingId = [...extension.ids].find(id => surroundingId && id !== surroundingId);
|
|
489
|
+
if (conflictingId) {
|
|
490
|
+
return new ExtendError(ExtendErrorType.ID_CONFLICT, 'Extend would introduce a conflicting id selector');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return undefined;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Removes element and id selectors from `selector` when they are already
|
|
498
|
+
* supplied by the surrounding compound context outside the generated `:is(...)`
|
|
499
|
+
* wrapper.
|
|
500
|
+
*
|
|
501
|
+
* This keeps rewrites like `div:is(.a, div.b)` normalized to `div:is(.a, .b)`
|
|
502
|
+
* and `#foo:is(.class, #foo.other)` normalized to `#foo:is(.class, .other)`.
|
|
503
|
+
*/
|
|
504
|
+
function stripRedundantCompoundContext(
|
|
505
|
+
selector: Selector,
|
|
506
|
+
surroundingMembers: readonly Selector[]
|
|
507
|
+
): Selector {
|
|
508
|
+
const surrounding = createCompoundConflictInfo();
|
|
509
|
+
for (const member of surroundingMembers) {
|
|
510
|
+
collectCompoundConflictInfo(member, surrounding);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const normalize = (node: Selector): Selector | undefined => {
|
|
514
|
+
if (isNode(node, N.BasicSelector)) {
|
|
515
|
+
if (node.isTag && surrounding.elements.has(node.valueOf())) {
|
|
516
|
+
return undefined;
|
|
517
|
+
}
|
|
518
|
+
if (node.isId && surrounding.ids.has(node.valueOf())) {
|
|
519
|
+
return undefined;
|
|
520
|
+
}
|
|
521
|
+
return node;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const compound = getCompoundSelector(node);
|
|
525
|
+
if (compound) {
|
|
526
|
+
const next = compound.get('value')
|
|
527
|
+
.map(child => normalize(child))
|
|
528
|
+
.filter((child): child is Selector => !!child);
|
|
529
|
+
if (next.length === 0) {
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
if (next.length === 1) {
|
|
533
|
+
return next[0]!;
|
|
534
|
+
}
|
|
535
|
+
return CompoundSelector.create(next).inherit(node);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const list = getSelectorList(node);
|
|
539
|
+
if (list) {
|
|
540
|
+
const next = list.get('value')
|
|
541
|
+
.map(child => normalize(child))
|
|
542
|
+
.filter((child): child is Selector => !!child);
|
|
543
|
+
if (next.length === 0) {
|
|
544
|
+
return node;
|
|
545
|
+
}
|
|
546
|
+
if (next.length === 1) {
|
|
547
|
+
return next[0]!;
|
|
548
|
+
}
|
|
549
|
+
return SelectorList.create(next).inherit(node);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (isNode(node, N.PseudoSelector) && node.get('name') === ':is' && getSelectorArg(node)) {
|
|
553
|
+
const nextArg = normalize(getSelectorArg(node)!);
|
|
554
|
+
if (!nextArg) {
|
|
555
|
+
return undefined;
|
|
556
|
+
}
|
|
557
|
+
const copy = node.copy(true);
|
|
558
|
+
return setPseudoArg(copy, nextArg);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return node;
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
return normalize(selector) ?? selector;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Rewrites a compound-local partial match by replacing only the consumed
|
|
569
|
+
* members with a generated `:is(...)` wrapper and leaving the unmatched
|
|
570
|
+
* remainder in place.
|
|
571
|
+
*/
|
|
572
|
+
function wrapCompoundMatchRange(
|
|
573
|
+
targetCompound: CompoundSelector,
|
|
574
|
+
startIndex: number,
|
|
575
|
+
endIndex: number,
|
|
576
|
+
matchedIndices: number[] | undefined,
|
|
577
|
+
extendWith: Selector
|
|
578
|
+
): Selector {
|
|
579
|
+
const effectiveMatchedIndices = matchedIndices && matchedIndices.length > 0
|
|
580
|
+
? matchedIndices
|
|
581
|
+
: Array.from({ length: endIndex - startIndex + 1 }, (_, offset) => startIndex + offset);
|
|
582
|
+
const outsideMembers = getCompoundMembersOutsideRange(targetCompound, startIndex, endIndex, matchedIndices);
|
|
583
|
+
const matched = effectiveMatchedIndices.map(index => targetCompound.value[index]!);
|
|
584
|
+
const matchedSingle = matched.length === 1 ? matched[0]! : undefined;
|
|
585
|
+
const wrapped = wrapSelectorInIs(
|
|
586
|
+
matchedSingle ?? CompoundSelector.create(matched).inherit(targetCompound),
|
|
587
|
+
stripRedundantCompoundContext(extendWith, outsideMembers)
|
|
588
|
+
);
|
|
589
|
+
if (matchedSingle && wrapped === matchedSingle) {
|
|
590
|
+
return targetCompound;
|
|
591
|
+
}
|
|
592
|
+
const nextData: Selector[] = [];
|
|
593
|
+
const matchedIndexSet = new Set(effectiveMatchedIndices);
|
|
594
|
+
|
|
595
|
+
for (let i = 0; i < targetCompound.value.length; i++) {
|
|
596
|
+
const node = targetCompound.value[i]!;
|
|
597
|
+
if (i === effectiveMatchedIndices[0]) {
|
|
598
|
+
nextData.push(wrapped);
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (matchedIndexSet.has(i)) {
|
|
602
|
+
continue;
|
|
603
|
+
}
|
|
604
|
+
nextData.push(node);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (nextData.length === 1) {
|
|
608
|
+
return nextData[0]!;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return ComplexSelector.create(nextData).inherit(targetCompound);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function getCompoundMembersOutsideRange(
|
|
615
|
+
compoundSelector: CompoundSelector,
|
|
616
|
+
startIndex: number,
|
|
617
|
+
endIndex: number,
|
|
618
|
+
matchedIndices: number[] | undefined
|
|
619
|
+
): Selector[] {
|
|
620
|
+
const effectiveMatchedIndices = matchedIndices && matchedIndices.length > 0
|
|
621
|
+
? matchedIndices
|
|
622
|
+
: Array.from({ length: endIndex - startIndex + 1 }, (_, offset) => startIndex + offset);
|
|
623
|
+
const matchedIndexSet = new Set(effectiveMatchedIndices);
|
|
624
|
+
|
|
625
|
+
return compoundSelector.value.filter((_, index) => !matchedIndexSet.has(index));
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function wrapOrderedMatchRange(
|
|
629
|
+
targetSelector: ComplexSelector,
|
|
630
|
+
startIndex: number,
|
|
631
|
+
endIndex: number,
|
|
632
|
+
extendWith: Selector
|
|
633
|
+
): Selector {
|
|
634
|
+
const matched = targetSelector.value.slice(startIndex, endIndex + 1);
|
|
635
|
+
const wrapped = wrapSelectorInIs(
|
|
636
|
+
matched.length === 1
|
|
637
|
+
? matched[0]!
|
|
638
|
+
: ComplexSelector.create(matched).inherit(targetSelector),
|
|
639
|
+
extendWith
|
|
640
|
+
);
|
|
641
|
+
const nextData: Selector[] = [];
|
|
642
|
+
|
|
643
|
+
for (let i = 0; i < targetSelector.value.length; i++) {
|
|
644
|
+
const node = targetSelector.value[i]!;
|
|
645
|
+
if (i === startIndex) {
|
|
646
|
+
nextData.push(wrapped);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
if (i > startIndex && i <= endIndex) {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
nextData.push(node);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (nextData.length === 1) {
|
|
656
|
+
return nextData[0]!;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return ComplexSelector.create(nextData).inherit(targetSelector);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Replaces one direct child selector inside its parent container.
|
|
664
|
+
*
|
|
665
|
+
* This keeps mutation localized to the owning parent so downstream caches and
|
|
666
|
+
* parent links stay consistent with the node model.
|
|
667
|
+
*/
|
|
668
|
+
function replaceDirectSelectorChild(
|
|
669
|
+
parent: Selector,
|
|
670
|
+
child: Selector,
|
|
671
|
+
replacement: Selector
|
|
672
|
+
): Selector | undefined {
|
|
673
|
+
const parentChildren = getSelectorChildren(parent);
|
|
674
|
+
|
|
675
|
+
if (
|
|
676
|
+
isNode(parent, N.PseudoSelector)
|
|
677
|
+
&& (
|
|
678
|
+
parent.get('arg') === child
|
|
679
|
+
|| (
|
|
680
|
+
getSelectorArg(parent) !== undefined
|
|
681
|
+
&& getSelectorArg(parent)!.type === child.type
|
|
682
|
+
&& getSelectorArg(parent)!.valueOf() === child.valueOf()
|
|
683
|
+
)
|
|
684
|
+
)
|
|
685
|
+
) {
|
|
686
|
+
return setPseudoArg(parent, replacement);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (parentChildren) {
|
|
690
|
+
const index = parentChildren.findIndex(node => node === child);
|
|
691
|
+
const fallbackIndex = index !== -1
|
|
692
|
+
? index
|
|
693
|
+
: parentChildren.findIndex(node => node.type === child.type && node.valueOf() === child.valueOf());
|
|
694
|
+
if (fallbackIndex !== -1 && isNode(parent, N.SelectorList | N.ComplexSelector | N.CompoundSelector)) {
|
|
695
|
+
return setSelectorContainerValueAt(parent, fallbackIndex, replacement);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function getStructurallyMatchingDirectChildSelector(
|
|
703
|
+
target: Selector,
|
|
704
|
+
node: Selector
|
|
705
|
+
): Selector | undefined {
|
|
706
|
+
const arg = getSelectorArg(target);
|
|
707
|
+
if (isNode(target, N.PseudoSelector) && arg) {
|
|
708
|
+
if (arg.type === node.type && arg.valueOf() === node.valueOf()) {
|
|
709
|
+
return arg;
|
|
710
|
+
}
|
|
711
|
+
return undefined;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const children = getSelectorChildren(target);
|
|
715
|
+
if (!children) {
|
|
716
|
+
return undefined;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return children.find(child => child.type === node.type && child.valueOf() === node.valueOf());
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Resolves authored ampersands in a root complex selector against the provided
|
|
724
|
+
* parent without mutating the original target selector.
|
|
725
|
+
*
|
|
726
|
+
* This is currently used only for full-span crossed matches on the root target
|
|
727
|
+
* route.
|
|
728
|
+
*/
|
|
729
|
+
function resolveAmpersandTarget(
|
|
730
|
+
target: ComplexSelector,
|
|
731
|
+
parent?: Selector
|
|
732
|
+
): Selector | undefined {
|
|
733
|
+
const nextData: ComplexSelectorValue = [];
|
|
734
|
+
let replacedAny = false;
|
|
735
|
+
|
|
736
|
+
for (let i = 0; i < target.get('value').length; i++) {
|
|
737
|
+
const component = target.get('value')[i]!;
|
|
738
|
+
if (isNode(component, N.Ampersand)) {
|
|
739
|
+
const resolvedRaw = component.getResolvedSelector() ?? parent;
|
|
740
|
+
if (!resolvedRaw || isNode(resolvedRaw, N.Nil)) {
|
|
741
|
+
nextData.push(cloneComplexSelectorComponent(component));
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
nextData.push(...getParentReplacementForAmpersand(resolvedRaw, i === 0));
|
|
745
|
+
replacedAny = true;
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
nextData.push(cloneComplexSelectorComponent(component));
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (!replacedAny) {
|
|
752
|
+
return undefined;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return ComplexSelector.create(nextData).inherit(target);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Materializes the original matched target side when an extend crossed a parent
|
|
760
|
+
* boundary.
|
|
761
|
+
*
|
|
762
|
+
* Explicit authored ampersands resolve through their own replacement logic.
|
|
763
|
+
* Plain nested selectors with an implicit parent boundary are composed against
|
|
764
|
+
* `parent` on demand so the resulting selector is self-contained once hoisted
|
|
765
|
+
* or appended as an exact alternative.
|
|
766
|
+
*/
|
|
767
|
+
function materializeCrossedTarget(
|
|
768
|
+
target: Selector,
|
|
769
|
+
parent: Selector | undefined
|
|
770
|
+
): Selector | undefined {
|
|
771
|
+
const resolvedAmpersands = isNode(target, N.ComplexSelector)
|
|
772
|
+
? resolveAmpersandTarget(target, parent)
|
|
773
|
+
: undefined;
|
|
774
|
+
if (resolvedAmpersands) {
|
|
775
|
+
return resolvedAmpersands;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (!parent) {
|
|
779
|
+
return undefined;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// When both are SelectorLists, produce :is(parent) :is(inner) to avoid
|
|
783
|
+
// duplicating the parent prefix across every alternative.
|
|
784
|
+
if (isNode(target, N.SelectorList) && isNode(parent, N.SelectorList)) {
|
|
785
|
+
const parentFragment = wrapParentSelectorForNestedContext(parent, false);
|
|
786
|
+
const innerIs = PseudoSelector.create({ name: ':is', arg: target.copy(true) });
|
|
787
|
+
innerIs.generated = true;
|
|
788
|
+
return ComplexSelector.create([parentFragment, Combinator.create(' '), innerIs]).inherit(target);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return getImplicitSelector(target, parent, false);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Materializes authored ampersands throughout a hoisted selector so the
|
|
796
|
+
* returned selector no longer depends on external parent context.
|
|
797
|
+
*
|
|
798
|
+
* Once a rewrite hoists to root, every remaining `&` in the returned selector
|
|
799
|
+
* must be replaced with its parent selector using the same wrapping rules as
|
|
800
|
+
* ordinary ampersand replacement.
|
|
801
|
+
*/
|
|
802
|
+
function materializeAmpersandsForHoist(
|
|
803
|
+
selector: Selector,
|
|
804
|
+
parent: Selector
|
|
805
|
+
): Selector {
|
|
806
|
+
if (isNode(selector, N.Ampersand)) {
|
|
807
|
+
const resolved = selector.getResolvedSelector() ?? parent;
|
|
808
|
+
const resolvedCopy = resolved.copy(true);
|
|
809
|
+
return materializeAmpersandsForHoist(resolvedCopy, parent);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const selectorList = getSelectorList(selector);
|
|
813
|
+
if (selectorList) {
|
|
814
|
+
const next = selectorList.get('value').map(child =>
|
|
815
|
+
materializeAmpersandsForHoist(child.copy(true), parent)
|
|
816
|
+
);
|
|
817
|
+
const rebuilt = SelectorList.create(next).inherit(selector);
|
|
818
|
+
rebuilt.hoistToRoot = selector.hoistToRoot;
|
|
819
|
+
return rebuilt;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const complex = getComplexSelector(selector);
|
|
823
|
+
if (complex) {
|
|
824
|
+
const nextData: Selector[] = [];
|
|
825
|
+
|
|
826
|
+
for (let i = 0; i < complex.get('value').length; i++) {
|
|
827
|
+
const child = complex.get('value')[i]!;
|
|
828
|
+
if (isNode(child, N.Ampersand)) {
|
|
829
|
+
const resolved = child.getResolvedSelector() ?? parent;
|
|
830
|
+
const replacement = materializeAmpersandsForHoist(resolved.copy(true), parent);
|
|
831
|
+
nextData.push(...getParentReplacementForAmpersand(replacement, i === 0));
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
nextData.push(materializeAmpersandsForHoist(child.copy(true), parent));
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
const rebuilt = ComplexSelector.create(nextData).inherit(selector);
|
|
839
|
+
rebuilt.hoistToRoot = selector.hoistToRoot;
|
|
840
|
+
return rebuilt;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const compound = getCompoundSelector(selector);
|
|
844
|
+
if (compound) {
|
|
845
|
+
const nextData: Selector[] = [];
|
|
846
|
+
|
|
847
|
+
for (const selectorChild of compound.get('value')) {
|
|
848
|
+
if (isNode(selectorChild, N.Ampersand)) {
|
|
849
|
+
const resolved = materializeAmpersandsForHoist((selectorChild.getResolvedSelector() ?? parent)!.copy(true), parent);
|
|
850
|
+
if (isNode(resolved, N.CompoundSelector)) {
|
|
851
|
+
nextData.push(...resolved.get('value'));
|
|
852
|
+
} else if (isNode(resolved, N.ComplexSelector | N.SelectorList)) {
|
|
853
|
+
nextData.push(wrapSelectorAsGeneratedIs(resolved));
|
|
854
|
+
} else {
|
|
855
|
+
nextData.push(resolved);
|
|
856
|
+
}
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
nextData.push(materializeAmpersandsForHoist(selectorChild.copy(true), parent));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (nextData.length === 1) {
|
|
864
|
+
const only = nextData[0]!;
|
|
865
|
+
only.hoistToRoot = selector.hoistToRoot;
|
|
866
|
+
return only;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const rebuilt = CompoundSelector.create(nextData).inherit(selector);
|
|
870
|
+
rebuilt.hoistToRoot = selector.hoistToRoot;
|
|
871
|
+
return rebuilt;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (isNode(selector, N.PseudoSelector) && isNode(selector.get('arg'), N.Selector)) {
|
|
875
|
+
const copy = selector.copy(true);
|
|
876
|
+
const nextCopy = setPseudoArg(copy, materializeAmpersandsForHoist(getSelectorArg(selector)!, parent));
|
|
877
|
+
nextCopy.hoistToRoot = selector.hoistToRoot;
|
|
878
|
+
return nextCopy;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const copy = selector.copy(true);
|
|
882
|
+
copy.hoistToRoot = selector.hoistToRoot;
|
|
883
|
+
return copy;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function getCrossedAmpersandParent(location: ReturnType<typeof selectorMatch>['matches'][number]): Selector | undefined {
|
|
887
|
+
const crossing = location.ampersandCrossings?.find(crossing => crossing.parentSegment && isNode(crossing.parentSegment.containingNode, N.Selector));
|
|
888
|
+
return crossing?.parentSegment?.containingNode;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Rewrites a root compound span that crossed an ampersand by resolving the
|
|
893
|
+
* matched span against the parent seam and leaving unmatched compound members
|
|
894
|
+
* outside the generated `:is(...)`.
|
|
895
|
+
*/
|
|
896
|
+
function wrapResolvedCompoundSpan(
|
|
897
|
+
targetCompound: CompoundSelector,
|
|
898
|
+
startIndex: number,
|
|
899
|
+
endIndex: number,
|
|
900
|
+
extendWith: Selector,
|
|
901
|
+
resolvedParent: Selector
|
|
902
|
+
): Selector {
|
|
903
|
+
const matchedMembers = targetCompound.value.slice(startIndex, endIndex + 1);
|
|
904
|
+
const matchedSelector = matchedMembers.length === 1
|
|
905
|
+
? matchedMembers[0]!
|
|
906
|
+
: CompoundSelector.create(matchedMembers).inherit(targetCompound);
|
|
907
|
+
const outsideMembers = targetCompound.value.filter((_: Selector, index: number) => index < startIndex || index > endIndex);
|
|
908
|
+
const wrapped = wrapSelectorInIs(
|
|
909
|
+
materializeAmpersandsForHoist(matchedSelector, resolvedParent),
|
|
910
|
+
stripRedundantCompoundContext(extendWith, outsideMembers)
|
|
911
|
+
);
|
|
912
|
+
const nextData: Selector[] = [];
|
|
913
|
+
|
|
914
|
+
for (let i = 0; i < targetCompound.value.length; i++) {
|
|
915
|
+
if (i === startIndex) {
|
|
916
|
+
nextData.push(wrapped);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (i > startIndex && i <= endIndex) {
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
nextData.push(targetCompound.value[i]!);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (nextData.length === 1) {
|
|
926
|
+
return nextData[0]!;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
return ComplexSelector.create(nextData).inherit(targetCompound);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function getLastOrderedSelector(selector: Selector): Selector {
|
|
933
|
+
const complex = getComplexSelector(selector);
|
|
934
|
+
if (complex) {
|
|
935
|
+
for (let i = complex.get('value').length - 1; i >= 0; i--) {
|
|
936
|
+
const child = complex.get('value')[i]!;
|
|
937
|
+
if (!isNode(child, N.Combinator)) {
|
|
938
|
+
return child;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
return selector;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
function buildMatchedCompoundSelector(
|
|
947
|
+
targetCompound: CompoundSelector,
|
|
948
|
+
startIndex: number,
|
|
949
|
+
endIndex: number,
|
|
950
|
+
matchedIndices?: number[]
|
|
951
|
+
): Selector {
|
|
952
|
+
const effectiveMatchedIndices = matchedIndices && matchedIndices.length > 0
|
|
953
|
+
? matchedIndices
|
|
954
|
+
: Array.from({ length: endIndex - startIndex + 1 }, (_, offset) => startIndex + offset);
|
|
955
|
+
const matched = effectiveMatchedIndices.map(index => targetCompound.value[index]!);
|
|
956
|
+
|
|
957
|
+
if (matched.length === 1) {
|
|
958
|
+
return matched[0]!;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return CompoundSelector.create(matched).inherit(targetCompound);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Rewrites a crossed ordered span whose terminal position is a compound with
|
|
966
|
+
* unmatched tail members that must stay outside the generated `:is(...)`.
|
|
967
|
+
*
|
|
968
|
+
* When the terminal compound crossed an ampersand seam, the matched portion is
|
|
969
|
+
* the full crossed target segment, not just the visible `matchedIndices`.
|
|
970
|
+
* Otherwise the authored `&` would leak into the remainder instead of staying
|
|
971
|
+
* attached to the matched fragment it helped complete.
|
|
972
|
+
*/
|
|
973
|
+
function wrapResolvedOrderedSpanWithTailRemainder(
|
|
974
|
+
targetSelector: ComplexSelector,
|
|
975
|
+
startIndex: number,
|
|
976
|
+
endIndex: number,
|
|
977
|
+
extendWith: Selector,
|
|
978
|
+
resolvedParent: Selector,
|
|
979
|
+
terminalFind: Selector,
|
|
980
|
+
location?: ReturnType<typeof selectorMatch>['matches'][number],
|
|
981
|
+
context?: Context
|
|
982
|
+
): Selector | undefined {
|
|
983
|
+
const tail = targetSelector.value[endIndex]!;
|
|
984
|
+
if (!isNode(tail, N.CompoundSelector)) {
|
|
985
|
+
return undefined;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
const tailMatch = selectorMatch(terminalFind, tail, resolvedParent, context);
|
|
989
|
+
const tailLocation = tailMatch.matches.find(match => match.containingNode === tail);
|
|
990
|
+
if (!tailLocation || tailLocation.startIndex === undefined || tailLocation.endIndex === undefined) {
|
|
991
|
+
return undefined;
|
|
992
|
+
}
|
|
993
|
+
const crossedTailSegment = location?.ampersandCrossings?.find(crossing =>
|
|
994
|
+
crossing.targetSegment.containingNode === tail
|
|
995
|
+
)?.targetSegment ?? tailLocation.ampersandCrossings?.find(crossing =>
|
|
996
|
+
crossing.targetSegment.containingNode === tail
|
|
997
|
+
)?.targetSegment;
|
|
998
|
+
const tailStartIndex = crossedTailSegment?.startIndex ?? tailLocation.startIndex;
|
|
999
|
+
const tailEndIndex = crossedTailSegment?.endIndex ?? tailLocation.endIndex;
|
|
1000
|
+
const tailMatchedIndices = crossedTailSegment?.matchedIndices ?? tailLocation.matchedIndices;
|
|
1001
|
+
const effectiveTailMatchedIndices = crossedTailSegment ? undefined : tailMatchedIndices;
|
|
1002
|
+
|
|
1003
|
+
const matchedPrefix: Selector[] = [];
|
|
1004
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
1005
|
+
matchedPrefix.push(materializeAmpersandsForHoist(targetSelector.value[i]!.copy(true), resolvedParent));
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
const matchedTailSelector = buildMatchedCompoundSelector(
|
|
1009
|
+
tail,
|
|
1010
|
+
tailStartIndex,
|
|
1011
|
+
tailEndIndex,
|
|
1012
|
+
effectiveTailMatchedIndices
|
|
1013
|
+
);
|
|
1014
|
+
matchedPrefix.push(crossedTailSegment
|
|
1015
|
+
? materializeAmpersandsForHoist(matchedTailSelector, resolvedParent)
|
|
1016
|
+
: matchedTailSelector);
|
|
1017
|
+
|
|
1018
|
+
const orderedMatchedSelector = matchedPrefix.length === 1
|
|
1019
|
+
? matchedPrefix[0]!
|
|
1020
|
+
: ComplexSelector.create(matchedPrefix).inherit(targetSelector);
|
|
1021
|
+
const wrapped = wrapSelectorInIs(orderedMatchedSelector, extendWith);
|
|
1022
|
+
const tailRemainder = getCompoundMembersOutsideRange(
|
|
1023
|
+
tail,
|
|
1024
|
+
tailStartIndex,
|
|
1025
|
+
tailEndIndex,
|
|
1026
|
+
effectiveTailMatchedIndices
|
|
1027
|
+
);
|
|
1028
|
+
const inserted = tailRemainder.length > 0
|
|
1029
|
+
? CompoundSelector.create([wrapped, ...tailRemainder]).inherit(tail)
|
|
1030
|
+
: wrapped;
|
|
1031
|
+
|
|
1032
|
+
const nextData: Selector[] = [];
|
|
1033
|
+
for (let i = 0; i < targetSelector.value.length; i++) {
|
|
1034
|
+
if (i === startIndex) {
|
|
1035
|
+
nextData.push(inserted);
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
if (i > startIndex && i <= endIndex) {
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
nextData.push(targetSelector.value[i]!);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
if (nextData.length === 1) {
|
|
1045
|
+
return nextData[0]!;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
return ComplexSelector.create(nextData).inherit(targetSelector);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Rewrites one direct child selector of `target` by delegating to
|
|
1053
|
+
* `tryExtendSelector()` on that child and then replacing it through the parent
|
|
1054
|
+
* container.
|
|
1055
|
+
*
|
|
1056
|
+
* This is a structural rewrite step on an already-selected subtree. When that
|
|
1057
|
+
* child still belongs to the same authored selector route, the same `parent`
|
|
1058
|
+
* context is forwarded so authored ampersands inside the child keep the same
|
|
1059
|
+
* resolution context the outer target matched with.
|
|
1060
|
+
*
|
|
1061
|
+
* The fallback `selectorMatch()` call is only used to recover crossed-seam
|
|
1062
|
+
* hoist information for exact child rewrites that do not otherwise surface it
|
|
1063
|
+
* on the rewritten child node itself.
|
|
1064
|
+
*/
|
|
1065
|
+
function tryExtendDirectChildSelector(
|
|
1066
|
+
target: Selector,
|
|
1067
|
+
child: Selector | undefined,
|
|
1068
|
+
find: Selector,
|
|
1069
|
+
extendWith: Selector,
|
|
1070
|
+
parent?: Selector,
|
|
1071
|
+
crossedAmpersandHint = false,
|
|
1072
|
+
context?: Context
|
|
1073
|
+
): ExtendResult | undefined {
|
|
1074
|
+
if (!child) {
|
|
1075
|
+
return undefined;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
const nested = tryExtendSelector(child, find, extendWith, true, parent, context);
|
|
1079
|
+
if (nested.error) {
|
|
1080
|
+
return undefined;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const nextTarget = replaceDirectSelectorChild(target, child, nested.value);
|
|
1084
|
+
if (!nextTarget) {
|
|
1085
|
+
return undefined;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if (nested.value.hoistToRoot || crossedAmpersandHint) {
|
|
1089
|
+
nextTarget.hoistToRoot = true;
|
|
1090
|
+
} else if (parent && child.hasFlag(F_AMPERSAND)) {
|
|
1091
|
+
const childMatch = selectorMatch(find, child, parent, context);
|
|
1092
|
+
if (childMatch.fullMatch && childMatch.crossesAmpersand) {
|
|
1093
|
+
nextTarget.hoistToRoot = true;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return createSuccessResult(nextTarget, nested.isChanged);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Finds the direct child selector of `target` that structurally owns `node`.
|
|
1101
|
+
*
|
|
1102
|
+
* This lets partial rewrites recurse into the nearest direct child selector
|
|
1103
|
+
* even when the selected match location lives several wrapper levels below it.
|
|
1104
|
+
*/
|
|
1105
|
+
function getContainingDirectChildSelector(
|
|
1106
|
+
target: Selector,
|
|
1107
|
+
node: Node
|
|
1108
|
+
): Selector | undefined {
|
|
1109
|
+
if (!isNode(target, N.SelectorList | N.CompoundSelector | N.ComplexSelector)) {
|
|
1110
|
+
return undefined;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
let current: Node | undefined = node;
|
|
1114
|
+
while (current && current.parent && current.parent !== target) {
|
|
1115
|
+
current = current.parent;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if (current && current.parent === target && isNode(current, N.Selector)) {
|
|
1119
|
+
return current;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
return undefined;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Returns the single direct child selector selected by one exact location on
|
|
1127
|
+
* an ordered or alternate container, when that location maps to exactly one
|
|
1128
|
+
* child slot of the current target.
|
|
1129
|
+
*/
|
|
1130
|
+
function getSingleMatchedDirectChild(
|
|
1131
|
+
target: Selector,
|
|
1132
|
+
location: ReturnType<typeof selectorMatch>['matches'][number]
|
|
1133
|
+
): Selector | undefined {
|
|
1134
|
+
if (
|
|
1135
|
+
!isNode(target, N.SelectorList | N.CompoundSelector | N.ComplexSelector)
|
|
1136
|
+
|| location.containingNode !== target
|
|
1137
|
+
|| location.startIndex === undefined
|
|
1138
|
+
|| location.startIndex !== location.endIndex
|
|
1139
|
+
) {
|
|
1140
|
+
return undefined;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const children = getSelectorChildren(target);
|
|
1144
|
+
return children ? children[location.startIndex] : undefined;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/** Returns the direct selector-valued arg on a pseudo selector, if any. */
|
|
1148
|
+
function getDirectPseudoArg(
|
|
1149
|
+
target: Selector
|
|
1150
|
+
): Selector | undefined {
|
|
1151
|
+
return isNode(target, N.PseudoSelector) && getSelectorArg(target)
|
|
1152
|
+
? getSelectorArg(target)
|
|
1153
|
+
: undefined;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/** Returns the direct selector-valued arg on a `:is(...)` pseudo, if any. */
|
|
1157
|
+
function getDirectIsArg(
|
|
1158
|
+
target: Selector
|
|
1159
|
+
): Selector | undefined {
|
|
1160
|
+
return isNode(target, N.PseudoSelector) && target.get('name') === ':is' && getSelectorArg(target)
|
|
1161
|
+
? getSelectorArg(target)
|
|
1162
|
+
: undefined;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/** Returns the directly rewriteable selector-list container on `target`, if any. */
|
|
1166
|
+
function getDirectSelectorList(
|
|
1167
|
+
target: Selector
|
|
1168
|
+
): Selector | undefined {
|
|
1169
|
+
return getSelectorList(target) ?? getSelectorListArg(target);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* Appends to the direct selector-list container that already owns the matched
|
|
1174
|
+
* selector, preserving surrounding wrapper shape.
|
|
1175
|
+
*/
|
|
1176
|
+
function tryAppendToContainingSelectorList(
|
|
1177
|
+
target: Selector,
|
|
1178
|
+
containingNode: Selector,
|
|
1179
|
+
extendWith: Selector
|
|
1180
|
+
): ExtendResult | undefined {
|
|
1181
|
+
const list = getDirectSelectorList(target);
|
|
1182
|
+
if (!list) {
|
|
1183
|
+
return undefined;
|
|
1184
|
+
}
|
|
1185
|
+
if (containingNode.parent !== list) {
|
|
1186
|
+
return undefined;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
const replacement = appendAlternative(list, extendWith);
|
|
1190
|
+
if (!replacement) {
|
|
1191
|
+
return undefined;
|
|
1192
|
+
}
|
|
1193
|
+
if (list === target) {
|
|
1194
|
+
return createSuccessResult(replacement);
|
|
1195
|
+
}
|
|
1196
|
+
const nextTarget = replaceDirectSelectorChild(target, list, replacement);
|
|
1197
|
+
if (!nextTarget) {
|
|
1198
|
+
return undefined;
|
|
1199
|
+
}
|
|
1200
|
+
return createSuccessResult(nextTarget);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* Appends at the end of the direct selector-list container when `find`
|
|
1205
|
+
* fully matches that container under the same parent context.
|
|
1206
|
+
*/
|
|
1207
|
+
function tryAppendToDirectSelectorListOnFullMatch(
|
|
1208
|
+
target: Selector,
|
|
1209
|
+
find: Selector,
|
|
1210
|
+
extendWith: Selector,
|
|
1211
|
+
parent?: Selector,
|
|
1212
|
+
context?: Context
|
|
1213
|
+
): ExtendResult | undefined {
|
|
1214
|
+
const list = getDirectSelectorList(target);
|
|
1215
|
+
if (!list) {
|
|
1216
|
+
return undefined;
|
|
1217
|
+
}
|
|
1218
|
+
const innerMatch = selectorMatch(find, list, parent, context);
|
|
1219
|
+
if (!innerMatch.fullMatch) {
|
|
1220
|
+
return undefined;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const replacement = appendAlternative(list, extendWith);
|
|
1224
|
+
if (!replacement) {
|
|
1225
|
+
return undefined;
|
|
1226
|
+
}
|
|
1227
|
+
if (list === target) {
|
|
1228
|
+
if (innerMatch.crossesAmpersand) {
|
|
1229
|
+
replacement.hoistToRoot = true;
|
|
1230
|
+
}
|
|
1231
|
+
return createSuccessResult(replacement);
|
|
1232
|
+
}
|
|
1233
|
+
const nextTarget = replaceDirectSelectorChild(target, list, replacement);
|
|
1234
|
+
if (!nextTarget) {
|
|
1235
|
+
return undefined;
|
|
1236
|
+
}
|
|
1237
|
+
if (innerMatch.crossesAmpersand) {
|
|
1238
|
+
nextTarget.hoistToRoot = true;
|
|
1239
|
+
}
|
|
1240
|
+
return createSuccessResult(nextTarget);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function getLastOrderedSelectorIndex(selector: ComplexSelector): number {
|
|
1244
|
+
for (let i = selector.value.length - 1; i >= 0; i--) {
|
|
1245
|
+
if (!isNode(selector.value[i], N.Combinator)) {
|
|
1246
|
+
return i;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return -1;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function mergeCompoundMembersIntoSelector(
|
|
1253
|
+
members: readonly Selector[],
|
|
1254
|
+
selector: Selector
|
|
1255
|
+
): Selector {
|
|
1256
|
+
const merged: Selector[] = [...members];
|
|
1257
|
+
const compound = getCompoundSelector(selector);
|
|
1258
|
+
if (compound) {
|
|
1259
|
+
merged.push(...compound.get('value'));
|
|
1260
|
+
} else {
|
|
1261
|
+
merged.push(selector);
|
|
1262
|
+
}
|
|
1263
|
+
if (merged.length === 1) {
|
|
1264
|
+
return merged[0]!;
|
|
1265
|
+
}
|
|
1266
|
+
return CompoundSelector.create(merged).inherit(selector);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Appends `extendWith` into a nested `:is(...)` when `find` already fully
|
|
1271
|
+
* matches a selector that belongs to the same rewritten component.
|
|
1272
|
+
*
|
|
1273
|
+
* The important distinction here is component identity, not the mere presence
|
|
1274
|
+
* of `:is(...)`: when the full match stays attached to the same selector
|
|
1275
|
+
* component, the rewrite should append into that component's alternate
|
|
1276
|
+
* container instead of widening the outer selector shape. Parent-aware crossed
|
|
1277
|
+
* full matches hoist the owning target so the returned selector is later
|
|
1278
|
+
* materialized against that parent context.
|
|
1279
|
+
*/
|
|
1280
|
+
function tryAppendIntoNestedIsOnFullMatch(
|
|
1281
|
+
target: Selector,
|
|
1282
|
+
find: Selector,
|
|
1283
|
+
extendWith: Selector,
|
|
1284
|
+
parent?: Selector,
|
|
1285
|
+
context?: Context
|
|
1286
|
+
): ExtendResult | undefined {
|
|
1287
|
+
if (!isNode(target, N.CompoundSelector | N.ComplexSelector)) {
|
|
1288
|
+
return undefined;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const targetData = getSelectorChildren(target);
|
|
1292
|
+
if (!targetData) {
|
|
1293
|
+
return undefined;
|
|
1294
|
+
}
|
|
1295
|
+
for (let i = 0; i < targetData.length; i++) {
|
|
1296
|
+
const child = targetData[i];
|
|
1297
|
+
const childArg = getSelectorArg(child);
|
|
1298
|
+
if (!(isNode(child, N.PseudoSelector) && child.get('name') === ':is' && childArg)) {
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
const innerMatch = selectorMatch(find, childArg, parent, context);
|
|
1302
|
+
if (!innerMatch.fullMatch) {
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
const replacement = appendAlternative(child, extendWith);
|
|
1306
|
+
if (!replacement) {
|
|
1307
|
+
continue;
|
|
1308
|
+
}
|
|
1309
|
+
const nextTarget = replaceDirectSelectorChild(target, child, replacement);
|
|
1310
|
+
if (!nextTarget) {
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
if (innerMatch.crossesAmpersand) {
|
|
1314
|
+
nextTarget.hoistToRoot = true;
|
|
1315
|
+
}
|
|
1316
|
+
return createSuccessResult(nextTarget);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
return undefined;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Pulls matched compound members into the terminal selector of a nested
|
|
1324
|
+
* complex branch when those members all refer to the same selector component.
|
|
1325
|
+
*
|
|
1326
|
+
* This is the case where outer compound members and the branch terminal are
|
|
1327
|
+
* interchangeable because they all describe the same element/component. In
|
|
1328
|
+
* that situation the rewrite should stay attached to that component rather
|
|
1329
|
+
* than widen across the entire outer compound span.
|
|
1330
|
+
*/
|
|
1331
|
+
function tryPullCompoundMatchIntoNestedIsBranch(
|
|
1332
|
+
target: CompoundSelector,
|
|
1333
|
+
location: ReturnType<typeof selectorMatch>['matches'][number],
|
|
1334
|
+
find: Selector,
|
|
1335
|
+
extendWith: Selector,
|
|
1336
|
+
context?: Context
|
|
1337
|
+
): ExtendResult | undefined {
|
|
1338
|
+
if (!(location.startIndex !== undefined && location.endIndex !== undefined)) {
|
|
1339
|
+
return undefined;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const targetValues = target.get('value');
|
|
1343
|
+
const pseudoIndex = targetValues.findIndex((node, index) =>
|
|
1344
|
+
index >= location.startIndex!
|
|
1345
|
+
&& index <= location.endIndex!
|
|
1346
|
+
&& isNode(node, N.PseudoSelector)
|
|
1347
|
+
&& node.get('name') === ':is'
|
|
1348
|
+
&& getSelectorArg(node)
|
|
1349
|
+
);
|
|
1350
|
+
if (pseudoIndex === -1) {
|
|
1351
|
+
return undefined;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
const pseudoNode = targetValues[pseudoIndex];
|
|
1355
|
+
if (!isNode(pseudoNode, N.PseudoSelector)) {
|
|
1356
|
+
return undefined;
|
|
1357
|
+
}
|
|
1358
|
+
const pulledMembers = targetValues.filter((node, index) =>
|
|
1359
|
+
index >= location.startIndex!
|
|
1360
|
+
&& index <= location.endIndex!
|
|
1361
|
+
&& index !== pseudoIndex
|
|
1362
|
+
);
|
|
1363
|
+
if (pulledMembers.length === 0) {
|
|
1364
|
+
return undefined;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
const arg = getSelectorArg(pseudoNode) ?? pseudoNode.get('arg');
|
|
1368
|
+
if (!isNode(arg, N.Selector)) {
|
|
1369
|
+
return undefined;
|
|
1370
|
+
}
|
|
1371
|
+
const alternatives = isNode(arg, N.SelectorList)
|
|
1372
|
+
? [...arg.get('value')]
|
|
1373
|
+
: [arg];
|
|
1374
|
+
|
|
1375
|
+
for (let i = 0; i < alternatives.length; i++) {
|
|
1376
|
+
const alternative = alternatives[i]!;
|
|
1377
|
+
if (!isNode(alternative, N.ComplexSelector)) {
|
|
1378
|
+
continue;
|
|
1379
|
+
}
|
|
1380
|
+
const lastIndex = getLastOrderedSelectorIndex(alternative);
|
|
1381
|
+
if (lastIndex === -1) {
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const lastSelector = alternative.get('value')[lastIndex]!;
|
|
1386
|
+
const merged = mergeCompoundMembersIntoSelector(pulledMembers, lastSelector);
|
|
1387
|
+
const mergedMatch = selectorMatch(find, merged, undefined, context);
|
|
1388
|
+
if (!mergedMatch.fullMatch) {
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
const wrapped = wrapSelectorInIs(merged, extendWith);
|
|
1393
|
+
const nextAlternative = setSelectorContainerValueAt(
|
|
1394
|
+
alternative,
|
|
1395
|
+
lastIndex,
|
|
1396
|
+
wrapped
|
|
1397
|
+
);
|
|
1398
|
+
const nextArg = isNode(arg, N.SelectorList)
|
|
1399
|
+
? setSelectorContainerValueAt(arg, i, nextAlternative)
|
|
1400
|
+
: nextAlternative;
|
|
1401
|
+
const nextPseudo = setPseudoArg(pseudoNode, nextArg);
|
|
1402
|
+
|
|
1403
|
+
const nextData = targetValues
|
|
1404
|
+
.flatMap((node, index) => {
|
|
1405
|
+
if (index < location.startIndex! || index > location.endIndex! || index === pseudoIndex) {
|
|
1406
|
+
return [index === pseudoIndex ? nextPseudo : node];
|
|
1407
|
+
}
|
|
1408
|
+
return [];
|
|
1409
|
+
});
|
|
1410
|
+
const nextTarget = setSelectorContainerValue(target, nextData);
|
|
1411
|
+
return createSuccessResult(nextTarget);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
return undefined;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
/** Picks the most useful rewrite location from a selector-match result. */
|
|
1418
|
+
function getPreferredMatchLocation(
|
|
1419
|
+
target: Selector,
|
|
1420
|
+
match: ReturnType<typeof selectorMatch>
|
|
1421
|
+
): ReturnType<typeof selectorMatch>['matches'][number] {
|
|
1422
|
+
return match.matches.find(location => location.exact && location.containingNode === target)
|
|
1423
|
+
?? match.matches.find(location => location.exact)
|
|
1424
|
+
?? match.matches[0]!;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Produces the exact-mode extend output once a full match has already been
|
|
1429
|
+
* established by `selectorMatch()`.
|
|
1430
|
+
*/
|
|
1431
|
+
function createExactExtendResult(
|
|
1432
|
+
target: Selector,
|
|
1433
|
+
extendWith: Selector,
|
|
1434
|
+
parent: Selector | undefined,
|
|
1435
|
+
crossedAmpersand: boolean,
|
|
1436
|
+
finalize: (result: ExtendResult) => ExtendResult
|
|
1437
|
+
): ExtendResult {
|
|
1438
|
+
if (
|
|
1439
|
+
isNode(target, N.SelectorList)
|
|
1440
|
+
|| (isNode(target, N.PseudoSelector) && target.get('name') === ':is' && isNode(target.get('arg'), N.Selector))
|
|
1441
|
+
) {
|
|
1442
|
+
// When the match crossed a parent boundary, materialize before appending.
|
|
1443
|
+
if (crossedAmpersand) {
|
|
1444
|
+
const resolved = materializeCrossedTarget(target, parent);
|
|
1445
|
+
if (resolved) {
|
|
1446
|
+
return finalize(createSuccessResult(createAlternativeSelector(resolved, extendWith)));
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
return finalize(createSuccessResult(createAlternativeSelector(target, extendWith)));
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
const resolved = crossedAmpersand
|
|
1453
|
+
? materializeCrossedTarget(target, parent)
|
|
1454
|
+
: resolveAmpersandTarget(target, parent);
|
|
1455
|
+
if (resolved) {
|
|
1456
|
+
return finalize(createSuccessResult(createAlternativeSelector(resolved, extendWith)));
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
return finalize(createSuccessResult(createAlternativeSelector(target, extendWith)));
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
/**
|
|
1463
|
+
* Handles the fallback path where the top-level target had no partial matches,
|
|
1464
|
+
* but an exact nested append or direct child rewrite may still be valid.
|
|
1465
|
+
*/
|
|
1466
|
+
function tryHandleNoPartialMatch(
|
|
1467
|
+
target: Selector,
|
|
1468
|
+
find: Selector,
|
|
1469
|
+
extendWith: Selector,
|
|
1470
|
+
parent: Selector | undefined,
|
|
1471
|
+
finalize: (result: ExtendResult) => ExtendResult,
|
|
1472
|
+
context?: Context
|
|
1473
|
+
): ExtendResult | undefined {
|
|
1474
|
+
const nestedIsAppend = tryAppendIntoNestedIsOnFullMatch(target, find, extendWith, parent, context);
|
|
1475
|
+
if (nestedIsAppend) {
|
|
1476
|
+
return finalize(nestedIsAppend);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
const nestedDirectChild = getDirectPseudoArg(target)
|
|
1480
|
+
? tryExtendDirectChildSelector(target, getDirectPseudoArg(target), find, extendWith, parent, false, context)
|
|
1481
|
+
: undefined;
|
|
1482
|
+
if (nestedDirectChild) {
|
|
1483
|
+
if (nestedDirectChild.value.hoistToRoot) {
|
|
1484
|
+
target.hoistToRoot = true;
|
|
1485
|
+
}
|
|
1486
|
+
return finalize(nestedDirectChild);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
return undefined;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
function tryHandleMultiDirectChildFullMatches(
|
|
1493
|
+
target: Selector,
|
|
1494
|
+
find: Selector,
|
|
1495
|
+
extendWith: Selector,
|
|
1496
|
+
parent: Selector | undefined,
|
|
1497
|
+
finalize: (result: ExtendResult) => ExtendResult,
|
|
1498
|
+
context?: Context
|
|
1499
|
+
): ExtendResult | undefined {
|
|
1500
|
+
if (!isNode(target, N.SelectorList | N.CompoundSelector | N.ComplexSelector)) {
|
|
1501
|
+
return undefined;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
const matchedChildren: Array<{ index: number; child: Selector }> = [];
|
|
1505
|
+
const targetData = getSelectorChildren(target);
|
|
1506
|
+
if (!targetData) {
|
|
1507
|
+
return undefined;
|
|
1508
|
+
}
|
|
1509
|
+
for (let i = 0; i < targetData.length; i++) {
|
|
1510
|
+
const child = targetData[i];
|
|
1511
|
+
if (!child || !isNode(child, N.Selector)) {
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
if (isNode(child, N.Combinator)) {
|
|
1515
|
+
continue;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
const childMatch = selectorMatch(find, child, parent, context);
|
|
1519
|
+
if (childMatch.crossesAmpersand) {
|
|
1520
|
+
continue;
|
|
1521
|
+
}
|
|
1522
|
+
if (!childMatch.fullMatch && !childMatch.partialMatch) {
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
matchedChildren.push({ index: i, child });
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
if (matchedChildren.length < 2) {
|
|
1529
|
+
return undefined;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
let currentTarget = target;
|
|
1533
|
+
let crossedAmpersand = false;
|
|
1534
|
+
let anyChanged = false;
|
|
1535
|
+
for (const { index, child } of matchedChildren) {
|
|
1536
|
+
const childValueBefore = child.valueOf();
|
|
1537
|
+
let replacement: Selector | undefined;
|
|
1538
|
+
|
|
1539
|
+
if (isNode(target, N.SelectorList)) {
|
|
1540
|
+
const result = tryExtendSelector(child, find, extendWith, true, parent, context);
|
|
1541
|
+
if (!result.error) {
|
|
1542
|
+
replacement = result.value;
|
|
1543
|
+
crossedAmpersand ||= !!result.value.hoistToRoot;
|
|
1544
|
+
}
|
|
1545
|
+
} else if (isNode(target, N.CompoundSelector)) {
|
|
1546
|
+
const outsideMembers = getCompoundMembersOutsideRange(
|
|
1547
|
+
target,
|
|
1548
|
+
index,
|
|
1549
|
+
index,
|
|
1550
|
+
undefined
|
|
1551
|
+
);
|
|
1552
|
+
const conflict = getCompoundConflictError(outsideMembers, extendWith);
|
|
1553
|
+
if (conflict) {
|
|
1554
|
+
return { value: target, error: conflict, isChanged: false };
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
if (isNode(child, N.CompoundSelector | N.ComplexSelector | N.PseudoSelector | N.SelectorList)) {
|
|
1558
|
+
const result = tryExtendSelector(child, find, extendWith, true, parent, context);
|
|
1559
|
+
if (!result.error) {
|
|
1560
|
+
replacement = result.value;
|
|
1561
|
+
crossedAmpersand ||= !!result.value.hoistToRoot;
|
|
1562
|
+
}
|
|
1563
|
+
} else {
|
|
1564
|
+
replacement = wrapSelectorInIs(child, stripRedundantCompoundContext(extendWith, outsideMembers));
|
|
1565
|
+
}
|
|
1566
|
+
} else {
|
|
1567
|
+
if (isNode(child, N.CompoundSelector | N.ComplexSelector | N.PseudoSelector | N.SelectorList)) {
|
|
1568
|
+
const result = tryExtendSelector(child, find, extendWith, true, parent, context);
|
|
1569
|
+
if (!result.error) {
|
|
1570
|
+
replacement = result.value;
|
|
1571
|
+
crossedAmpersand ||= !!result.value.hoistToRoot;
|
|
1572
|
+
}
|
|
1573
|
+
} else {
|
|
1574
|
+
replacement = wrapSelectorInIs(child, extendWith);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
if (!replacement) {
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
if (replacement !== child || replacement.valueOf() !== childValueBefore) {
|
|
1582
|
+
anyChanged = true;
|
|
1583
|
+
}
|
|
1584
|
+
currentTarget = setSelectorContainerValueAt(currentTarget, index, replacement);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
if (crossedAmpersand) {
|
|
1588
|
+
currentTarget.hoistToRoot = true;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
return finalize(createSuccessResult(currentTarget, anyChanged));
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Attempts to extend `target` using `find` and `extendWith`.
|
|
1596
|
+
*
|
|
1597
|
+
* Exact mode adds a new alternative only when `selectorMatch()` reports a full
|
|
1598
|
+
* match. Partial mode wraps only genuinely partial matches; if partial mode
|
|
1599
|
+
* finds an exact route, it also adds a new alternative instead of generating a
|
|
1600
|
+
* redundant `:is(...)` wrapper.
|
|
1601
|
+
*
|
|
1602
|
+
* The deciding question is whether the successful match stays attached to the
|
|
1603
|
+
* same selector component or crosses a component boundary. When the match
|
|
1604
|
+
* remains on the same component, the rewrite should append to that component's
|
|
1605
|
+
* alternate container. When it crosses a component boundary, the rewrite may
|
|
1606
|
+
* need a generated `:is(...)` wrapper to preserve the unmatched surrounding
|
|
1607
|
+
* structure.
|
|
1608
|
+
*
|
|
1609
|
+
* Put differently: before introducing a generated `:is(...)`, first ask
|
|
1610
|
+
* whether `selectorMatch()` found an exact full match route for the selector
|
|
1611
|
+
* component being rewritten. If it did, the correct shape is a selector list
|
|
1612
|
+
* or an append into an existing alternate container.
|
|
1613
|
+
*
|
|
1614
|
+
* When `parent` is provided, it is passed directly into `selectorMatch()` as a
|
|
1615
|
+
* non-mutating implicit ampersand context. This allows extend callers to search
|
|
1616
|
+
* authored selectors against their resolved parent context without first
|
|
1617
|
+
* materializing implicit ampersand wrappers into the target tree.
|
|
1618
|
+
*
|
|
1619
|
+
* Nested `tryExtendSelector()` calls in this file are still structural rewrite
|
|
1620
|
+
* helpers on already-selected child selectors. They may reuse the same
|
|
1621
|
+
* `parent` when that child is part of the same authored selector route and
|
|
1622
|
+
* therefore must preserve the same ampersand resolution context, but they do
|
|
1623
|
+
* not widen the search into unrelated outer contexts.
|
|
1624
|
+
*
|
|
1625
|
+
* This implementation is intentionally being rebuilt in tiny slices on top of
|
|
1626
|
+
* `selectorMatch()`. Unsupported rewrite shapes currently return `NOT_FOUND`
|
|
1627
|
+
* rather than falling back to legacy extend behavior.
|
|
1628
|
+
*/
|
|
1629
|
+
export function tryExtendSelector(
|
|
1630
|
+
target: Selector,
|
|
1631
|
+
find: Selector,
|
|
1632
|
+
extendWith: Selector,
|
|
1633
|
+
partial: boolean,
|
|
1634
|
+
parent?: Selector,
|
|
1635
|
+
context?: Context
|
|
1636
|
+
): ExtendResult {
|
|
1637
|
+
const finalize = (result: ExtendResult): ExtendResult => {
|
|
1638
|
+
if (!result.error && parent && result.value.hoistToRoot) {
|
|
1639
|
+
return createSuccessResult(materializeAmpersandsForHoist(result.value, parent));
|
|
1640
|
+
}
|
|
1641
|
+
return result;
|
|
1642
|
+
};
|
|
1643
|
+
|
|
1644
|
+
const match = selectorMatch(find, target, parent, context);
|
|
1645
|
+
if (!partial) {
|
|
1646
|
+
if (!match.fullMatch) {
|
|
1647
|
+
return createErrorResult(target, ExtendErrorType.NOT_FOUND, 'Selector not found');
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
return createExactExtendResult(target, extendWith, parent, match.crossesAmpersand, finalize);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
if (!match.partialMatch || match.matches.length === 0) {
|
|
1654
|
+
const noPartialMatchResult = tryHandleNoPartialMatch(target, find, extendWith, parent, finalize, context);
|
|
1655
|
+
if (noPartialMatchResult) {
|
|
1656
|
+
return noPartialMatchResult;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
return createErrorResult(target, ExtendErrorType.NOT_FOUND, 'Selector not found');
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
const earlyDirectListAppend = tryAppendToDirectSelectorListOnFullMatch(target, find, extendWith, parent, context);
|
|
1663
|
+
if (earlyDirectListAppend) {
|
|
1664
|
+
return finalize(earlyDirectListAppend);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
const multiDirectChildFullMatch = tryHandleMultiDirectChildFullMatches(
|
|
1668
|
+
target,
|
|
1669
|
+
find,
|
|
1670
|
+
extendWith,
|
|
1671
|
+
parent,
|
|
1672
|
+
finalize,
|
|
1673
|
+
context
|
|
1674
|
+
);
|
|
1675
|
+
if (multiDirectChildFullMatch) {
|
|
1676
|
+
return multiDirectChildFullMatch;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
const location = getPreferredMatchLocation(target, match);
|
|
1680
|
+
const crossedAmpersand = !!(location.crossesAmpersand || match.crossesAmpersand);
|
|
1681
|
+
const markTargetHoist = (extra = false): void => {
|
|
1682
|
+
if (crossedAmpersand || extra) {
|
|
1683
|
+
target.hoistToRoot = true;
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
const finishNested = (
|
|
1687
|
+
result: ExtendResult | undefined,
|
|
1688
|
+
extraHoist = false
|
|
1689
|
+
): ExtendResult | undefined => {
|
|
1690
|
+
if (!result) {
|
|
1691
|
+
return undefined;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
markTargetHoist(extraHoist || !!result.value.hoistToRoot);
|
|
1695
|
+
return finalize(result);
|
|
1696
|
+
};
|
|
1697
|
+
const finishRootReplacement = (
|
|
1698
|
+
replacement: Selector,
|
|
1699
|
+
preserveRootKinds: number
|
|
1700
|
+
): ExtendResult => {
|
|
1701
|
+
if (replacement === target) {
|
|
1702
|
+
return finalize(createSuccessResult(target, false));
|
|
1703
|
+
}
|
|
1704
|
+
if (isNode(replacement, preserveRootKinds)) {
|
|
1705
|
+
const rootTarget = getSelectorChildren(target) ? target : undefined;
|
|
1706
|
+
if (!rootTarget) {
|
|
1707
|
+
return finalize(createSuccessResult(replacement));
|
|
1708
|
+
}
|
|
1709
|
+
const nextTarget = setSelectorContainerValue(
|
|
1710
|
+
rootTarget,
|
|
1711
|
+
[...getSelectorChildren(replacement)!]
|
|
1712
|
+
);
|
|
1713
|
+
if (crossedAmpersand) {
|
|
1714
|
+
nextTarget.hoistToRoot = true;
|
|
1715
|
+
}
|
|
1716
|
+
return finalize(createSuccessResult(nextTarget));
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
if (crossedAmpersand) {
|
|
1720
|
+
replacement.hoistToRoot = true;
|
|
1721
|
+
}
|
|
1722
|
+
return finalize(createSuccessResult(replacement));
|
|
1723
|
+
};
|
|
1724
|
+
const finishStructuralChildRewrite = (
|
|
1725
|
+
child: Selector | undefined
|
|
1726
|
+
): ExtendResult | undefined => {
|
|
1727
|
+
if (!child) {
|
|
1728
|
+
return undefined;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
const nested = tryExtendSelector(child, find, extendWith, true, parent, context);
|
|
1732
|
+
const nextTarget = nested.error ? undefined : replaceDirectSelectorChild(target, child, nested.value);
|
|
1733
|
+
if (!nextTarget) {
|
|
1734
|
+
return undefined;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
if (crossedAmpersand || nested.value.hoistToRoot) {
|
|
1738
|
+
nextTarget.hoistToRoot = true;
|
|
1739
|
+
}
|
|
1740
|
+
return finalize(createSuccessResult(nextTarget, nested.isChanged));
|
|
1741
|
+
};
|
|
1742
|
+
const tryHandleRootSingleSlotPartial = (): ExtendResult | undefined => {
|
|
1743
|
+
const rootIsOrdered = isNode(target, N.ComplexSelector) || isNode(target, N.CompoundSelector);
|
|
1744
|
+
const targetChildren = getSelectorChildren(target);
|
|
1745
|
+
const orderedTarget = getComplexSelector(target) ?? getCompoundSelector(target);
|
|
1746
|
+
if (
|
|
1747
|
+
!rootIsOrdered
|
|
1748
|
+
|| location.containingNode !== target
|
|
1749
|
+
|| location.startIndex === undefined
|
|
1750
|
+
|| location.endIndex === undefined
|
|
1751
|
+
|| location.startIndex !== location.endIndex
|
|
1752
|
+
|| (
|
|
1753
|
+
targetChildren !== undefined
|
|
1754
|
+
&& isNode(targetChildren[location.startIndex]!, N.CompoundSelector)
|
|
1755
|
+
&& location.matchedIndices
|
|
1756
|
+
&& location.matchedIndices.length > 0
|
|
1757
|
+
)
|
|
1758
|
+
) {
|
|
1759
|
+
return undefined;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
const compoundTarget = getCompoundSelector(target);
|
|
1763
|
+
const compoundOutsideMembers = compoundTarget
|
|
1764
|
+
? getCompoundMembersOutsideRange(
|
|
1765
|
+
compoundTarget,
|
|
1766
|
+
location.startIndex,
|
|
1767
|
+
location.endIndex,
|
|
1768
|
+
location.matchedIndices
|
|
1769
|
+
)
|
|
1770
|
+
: undefined;
|
|
1771
|
+
|
|
1772
|
+
if (compoundTarget) {
|
|
1773
|
+
const conflict = getCompoundConflictError(compoundOutsideMembers!, extendWith);
|
|
1774
|
+
if (conflict) {
|
|
1775
|
+
return { value: target, error: conflict, isChanged: false };
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
const existing = targetChildren![location.startIndex]!;
|
|
1780
|
+
const nestedIsListAppend = tryAppendToDirectSelectorListOnFullMatch(existing, find, extendWith, parent, context);
|
|
1781
|
+
if (nestedIsListAppend) {
|
|
1782
|
+
const nextTarget = setSelectorContainerValueAt(
|
|
1783
|
+
orderedTarget!,
|
|
1784
|
+
location.startIndex,
|
|
1785
|
+
nestedIsListAppend.value
|
|
1786
|
+
);
|
|
1787
|
+
if (crossedAmpersand || nestedIsListAppend.value.hoistToRoot) {
|
|
1788
|
+
nextTarget.hoistToRoot = true;
|
|
1789
|
+
}
|
|
1790
|
+
return finalize(createSuccessResult(nextTarget));
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
if (
|
|
1794
|
+
isNode(existing, N.PseudoSelector | N.SelectorList | N.CompoundSelector | N.ComplexSelector)
|
|
1795
|
+
&& !isNode(existing, N.CompoundSelector)
|
|
1796
|
+
) {
|
|
1797
|
+
const nested = tryExtendSelector(existing, find, extendWith, true, parent, context);
|
|
1798
|
+
if (!nested.error) {
|
|
1799
|
+
const nextTarget = setSelectorContainerValueAt(
|
|
1800
|
+
orderedTarget!,
|
|
1801
|
+
location.startIndex,
|
|
1802
|
+
nested.value
|
|
1803
|
+
);
|
|
1804
|
+
if (crossedAmpersand || nested.value.hoistToRoot) {
|
|
1805
|
+
nextTarget.hoistToRoot = true;
|
|
1806
|
+
}
|
|
1807
|
+
return finalize(createSuccessResult(nextTarget, nested.isChanged));
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
const wrapped = wrapSelectorInIs(existing, stripRedundantCompoundContext(extendWith, compoundOutsideMembers ?? []));
|
|
1812
|
+
const nextTarget = setSelectorContainerValueAt(
|
|
1813
|
+
orderedTarget!,
|
|
1814
|
+
location.startIndex,
|
|
1815
|
+
wrapped
|
|
1816
|
+
);
|
|
1817
|
+
if (crossedAmpersand) {
|
|
1818
|
+
nextTarget.hoistToRoot = true;
|
|
1819
|
+
}
|
|
1820
|
+
return finalize(createSuccessResult(nextTarget, wrapped !== existing));
|
|
1821
|
+
};
|
|
1822
|
+
const tryHandleRootMultiSlotPartial = (): ExtendResult | undefined => {
|
|
1823
|
+
const compoundTarget = getCompoundSelector(target);
|
|
1824
|
+
if (
|
|
1825
|
+
compoundTarget
|
|
1826
|
+
&& location.containingNode === target
|
|
1827
|
+
&& location.startIndex !== undefined
|
|
1828
|
+
&& location.endIndex !== undefined
|
|
1829
|
+
&& location.startIndex < location.endIndex
|
|
1830
|
+
) {
|
|
1831
|
+
const crossedAmpersandParent = getCrossedAmpersandParent(location);
|
|
1832
|
+
if (crossedAmpersandParent) {
|
|
1833
|
+
const replacement = wrapResolvedCompoundSpan(
|
|
1834
|
+
compoundTarget,
|
|
1835
|
+
location.startIndex,
|
|
1836
|
+
location.endIndex,
|
|
1837
|
+
extendWith,
|
|
1838
|
+
crossedAmpersandParent
|
|
1839
|
+
);
|
|
1840
|
+
return finishRootReplacement(replacement, N.ComplexSelector | N.CompoundSelector);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
const outsideMembers = getCompoundMembersOutsideRange(
|
|
1844
|
+
compoundTarget,
|
|
1845
|
+
location.startIndex,
|
|
1846
|
+
location.endIndex,
|
|
1847
|
+
location.matchedIndices
|
|
1848
|
+
);
|
|
1849
|
+
const conflict = getCompoundConflictError(outsideMembers, extendWith);
|
|
1850
|
+
if (conflict) {
|
|
1851
|
+
return { value: target, error: conflict, isChanged: false };
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
const pulledIntoNestedIs = tryPullCompoundMatchIntoNestedIsBranch(compoundTarget, location, find, extendWith, context);
|
|
1855
|
+
if (pulledIntoNestedIs) {
|
|
1856
|
+
return finalize(pulledIntoNestedIs);
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
const replacement = wrapCompoundMatchRange(
|
|
1860
|
+
compoundTarget,
|
|
1861
|
+
location.startIndex,
|
|
1862
|
+
location.endIndex,
|
|
1863
|
+
location.matchedIndices,
|
|
1864
|
+
extendWith
|
|
1865
|
+
);
|
|
1866
|
+
return finishRootReplacement(replacement, N.ComplexSelector | N.CompoundSelector);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
const complexTarget = getComplexSelector(target);
|
|
1870
|
+
if (
|
|
1871
|
+
complexTarget
|
|
1872
|
+
&& location.containingNode === target
|
|
1873
|
+
&& location.startIndex !== undefined
|
|
1874
|
+
&& location.endIndex !== undefined
|
|
1875
|
+
&& location.startIndex < location.endIndex
|
|
1876
|
+
) {
|
|
1877
|
+
const crossedAmpersandParent = getCrossedAmpersandParent(location)
|
|
1878
|
+
?? (crossedAmpersand ? parent : undefined);
|
|
1879
|
+
if (crossedAmpersandParent) {
|
|
1880
|
+
const replacement = wrapResolvedOrderedSpanWithTailRemainder(
|
|
1881
|
+
complexTarget,
|
|
1882
|
+
location.startIndex,
|
|
1883
|
+
location.endIndex,
|
|
1884
|
+
extendWith,
|
|
1885
|
+
crossedAmpersandParent,
|
|
1886
|
+
getLastOrderedSelector(find),
|
|
1887
|
+
location,
|
|
1888
|
+
context
|
|
1889
|
+
);
|
|
1890
|
+
if (replacement) {
|
|
1891
|
+
return finishRootReplacement(replacement, N.ComplexSelector);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
const replacement = wrapOrderedMatchRange(complexTarget, location.startIndex, location.endIndex, extendWith);
|
|
1896
|
+
return finishRootReplacement(replacement, N.ComplexSelector);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
return undefined;
|
|
1900
|
+
};
|
|
1901
|
+
const tryHandleDirectChildContainingNodePartial = (): ExtendResult | undefined => {
|
|
1902
|
+
const containingNode = location.containingNode;
|
|
1903
|
+
const directChild = isNode(containingNode, N.Selector)
|
|
1904
|
+
? (
|
|
1905
|
+
getContainingDirectChildSelector(target, containingNode)
|
|
1906
|
+
?? getStructurallyMatchingDirectChildSelector(target, containingNode)
|
|
1907
|
+
)
|
|
1908
|
+
: undefined;
|
|
1909
|
+
if (!directChild) {
|
|
1910
|
+
return undefined;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
let replacement: Selector;
|
|
1914
|
+
if (
|
|
1915
|
+
isNode(directChild, N.CompoundSelector)
|
|
1916
|
+
&& location.startIndex !== undefined
|
|
1917
|
+
&& location.endIndex !== undefined
|
|
1918
|
+
) {
|
|
1919
|
+
const crossedAmpersandParent = getCrossedAmpersandParent(location);
|
|
1920
|
+
if (crossedAmpersandParent) {
|
|
1921
|
+
replacement = wrapSelectorInIs(
|
|
1922
|
+
materializeAmpersandsForHoist(directChild, crossedAmpersandParent),
|
|
1923
|
+
extendWith
|
|
1924
|
+
);
|
|
1925
|
+
} else {
|
|
1926
|
+
const pulledIntoNestedIs = tryPullCompoundMatchIntoNestedIsBranch(directChild, location, find, extendWith, context);
|
|
1927
|
+
if (pulledIntoNestedIs) {
|
|
1928
|
+
replacement = pulledIntoNestedIs.value;
|
|
1929
|
+
} else {
|
|
1930
|
+
const conflict = getCompoundConflictError(
|
|
1931
|
+
getCompoundMembersOutsideRange(
|
|
1932
|
+
directChild,
|
|
1933
|
+
location.startIndex,
|
|
1934
|
+
location.endIndex,
|
|
1935
|
+
location.matchedIndices
|
|
1936
|
+
),
|
|
1937
|
+
extendWith
|
|
1938
|
+
);
|
|
1939
|
+
if (conflict) {
|
|
1940
|
+
return { value: target, error: conflict, isChanged: false };
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
replacement = wrapCompoundMatchRange(
|
|
1944
|
+
directChild,
|
|
1945
|
+
location.startIndex,
|
|
1946
|
+
location.endIndex,
|
|
1947
|
+
location.matchedIndices,
|
|
1948
|
+
extendWith
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
} else {
|
|
1953
|
+
const nestedDirectChild = isNode(directChild, N.PseudoSelector | N.SelectorList | N.ComplexSelector)
|
|
1954
|
+
? tryExtendSelector(directChild, find, extendWith, true, parent, context)
|
|
1955
|
+
: undefined;
|
|
1956
|
+
if (nestedDirectChild && !nestedDirectChild.error) {
|
|
1957
|
+
replacement = nestedDirectChild.value;
|
|
1958
|
+
} else {
|
|
1959
|
+
const compoundTarget = getCompoundSelector(target);
|
|
1960
|
+
if (compoundTarget) {
|
|
1961
|
+
const childIndex = compoundTarget.get('value').findIndex(node => node === containingNode);
|
|
1962
|
+
if (childIndex !== -1) {
|
|
1963
|
+
const conflict = getCompoundConflictError(
|
|
1964
|
+
getCompoundMembersOutsideRange(
|
|
1965
|
+
compoundTarget,
|
|
1966
|
+
childIndex,
|
|
1967
|
+
childIndex,
|
|
1968
|
+
undefined
|
|
1969
|
+
),
|
|
1970
|
+
extendWith
|
|
1971
|
+
);
|
|
1972
|
+
if (conflict) {
|
|
1973
|
+
return { value: target, error: conflict, isChanged: false };
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
replacement = wrapSelectorInIs(directChild, extendWith);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
const childChanged = replacement !== directChild;
|
|
1983
|
+
const nextTarget = replaceDirectSelectorChild(target, directChild, replacement);
|
|
1984
|
+
if (nextTarget) {
|
|
1985
|
+
if (crossedAmpersand) {
|
|
1986
|
+
nextTarget.hoistToRoot = true;
|
|
1987
|
+
}
|
|
1988
|
+
return finalize(createSuccessResult(nextTarget, childChanged));
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
return undefined;
|
|
1992
|
+
};
|
|
1993
|
+
const directPseudoArg = getDirectPseudoArg(target);
|
|
1994
|
+
const directIsArg = getDirectIsArg(target);
|
|
1995
|
+
if (location.exact) {
|
|
1996
|
+
const directListAppend = tryAppendToDirectSelectorListOnFullMatch(target, find, extendWith, parent);
|
|
1997
|
+
if (directListAppend) {
|
|
1998
|
+
markTargetHoist(!!directListAppend.value.hoistToRoot);
|
|
1999
|
+
return finalize(directListAppend);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
if (
|
|
2003
|
+
location.containingNode === target
|
|
2004
|
+
&& isNode(target, N.ComplexSelector)
|
|
2005
|
+
) {
|
|
2006
|
+
const resolved = resolveAmpersandTarget(target, parent);
|
|
2007
|
+
if (resolved) {
|
|
2008
|
+
const exactResult = createAlternativeSelector(resolved, extendWith);
|
|
2009
|
+
exactResult.hoistToRoot = true;
|
|
2010
|
+
markTargetHoist(true);
|
|
2011
|
+
return finalize(createSuccessResult(exactResult));
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
if (location.containingNode === target) {
|
|
2016
|
+
const nestedIsResult = directIsArg
|
|
2017
|
+
? tryExtendDirectChildSelector(target, directIsArg, find, extendWith, parent, crossedAmpersand, context)
|
|
2018
|
+
: undefined;
|
|
2019
|
+
const finishedNestedIs = finishNested(nestedIsResult);
|
|
2020
|
+
if (finishedNestedIs) {
|
|
2021
|
+
return finishedNestedIs;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
const nestedPseudoResult = directPseudoArg && directPseudoArg === location.containingNode
|
|
2025
|
+
? tryExtendDirectChildSelector(target, location.containingNode, find, extendWith, parent, crossedAmpersand, context)
|
|
2026
|
+
: undefined;
|
|
2027
|
+
const finishedNestedPseudo = finishNested(nestedPseudoResult);
|
|
2028
|
+
if (finishedNestedPseudo) {
|
|
2029
|
+
return finishedNestedPseudo;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
const nestedListResult = isNode(target, N.SelectorList)
|
|
2033
|
+
? tryExtendDirectChildSelector(target, getSingleMatchedDirectChild(target, location), find, extendWith, parent, crossedAmpersand, context)
|
|
2034
|
+
: undefined;
|
|
2035
|
+
const finishedNestedList = finishNested(nestedListResult);
|
|
2036
|
+
if (finishedNestedList) {
|
|
2037
|
+
return finishedNestedList;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
const nestedOrderedChildResult = (isNode(target, N.ComplexSelector) || isNode(target, N.CompoundSelector))
|
|
2041
|
+
? tryExtendDirectChildSelector(target, getSingleMatchedDirectChild(target, location), find, extendWith, parent, crossedAmpersand, context)
|
|
2042
|
+
: undefined;
|
|
2043
|
+
const finishedNestedOrderedChild = finishNested(nestedOrderedChildResult);
|
|
2044
|
+
if (finishedNestedOrderedChild) {
|
|
2045
|
+
return finishedNestedOrderedChild;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
const exactTarget = crossedAmpersand
|
|
2049
|
+
? materializeCrossedTarget(target, parent) ?? target
|
|
2050
|
+
: target;
|
|
2051
|
+
const exactResult = createAlternativeSelector(exactTarget, extendWith);
|
|
2052
|
+
if (crossedAmpersand) {
|
|
2053
|
+
exactResult.hoistToRoot = true;
|
|
2054
|
+
markTargetHoist(true);
|
|
2055
|
+
}
|
|
2056
|
+
return finalize(createSuccessResult(exactResult));
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
const nestedPseudoListAppend = isNode(location.containingNode, N.Selector)
|
|
2060
|
+
? tryAppendToContainingSelectorList(target, location.containingNode, extendWith)
|
|
2061
|
+
: undefined;
|
|
2062
|
+
const finishedNestedPseudoListAppend = finishNested(nestedPseudoListAppend);
|
|
2063
|
+
if (finishedNestedPseudoListAppend) {
|
|
2064
|
+
return finishedNestedPseudoListAppend;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
if (location.containingNode.parent === target) {
|
|
2068
|
+
if (isNode(target, N.SelectorList)) {
|
|
2069
|
+
const rewrittenListChild = isNode(location.containingNode, N.Selector)
|
|
2070
|
+
? finishStructuralChildRewrite(location.containingNode)
|
|
2071
|
+
: undefined;
|
|
2072
|
+
if (rewrittenListChild) {
|
|
2073
|
+
return rewrittenListChild;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
return finalize(createSuccessResult(createAlternativeSelector(target, extendWith)));
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
const rewrittenChild = isNode(location.containingNode, N.Selector)
|
|
2080
|
+
? finishStructuralChildRewrite(location.containingNode)
|
|
2081
|
+
: undefined;
|
|
2082
|
+
if (rewrittenChild) {
|
|
2083
|
+
return rewrittenChild;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
const nestedPseudoResult = directPseudoArg && directPseudoArg === location.containingNode
|
|
2089
|
+
? tryExtendDirectChildSelector(target, location.containingNode, find, extendWith, parent, crossedAmpersand, context)
|
|
2090
|
+
: undefined;
|
|
2091
|
+
const finishedNestedPseudo = finishNested(nestedPseudoResult);
|
|
2092
|
+
if (finishedNestedPseudo) {
|
|
2093
|
+
return finishedNestedPseudo;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
if (location.containingNode === target) {
|
|
2097
|
+
const nestedIsResult = directIsArg
|
|
2098
|
+
? tryExtendDirectChildSelector(target, directIsArg, find, extendWith, parent, crossedAmpersand, context)
|
|
2099
|
+
: undefined;
|
|
2100
|
+
const finishedNestedIs = finishNested(nestedIsResult);
|
|
2101
|
+
if (finishedNestedIs) {
|
|
2102
|
+
return finishedNestedIs;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
const nestedListResult = isNode(target, N.SelectorList)
|
|
2106
|
+
? tryExtendDirectChildSelector(target, getSingleMatchedDirectChild(target, location), find, extendWith, parent, crossedAmpersand, context)
|
|
2107
|
+
: undefined;
|
|
2108
|
+
const finishedNestedList = finishNested(nestedListResult);
|
|
2109
|
+
if (finishedNestedList) {
|
|
2110
|
+
return finishedNestedList;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
const rootSingleSlotPartial = tryHandleRootSingleSlotPartial();
|
|
2115
|
+
if (rootSingleSlotPartial) {
|
|
2116
|
+
return rootSingleSlotPartial;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
const rootMultiSlotPartial = tryHandleRootMultiSlotPartial();
|
|
2120
|
+
if (rootMultiSlotPartial) {
|
|
2121
|
+
return rootMultiSlotPartial;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
const directChildContainingNodePartial = tryHandleDirectChildContainingNodePartial();
|
|
2125
|
+
if (directChildContainingNodePartial) {
|
|
2126
|
+
return directChildContainingNodePartial;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
const containingChild = getContainingDirectChildSelector(target, location.containingNode);
|
|
2130
|
+
if (containingChild) {
|
|
2131
|
+
const nested = tryExtendDirectChildSelector(target, containingChild, find, extendWith, parent, crossedAmpersand, context);
|
|
2132
|
+
const finishedNested = finishNested(nested);
|
|
2133
|
+
if (finishedNested) {
|
|
2134
|
+
return finishedNested;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
return createErrorResult(target, ExtendErrorType.NOT_FOUND, 'Partial extend shape not implemented yet');
|
|
2139
|
+
}
|