@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,2005 @@
|
|
|
1
|
+
import { Selector } from '../selector.js';
|
|
2
|
+
import { SelectorList } from '../selector-list.js';
|
|
3
|
+
import { F_AMPERSAND, type Node } from '../node.js';
|
|
4
|
+
import type { Context as EvalContext } from '../../context.js';
|
|
5
|
+
import { isNode } from './is-node.js';
|
|
6
|
+
import { N } from '../node-type.js';
|
|
7
|
+
import { isDisjoint, type BitSet } from './bitset.js';
|
|
8
|
+
import { type PseudoSelector } from '../selector-pseudo.js';
|
|
9
|
+
import { type CompoundSelector } from '../selector-compound.js';
|
|
10
|
+
import { type ComplexSelector } from '../selector-complex.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A single located selector match.
|
|
14
|
+
*
|
|
15
|
+
* `startIndex` / `endIndex` are measured in the `containingNode`'s local
|
|
16
|
+
* ordered data when the containing node is ordered.
|
|
17
|
+
*
|
|
18
|
+
* `exact` means the matched route consumed its span end-to-end without extra
|
|
19
|
+
* unmatched basic selectors on that route.
|
|
20
|
+
*
|
|
21
|
+
* `crossesAmpersand` is set on synthetic cross-boundary matches completed
|
|
22
|
+
* through a parent selector context.
|
|
23
|
+
*
|
|
24
|
+
* `consumedTarget` means this location matched everything it could possibly
|
|
25
|
+
* match in its target container. This is distinct from route-level
|
|
26
|
+
* `fullMatch`, which only means one exact match route succeeded.
|
|
27
|
+
*/
|
|
28
|
+
interface SelectorMatchLocation {
|
|
29
|
+
startIndex?: number;
|
|
30
|
+
endIndex?: number;
|
|
31
|
+
matchedIndices?: number[];
|
|
32
|
+
containingNode: Node;
|
|
33
|
+
exact?: boolean;
|
|
34
|
+
crossesAmpersand?: boolean;
|
|
35
|
+
consumedTarget?: boolean;
|
|
36
|
+
ampersandCrossings?: SelectorMatchAmpersandCrossing[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface SelectorMatchSegment {
|
|
40
|
+
containingNode: Node;
|
|
41
|
+
startIndex?: number;
|
|
42
|
+
endIndex?: number;
|
|
43
|
+
matchedIndices?: number[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface SelectorMatchAmpersandCrossing {
|
|
47
|
+
ampersandNode?: Node;
|
|
48
|
+
targetSegment: SelectorMatchSegment;
|
|
49
|
+
parentSegment?: SelectorMatchSegment;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Aggregate selector-match result.
|
|
54
|
+
*
|
|
55
|
+
* `fullMatch` means at least one exact end-to-end route matched.
|
|
56
|
+
* `partialMatch` means at least one match of any kind was found.
|
|
57
|
+
* `crossesAmpersand` means at least one recorded match crossed an ampersand
|
|
58
|
+
* boundary instead of matching entirely on one side of it.
|
|
59
|
+
*/
|
|
60
|
+
interface SelectorMatchState {
|
|
61
|
+
fullMatch: boolean;
|
|
62
|
+
partialMatch: boolean;
|
|
63
|
+
crossesAmpersand: boolean;
|
|
64
|
+
matches: SelectorMatchLocation[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The set of basic selectors that one unordered position can satisfy. */
|
|
68
|
+
type MatchGroupRequirement = {
|
|
69
|
+
basicSelectorIndex: Map<string, number>;
|
|
70
|
+
basicSelectorCounts: number[];
|
|
71
|
+
basicSelectorTotal: number;
|
|
72
|
+
hasComplexBranch: boolean;
|
|
73
|
+
branchTailAmbiguous: boolean;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** One position can satisfy any one of its alternatives, such as `:is(...)`. */
|
|
77
|
+
type MatchGroup = {
|
|
78
|
+
alternatives: MatchGroupRequirement[];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** One ordered unit in a route-level match plan. */
|
|
82
|
+
type MatchPlanUnit =
|
|
83
|
+
| {
|
|
84
|
+
kind: 'group';
|
|
85
|
+
index: number;
|
|
86
|
+
node: Node;
|
|
87
|
+
group: MatchGroup;
|
|
88
|
+
}
|
|
89
|
+
| {
|
|
90
|
+
kind: 'combinator';
|
|
91
|
+
index: number;
|
|
92
|
+
node: Node;
|
|
93
|
+
value: string;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
type RouteMatchPlan = {
|
|
97
|
+
kind: 'route';
|
|
98
|
+
selector: Selector;
|
|
99
|
+
units: MatchPlanUnit[];
|
|
100
|
+
hasAmbiguousBranchTail: boolean;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type SelectorListMatchPlan = {
|
|
104
|
+
kind: 'list';
|
|
105
|
+
selector: SelectorList;
|
|
106
|
+
alternates: MatchPlan[];
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type MatchPlan = RouteMatchPlan | SelectorListMatchPlan;
|
|
110
|
+
|
|
111
|
+
type MatchGroupState = {
|
|
112
|
+
remainingCounts: number[];
|
|
113
|
+
remainingTotal: number;
|
|
114
|
+
exact: boolean;
|
|
115
|
+
matchedOutsideAmpersand: boolean;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
type MatchWindowResult = {
|
|
119
|
+
matched: boolean;
|
|
120
|
+
exact: boolean;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
type GroupMatchCache = WeakMap<Node, WeakMap<MatchGroup, MatchWindowResult>>;
|
|
124
|
+
type SelectorMatchPairCache = WeakMap<Selector, WeakMap<Selector, SelectorMatchState>>;
|
|
125
|
+
type SelectorMatchContext = {
|
|
126
|
+
pairCache: SelectorMatchPairCache;
|
|
127
|
+
evalContext?: EvalContext;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const selectorMatchPlanCache = new WeakMap<Selector, {
|
|
131
|
+
value: string;
|
|
132
|
+
plan: MatchPlan;
|
|
133
|
+
}>();
|
|
134
|
+
|
|
135
|
+
function selectorValueOf(node: Node | Selector, context?: EvalContext): string {
|
|
136
|
+
return String((node as unknown as { valueOf(context?: EvalContext): string }).valueOf(context));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function bitSetValues(bitSet: BitSet<string> | undefined): string[] | undefined {
|
|
140
|
+
return bitSet?._library?.valuesOf(bitSet);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function safeIsDisjoint(
|
|
144
|
+
left: BitSet<string> | undefined,
|
|
145
|
+
right: BitSet<string> | undefined
|
|
146
|
+
): boolean {
|
|
147
|
+
if (!left || !right) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
if (left._library && right._library && left._library === right._library) {
|
|
151
|
+
return isDisjoint(left, right);
|
|
152
|
+
}
|
|
153
|
+
const leftValues = bitSetValues(left);
|
|
154
|
+
const rightValues = bitSetValues(right);
|
|
155
|
+
if (!leftValues || !rightValues) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const rightSet = new Set(rightValues);
|
|
159
|
+
for (const value of leftValues) {
|
|
160
|
+
if (rightSet.has(value)) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Returns true for pseudos whose selector arguments can be searched recursively
|
|
169
|
+
* but cannot be consumed as part of a continuing outer match, unlike `:is()`.
|
|
170
|
+
*/
|
|
171
|
+
function isSearchablePseudoBoundary(node: Node): node is PseudoSelector {
|
|
172
|
+
return (
|
|
173
|
+
isNode(node, N.PseudoSelector)
|
|
174
|
+
&& node.get('name') !== ':is'
|
|
175
|
+
&& isNode(node.get('arg'), N.Selector)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function createRequirement(): MatchGroupRequirement {
|
|
180
|
+
const basicSelectorIndex = new Map<string, number>();
|
|
181
|
+
const basicSelectorCounts: number[] = [];
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
basicSelectorIndex,
|
|
185
|
+
basicSelectorCounts,
|
|
186
|
+
basicSelectorTotal: 0,
|
|
187
|
+
hasComplexBranch: false,
|
|
188
|
+
branchTailAmbiguous: false
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function addRequirementValue(requirement: MatchGroupRequirement, value: string): MatchGroupRequirement {
|
|
193
|
+
const existingIndex = requirement.basicSelectorIndex.get(value);
|
|
194
|
+
if (existingIndex !== undefined) {
|
|
195
|
+
requirement.basicSelectorCounts[existingIndex]!++;
|
|
196
|
+
requirement.basicSelectorTotal++;
|
|
197
|
+
return requirement;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
requirement.basicSelectorIndex.set(value, requirement.basicSelectorCounts.length);
|
|
201
|
+
requirement.basicSelectorCounts.push(1);
|
|
202
|
+
requirement.basicSelectorTotal++;
|
|
203
|
+
return requirement;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function cloneRequirement(requirement: MatchGroupRequirement): MatchGroupRequirement {
|
|
207
|
+
return {
|
|
208
|
+
basicSelectorIndex: new Map(requirement.basicSelectorIndex),
|
|
209
|
+
basicSelectorCounts: [...requirement.basicSelectorCounts],
|
|
210
|
+
basicSelectorTotal: requirement.basicSelectorTotal,
|
|
211
|
+
hasComplexBranch: requirement.hasComplexBranch,
|
|
212
|
+
branchTailAmbiguous: requirement.branchTailAmbiguous
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function mergeRequirements(
|
|
217
|
+
left: MatchGroupRequirement,
|
|
218
|
+
right: MatchGroupRequirement
|
|
219
|
+
): MatchGroupRequirement {
|
|
220
|
+
const merged = cloneRequirement(left);
|
|
221
|
+
for (const [value, index] of right.basicSelectorIndex.entries()) {
|
|
222
|
+
const count = right.basicSelectorCounts[index] ?? 0;
|
|
223
|
+
for (let i = 0; i < count; i++) {
|
|
224
|
+
addRequirementValue(merged, value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
merged.hasComplexBranch ||= right.hasComplexBranch;
|
|
228
|
+
merged.branchTailAmbiguous ||= right.branchTailAmbiguous;
|
|
229
|
+
return merged;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function markComplexBranchRequirements(
|
|
233
|
+
requirements: MatchGroupRequirement[],
|
|
234
|
+
branch: Selector & { value?: readonly Node[] },
|
|
235
|
+
parent?: Selector,
|
|
236
|
+
context?: EvalContext
|
|
237
|
+
): MatchGroupRequirement[] {
|
|
238
|
+
const earlierValues = new Set<string>();
|
|
239
|
+
|
|
240
|
+
if (isNode(branch, N.ComplexSelector)) {
|
|
241
|
+
for (let i = 0; i < (branch as ComplexSelector).get('value').length - 1; i++) {
|
|
242
|
+
const component = (branch as ComplexSelector).get('value')[i]!;
|
|
243
|
+
const nested = buildGroupRequirements(component, parent, context);
|
|
244
|
+
for (let j = 0; j < nested.length; j++) {
|
|
245
|
+
for (const value of nested[j]!.basicSelectorIndex.keys()) {
|
|
246
|
+
earlierValues.add(value);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (let i = 0; i < requirements.length; i++) {
|
|
253
|
+
const requirement = requirements[i]!;
|
|
254
|
+
requirement.hasComplexBranch = true;
|
|
255
|
+
for (const value of requirement.basicSelectorIndex.keys()) {
|
|
256
|
+
if (earlierValues.has(value)) {
|
|
257
|
+
requirement.branchTailAmbiguous = true;
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return requirements;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function buildGroupRequirements(
|
|
267
|
+
node: Node,
|
|
268
|
+
parent?: Selector,
|
|
269
|
+
context?: EvalContext,
|
|
270
|
+
activePath: Set<Node> = new Set()
|
|
271
|
+
): MatchGroupRequirement[] {
|
|
272
|
+
if (activePath.has(node)) {
|
|
273
|
+
return [createRequirement()];
|
|
274
|
+
}
|
|
275
|
+
activePath.add(node);
|
|
276
|
+
|
|
277
|
+
if (isNode(node, N.BasicSelector)) {
|
|
278
|
+
const requirement = createRequirement();
|
|
279
|
+
addRequirementValue(requirement, selectorValueOf(node, context));
|
|
280
|
+
activePath.delete(node);
|
|
281
|
+
return [requirement];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (isNode(node, N.Ampersand)) {
|
|
285
|
+
const resolved = node.getResolvedSelector(context) ?? parent;
|
|
286
|
+
if (resolved && !isNode(resolved, N.Nil)) {
|
|
287
|
+
if (activePath.has(resolved)) {
|
|
288
|
+
activePath.delete(node);
|
|
289
|
+
return [createRequirement()];
|
|
290
|
+
}
|
|
291
|
+
const result = buildGroupRequirements(resolved, parent, context, activePath);
|
|
292
|
+
activePath.delete(node);
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
activePath.delete(node);
|
|
297
|
+
return [createRequirement()];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (isNode(node, N.ComplexSelector)) {
|
|
301
|
+
for (let i = (node as ComplexSelector).get('value').length - 1; i >= 0; i--) {
|
|
302
|
+
const component = (node as ComplexSelector).get('value')[i]!;
|
|
303
|
+
if (!isNode(component, N.Combinator)) {
|
|
304
|
+
return markComplexBranchRequirements(
|
|
305
|
+
buildGroupRequirements(component, parent, context, activePath),
|
|
306
|
+
node as unknown as Selector & { value?: readonly Node[] },
|
|
307
|
+
parent,
|
|
308
|
+
context
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
activePath.delete(node);
|
|
314
|
+
return [createRequirement()];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (isNode(node, N.PseudoSelector) && node.get('name') !== ':is') {
|
|
318
|
+
const requirement = createRequirement();
|
|
319
|
+
addRequirementValue(requirement, selectorValueOf(node, context));
|
|
320
|
+
activePath.delete(node);
|
|
321
|
+
return [requirement];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (isSearchablePseudoBoundary(node)) {
|
|
325
|
+
activePath.delete(node);
|
|
326
|
+
return [createRequirement()];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (isNode(node, N.SelectorList)) {
|
|
330
|
+
const alternatives: MatchGroupRequirement[] = [];
|
|
331
|
+
for (let i = 0; i < (node as SelectorList).get('value').length; i++) {
|
|
332
|
+
const nested = buildGroupRequirements((node as SelectorList).get('value')[i]!, parent, context, activePath);
|
|
333
|
+
for (let j = 0; j < nested.length; j++) {
|
|
334
|
+
alternatives.push(nested[j]!);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
activePath.delete(node);
|
|
338
|
+
return alternatives;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let requirements: MatchGroupRequirement[] = [createRequirement()];
|
|
342
|
+
const children = node.children();
|
|
343
|
+
let child = children.next();
|
|
344
|
+
|
|
345
|
+
while (!child.done) {
|
|
346
|
+
const childRequirements = buildGroupRequirements(child.value, parent, context, activePath);
|
|
347
|
+
const nextRequirements: MatchGroupRequirement[] = [];
|
|
348
|
+
for (let i = 0; i < requirements.length; i++) {
|
|
349
|
+
for (let j = 0; j < childRequirements.length; j++) {
|
|
350
|
+
nextRequirements.push(
|
|
351
|
+
mergeRequirements(requirements[i]!, childRequirements[j]!)
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
requirements = nextRequirements;
|
|
356
|
+
child = children.next();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
activePath.delete(node);
|
|
360
|
+
return requirements;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function buildMatchGroup(node: Node, parent?: Selector, context?: EvalContext): MatchGroup {
|
|
364
|
+
return {
|
|
365
|
+
alternatives: buildGroupRequirements(node, parent, context)
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function buildRouteMatchPlan(selector: Selector, parent?: Selector, context?: EvalContext): RouteMatchPlan {
|
|
370
|
+
if (isNode(selector, N.ComplexSelector)) {
|
|
371
|
+
const units: MatchPlanUnit[] = [];
|
|
372
|
+
let hasAmbiguousBranchTail = false;
|
|
373
|
+
|
|
374
|
+
for (let i = 0; i < (selector as ComplexSelector).get('value').length; i++) {
|
|
375
|
+
const component = (selector as ComplexSelector).get('value')[i]!;
|
|
376
|
+
if (isNode(component, N.Combinator)) {
|
|
377
|
+
units.push({
|
|
378
|
+
kind: 'combinator',
|
|
379
|
+
index: i,
|
|
380
|
+
node: component,
|
|
381
|
+
value: selectorValueOf(component, context)
|
|
382
|
+
});
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const group = buildMatchGroup(
|
|
387
|
+
component,
|
|
388
|
+
isNode(component, N.Ampersand) && i === 0 ? undefined : parent,
|
|
389
|
+
context
|
|
390
|
+
);
|
|
391
|
+
const hasAlternatives = group.alternatives.some(alternate => alternate.basicSelectorTotal > 0);
|
|
392
|
+
if (hasAlternatives) {
|
|
393
|
+
hasAmbiguousBranchTail ||= group.alternatives.some(alternate => alternate.branchTailAmbiguous);
|
|
394
|
+
units.push({
|
|
395
|
+
kind: 'group',
|
|
396
|
+
index: i,
|
|
397
|
+
node: component,
|
|
398
|
+
group
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
kind: 'route',
|
|
405
|
+
selector,
|
|
406
|
+
units,
|
|
407
|
+
hasAmbiguousBranchTail
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (isNode(selector, N.Combinator)) {
|
|
412
|
+
return {
|
|
413
|
+
kind: 'route',
|
|
414
|
+
selector,
|
|
415
|
+
hasAmbiguousBranchTail: false,
|
|
416
|
+
units: [{
|
|
417
|
+
kind: 'combinator',
|
|
418
|
+
index: 0,
|
|
419
|
+
node: selector,
|
|
420
|
+
value: selectorValueOf(selector, context)
|
|
421
|
+
}]
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const group = buildMatchGroup(selector, parent, context);
|
|
426
|
+
return {
|
|
427
|
+
kind: 'route',
|
|
428
|
+
selector,
|
|
429
|
+
hasAmbiguousBranchTail: group.alternatives.some(alternate => alternate.branchTailAmbiguous),
|
|
430
|
+
units: group.alternatives.some(alternate => alternate.basicSelectorTotal > 0)
|
|
431
|
+
? [{
|
|
432
|
+
kind: 'group',
|
|
433
|
+
index: 0,
|
|
434
|
+
node: selector,
|
|
435
|
+
group
|
|
436
|
+
}]
|
|
437
|
+
: []
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function buildMatchPlan(selector: Selector, parent?: Selector, context?: EvalContext): MatchPlan {
|
|
442
|
+
if (isNode(selector, N.SelectorList)) {
|
|
443
|
+
return {
|
|
444
|
+
kind: 'list',
|
|
445
|
+
selector,
|
|
446
|
+
alternates: (selector as SelectorList).get('value').map(item => getSelectorMatchPlan(item, parent, context))
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return buildRouteMatchPlan(selector, parent, context);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function getSelectorMatchPlan(selector: Selector, parent?: Selector, context?: EvalContext): MatchPlan {
|
|
454
|
+
if (parent || context) {
|
|
455
|
+
return buildMatchPlan(selector, parent, context);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const value = selectorValueOf(selector);
|
|
459
|
+
const cached = selectorMatchPlanCache.get(selector);
|
|
460
|
+
if (cached && cached.value === value) {
|
|
461
|
+
return cached.plan;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const plan = buildMatchPlan(selector);
|
|
465
|
+
selectorMatchPlanCache.set(selector, { value, plan });
|
|
466
|
+
return plan;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function cloneGroupStates(states: MatchGroupState[]): MatchGroupState[] {
|
|
470
|
+
const nextStates = new Array<MatchGroupState>(states.length);
|
|
471
|
+
for (let i = 0; i < states.length; i++) {
|
|
472
|
+
const state = states[i]!;
|
|
473
|
+
nextStates[i] = {
|
|
474
|
+
remainingCounts: [...state.remainingCounts],
|
|
475
|
+
remainingTotal: state.remainingTotal,
|
|
476
|
+
exact: state.exact,
|
|
477
|
+
matchedOutsideAmpersand: state.matchedOutsideAmpersand
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
return nextStates;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function allStatesAreTerminalPartial(states: MatchGroupState[]): boolean {
|
|
484
|
+
for (let i = 0; i < states.length; i++) {
|
|
485
|
+
const state = states[i]!;
|
|
486
|
+
if (state.remainingTotal > 0 || state.exact) {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function consumeGroupBasics(
|
|
494
|
+
node: Node,
|
|
495
|
+
group: MatchGroupRequirement,
|
|
496
|
+
states: MatchGroupState[],
|
|
497
|
+
insideAmpersand = false,
|
|
498
|
+
parent?: Selector,
|
|
499
|
+
context?: EvalContext,
|
|
500
|
+
activePath: Set<Node> = new Set()
|
|
501
|
+
): MatchGroupState[] {
|
|
502
|
+
if (states.length === 0) {
|
|
503
|
+
return states;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (activePath.has(node)) {
|
|
507
|
+
return states;
|
|
508
|
+
}
|
|
509
|
+
activePath.add(node);
|
|
510
|
+
|
|
511
|
+
if (isNode(node, N.BasicSelector) || (isNode(node, N.PseudoSelector) && node.get('name') !== ':is')) {
|
|
512
|
+
const idx = group.basicSelectorIndex.get(selectorValueOf(node, context));
|
|
513
|
+
|
|
514
|
+
for (let i = 0; i < states.length; i++) {
|
|
515
|
+
const state = states[i]!;
|
|
516
|
+
|
|
517
|
+
if (state.remainingTotal === 0 && !state.exact) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (idx === undefined || state.remainingCounts[idx] === 0) {
|
|
522
|
+
state.exact = false;
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
state.remainingCounts[idx]!--;
|
|
527
|
+
state.remainingTotal--;
|
|
528
|
+
if (!insideAmpersand) {
|
|
529
|
+
state.matchedOutsideAmpersand = true;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
activePath.delete(node);
|
|
534
|
+
return states;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (isNode(node, N.Ampersand)) {
|
|
538
|
+
const resolved = node.getResolvedSelector(context) ?? parent;
|
|
539
|
+
if (resolved && !isNode(resolved, N.Nil)) {
|
|
540
|
+
if (activePath.has(resolved)) {
|
|
541
|
+
activePath.delete(node);
|
|
542
|
+
return states;
|
|
543
|
+
}
|
|
544
|
+
const result = consumeGroupBasics(resolved, group, states, true, parent, context, activePath);
|
|
545
|
+
activePath.delete(node);
|
|
546
|
+
return result;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
activePath.delete(node);
|
|
550
|
+
return states;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (isSearchablePseudoBoundary(node)) {
|
|
554
|
+
activePath.delete(node);
|
|
555
|
+
return states;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (isNode(node, N.SelectorList)) {
|
|
559
|
+
const alternates = node.children();
|
|
560
|
+
const routes = [alternates.mark()];
|
|
561
|
+
const nextStates: MatchGroupState[] = [];
|
|
562
|
+
|
|
563
|
+
while (routes.length > 0) {
|
|
564
|
+
alternates.restore(routes.pop()!);
|
|
565
|
+
const alternate = alternates.next();
|
|
566
|
+
|
|
567
|
+
if (alternate.done) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
routes.push(alternates.mark());
|
|
572
|
+
|
|
573
|
+
nextStates.push(
|
|
574
|
+
...consumeGroupBasics(
|
|
575
|
+
alternate.value,
|
|
576
|
+
group,
|
|
577
|
+
cloneGroupStates(states),
|
|
578
|
+
insideAmpersand,
|
|
579
|
+
parent,
|
|
580
|
+
context,
|
|
581
|
+
activePath
|
|
582
|
+
)
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
activePath.delete(node);
|
|
587
|
+
return nextStates;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const children = node.children();
|
|
591
|
+
let nextStates = states;
|
|
592
|
+
let child = children.next();
|
|
593
|
+
|
|
594
|
+
while (!child.done && nextStates.length > 0 && !allStatesAreTerminalPartial(nextStates)) {
|
|
595
|
+
nextStates = consumeGroupBasics(child.value, group, nextStates, insideAmpersand, parent, context, activePath);
|
|
596
|
+
child = children.next();
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
activePath.delete(node);
|
|
600
|
+
return nextStates;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** Summarizes a set of consume-states with a single pass. */
|
|
604
|
+
function summarizeStates(states: MatchGroupState[]): MatchWindowResult {
|
|
605
|
+
let matched = false;
|
|
606
|
+
let exact = false;
|
|
607
|
+
|
|
608
|
+
for (let i = 0; i < states.length; i++) {
|
|
609
|
+
const state = states[i]!;
|
|
610
|
+
if (state.remainingTotal !== 0 || !state.matchedOutsideAmpersand) {
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
matched = true;
|
|
615
|
+
if (state.exact) {
|
|
616
|
+
exact = true;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return { matched, exact };
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Matches a single unordered target position against one match group.
|
|
626
|
+
*
|
|
627
|
+
* This is the core "consume basics within a position" operation used by
|
|
628
|
+
* route-level matching.
|
|
629
|
+
*/
|
|
630
|
+
function matchTargetGroup(
|
|
631
|
+
targetGroup: Node,
|
|
632
|
+
findGroup: MatchGroup,
|
|
633
|
+
parent?: Selector,
|
|
634
|
+
allowAmpersandOnlyMatch = false,
|
|
635
|
+
context?: EvalContext
|
|
636
|
+
): MatchWindowResult {
|
|
637
|
+
let matched = false;
|
|
638
|
+
let exact = false;
|
|
639
|
+
|
|
640
|
+
for (let i = 0; i < findGroup.alternatives.length; i++) {
|
|
641
|
+
const requirement = findGroup.alternatives[i]!;
|
|
642
|
+
const states = consumeGroupBasics(targetGroup, requirement, [{
|
|
643
|
+
remainingCounts: [...requirement.basicSelectorCounts],
|
|
644
|
+
remainingTotal: requirement.basicSelectorTotal,
|
|
645
|
+
exact: true,
|
|
646
|
+
matchedOutsideAmpersand: false
|
|
647
|
+
}], false, parent, context);
|
|
648
|
+
const summary = summarizeStates(states);
|
|
649
|
+
if (!summary.matched && allowAmpersandOnlyMatch) {
|
|
650
|
+
for (let j = 0; j < states.length; j++) {
|
|
651
|
+
const state = states[j]!;
|
|
652
|
+
if (state.remainingTotal !== 0) {
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
summary.matched = true;
|
|
657
|
+
if (state.exact) {
|
|
658
|
+
summary.exact = true;
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
matched ||= summary.matched;
|
|
665
|
+
exact ||= summary.exact && !requirement.hasComplexBranch;
|
|
666
|
+
|
|
667
|
+
if (exact) {
|
|
668
|
+
break;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return { matched, exact };
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function matchCompoundWindow(
|
|
676
|
+
targetCompound: Selector & { value: readonly Node[] },
|
|
677
|
+
start: number,
|
|
678
|
+
end: number,
|
|
679
|
+
requirement: MatchGroupRequirement,
|
|
680
|
+
parent?: Selector,
|
|
681
|
+
context?: EvalContext
|
|
682
|
+
): MatchWindowResult {
|
|
683
|
+
let states = [{
|
|
684
|
+
remainingCounts: [...requirement.basicSelectorCounts],
|
|
685
|
+
remainingTotal: requirement.basicSelectorTotal,
|
|
686
|
+
exact: true,
|
|
687
|
+
matchedOutsideAmpersand: false
|
|
688
|
+
}];
|
|
689
|
+
|
|
690
|
+
for (let i = start; i <= end && states.length > 0 && !allStatesAreTerminalPartial(states); i++) {
|
|
691
|
+
states = consumeGroupBasics(targetCompound.value[i]!, requirement, states, false, parent, context);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const summary = summarizeStates(states);
|
|
695
|
+
if (!summary.matched && parent) {
|
|
696
|
+
let allAmpersands = true;
|
|
697
|
+
for (let i = start; i <= end; i++) {
|
|
698
|
+
if (!isNode(targetCompound.value[i]!, N.Ampersand)) {
|
|
699
|
+
allAmpersands = false;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (allAmpersands) {
|
|
705
|
+
for (let i = 0; i < states.length; i++) {
|
|
706
|
+
const state = states[i]!;
|
|
707
|
+
if (state.remainingTotal !== 0) {
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
summary.matched = true;
|
|
711
|
+
if (state.exact) {
|
|
712
|
+
summary.exact = true;
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (requirement.hasComplexBranch) {
|
|
719
|
+
summary.exact = false;
|
|
720
|
+
}
|
|
721
|
+
return summary;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function collectMatchedIndicesForWindow(
|
|
725
|
+
targetCompound: Selector & { value: readonly Node[] },
|
|
726
|
+
start: number,
|
|
727
|
+
end: number,
|
|
728
|
+
requirement: MatchGroupRequirement,
|
|
729
|
+
context?: EvalContext
|
|
730
|
+
): number[] | undefined {
|
|
731
|
+
const remainingCounts = [...requirement.basicSelectorCounts];
|
|
732
|
+
const matchedIndices: number[] = [];
|
|
733
|
+
|
|
734
|
+
for (let i = start; i <= end; i++) {
|
|
735
|
+
const node = targetCompound.value[i]!;
|
|
736
|
+
if (!isNode(node, N.BasicSelector) && !(isNode(node, N.PseudoSelector) && node.get('name') !== ':is')) {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const idx = requirement.basicSelectorIndex.get(selectorValueOf(node, context));
|
|
741
|
+
if (idx === undefined || remainingCounts[idx] === 0) {
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
remainingCounts[idx]!--;
|
|
746
|
+
matchedIndices.push(i);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (matchedIndices.length === 0) {
|
|
750
|
+
return undefined;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const spanLength = end - start + 1;
|
|
754
|
+
if (matchedIndices.length === spanLength) {
|
|
755
|
+
return undefined;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return matchedIndices;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Collects every group-local span match for a target node.
|
|
763
|
+
*
|
|
764
|
+
* For compounds this scans contiguous windows so repeated matches in the same
|
|
765
|
+
* compound are reported independently.
|
|
766
|
+
*
|
|
767
|
+
* @todo This is still the hottest path in selector matching. It currently
|
|
768
|
+
* scans O(n^2) spans and may re-run `matchCompoundWindow()` for the same
|
|
769
|
+
* requirement to prove minimality (`withoutStartMatches` / `withoutEndMatches`).
|
|
770
|
+
* If this becomes a measurable bottleneck, replace the brute-force span scan
|
|
771
|
+
* with a requirement-aware sliding window or prefix-count index that can:
|
|
772
|
+
* 1. detect whether a span satisfies the requirement,
|
|
773
|
+
* 2. prove minimality without rescanning adjacent subspans, and
|
|
774
|
+
* 3. still preserve current semantics for extras-inside-span, matchedIndices,
|
|
775
|
+
* branch-tail ambiguity, and repeated independent matches in one compound.
|
|
776
|
+
*/
|
|
777
|
+
function collectGroupMatchLocations(
|
|
778
|
+
targetGroup: Node,
|
|
779
|
+
findGroup: MatchGroup,
|
|
780
|
+
parent?: Selector,
|
|
781
|
+
context?: EvalContext
|
|
782
|
+
): SelectorMatchLocation[] {
|
|
783
|
+
if (!isNode(targetGroup, N.CompoundSelector)) {
|
|
784
|
+
const groupMatch = matchTargetGroup(
|
|
785
|
+
targetGroup,
|
|
786
|
+
findGroup,
|
|
787
|
+
parent,
|
|
788
|
+
!!parent && isNode(targetGroup, N.Ampersand),
|
|
789
|
+
context
|
|
790
|
+
);
|
|
791
|
+
return groupMatch.matched
|
|
792
|
+
? [{
|
|
793
|
+
startIndex: 0,
|
|
794
|
+
endIndex: 0,
|
|
795
|
+
containingNode: targetGroup,
|
|
796
|
+
exact: groupMatch.exact,
|
|
797
|
+
consumedTarget: groupMatch.exact
|
|
798
|
+
}]
|
|
799
|
+
: [];
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const matches: SelectorMatchLocation[] = [];
|
|
803
|
+
const seen = new Set<number>();
|
|
804
|
+
const targetCompound = targetGroup as unknown as Selector & { value: readonly Node[] };
|
|
805
|
+
const targetLength = (targetGroup as CompoundSelector).get('value').length;
|
|
806
|
+
|
|
807
|
+
for (let i = 0; i < findGroup.alternatives.length; i++) {
|
|
808
|
+
const requirement = findGroup.alternatives[i]!;
|
|
809
|
+
for (let start = 0; start < targetLength; start++) {
|
|
810
|
+
for (let end = start; end < targetLength; end++) {
|
|
811
|
+
const windowMatch = matchCompoundWindow(
|
|
812
|
+
targetCompound,
|
|
813
|
+
start,
|
|
814
|
+
end,
|
|
815
|
+
requirement,
|
|
816
|
+
parent,
|
|
817
|
+
context
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
if (!windowMatch.matched) {
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const withoutStartMatches = start < end
|
|
825
|
+
&& matchCompoundWindow(targetCompound, start + 1, end, requirement, parent, context).matched;
|
|
826
|
+
if (withoutStartMatches) {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const withoutEndMatches = start < end
|
|
831
|
+
&& matchCompoundWindow(targetCompound, start, end - 1, requirement, parent, context).matched;
|
|
832
|
+
if (withoutEndMatches) {
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const key = start * targetLength + end;
|
|
837
|
+
if (seen.has(key)) {
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
seen.add(key);
|
|
842
|
+
|
|
843
|
+
matches.push({
|
|
844
|
+
startIndex: start,
|
|
845
|
+
endIndex: end,
|
|
846
|
+
matchedIndices: collectMatchedIndicesForWindow(targetCompound, start, end, requirement, context),
|
|
847
|
+
containingNode: targetGroup,
|
|
848
|
+
exact: windowMatch.exact && start === 0 && end === targetLength - 1,
|
|
849
|
+
consumedTarget: windowMatch.exact && start === 0 && end === targetLength - 1
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
matches.sort((left, right) => {
|
|
856
|
+
const leftStart = left.startIndex ?? 0;
|
|
857
|
+
const rightStart = right.startIndex ?? 0;
|
|
858
|
+
if (leftStart !== rightStart) {
|
|
859
|
+
return leftStart - rightStart;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const leftEnd = left.endIndex ?? leftStart;
|
|
863
|
+
const rightEnd = right.endIndex ?? rightStart;
|
|
864
|
+
return leftEnd - rightEnd;
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
const filtered: SelectorMatchLocation[] = [];
|
|
868
|
+
let lastEnd = -1;
|
|
869
|
+
for (let i = 0; i < matches.length; i++) {
|
|
870
|
+
const match = matches[i]!;
|
|
871
|
+
const start = match.startIndex ?? 0;
|
|
872
|
+
const end = match.endIndex ?? start;
|
|
873
|
+
|
|
874
|
+
if (start <= lastEnd) {
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
filtered.push(match);
|
|
879
|
+
lastEnd = end;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
return filtered;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/** Fast constructor for the no-match result shape. */
|
|
886
|
+
function emptySelectorMatchState(): SelectorMatchState {
|
|
887
|
+
return {
|
|
888
|
+
fullMatch: false,
|
|
889
|
+
partialMatch: false,
|
|
890
|
+
crossesAmpersand: false,
|
|
891
|
+
matches: []
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/** Appends one result list into another without allocating a combined array. */
|
|
896
|
+
function pushMatches(
|
|
897
|
+
target: SelectorMatchLocation[],
|
|
898
|
+
source: SelectorMatchLocation[]
|
|
899
|
+
): void {
|
|
900
|
+
for (let i = 0; i < source.length; i++) {
|
|
901
|
+
target.push(source[i]!);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function cloneMatchSegment(
|
|
906
|
+
segment: SelectorMatchSegment | undefined
|
|
907
|
+
): SelectorMatchSegment | undefined {
|
|
908
|
+
if (!segment) {
|
|
909
|
+
return undefined;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
return {
|
|
913
|
+
containingNode: segment.containingNode,
|
|
914
|
+
startIndex: segment.startIndex,
|
|
915
|
+
endIndex: segment.endIndex,
|
|
916
|
+
matchedIndices: segment.matchedIndices ? [...segment.matchedIndices] : undefined
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function cloneAmpersandCrossings(
|
|
921
|
+
crossings: SelectorMatchAmpersandCrossing[] | undefined
|
|
922
|
+
): SelectorMatchAmpersandCrossing[] | undefined {
|
|
923
|
+
if (!crossings || crossings.length === 0) {
|
|
924
|
+
return undefined;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const next = new Array<SelectorMatchAmpersandCrossing>(crossings.length);
|
|
928
|
+
for (let i = 0; i < crossings.length; i++) {
|
|
929
|
+
const crossing = crossings[i]!;
|
|
930
|
+
next[i] = {
|
|
931
|
+
ampersandNode: crossing.ampersandNode,
|
|
932
|
+
targetSegment: cloneMatchSegment(crossing.targetSegment)!,
|
|
933
|
+
parentSegment: cloneMatchSegment(crossing.parentSegment)
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
return next;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function getCachedGroupMatch(
|
|
941
|
+
cache: GroupMatchCache,
|
|
942
|
+
targetGroup: Node,
|
|
943
|
+
findGroup: MatchGroup,
|
|
944
|
+
parent?: Selector,
|
|
945
|
+
evalContext?: EvalContext
|
|
946
|
+
): MatchWindowResult {
|
|
947
|
+
let nodeCache = cache.get(targetGroup);
|
|
948
|
+
if (!nodeCache) {
|
|
949
|
+
nodeCache = new WeakMap<MatchGroup, MatchWindowResult>();
|
|
950
|
+
cache.set(targetGroup, nodeCache);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
const cached = nodeCache.get(findGroup);
|
|
954
|
+
if (cached) {
|
|
955
|
+
return cached;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
let result: MatchWindowResult;
|
|
959
|
+
if (isNode(targetGroup, N.CompoundSelector)) {
|
|
960
|
+
let matched = false;
|
|
961
|
+
let exact = false;
|
|
962
|
+
|
|
963
|
+
for (let i = 0; i < findGroup.alternatives.length; i++) {
|
|
964
|
+
const windowMatch = matchCompoundWindow(
|
|
965
|
+
targetGroup as unknown as Selector & { value: readonly Node[] },
|
|
966
|
+
0,
|
|
967
|
+
(targetGroup as CompoundSelector).get('value').length - 1,
|
|
968
|
+
findGroup.alternatives[i]!,
|
|
969
|
+
parent,
|
|
970
|
+
evalContext
|
|
971
|
+
);
|
|
972
|
+
matched ||= windowMatch.matched;
|
|
973
|
+
exact ||= windowMatch.exact;
|
|
974
|
+
|
|
975
|
+
if (exact) {
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
result = { matched, exact };
|
|
981
|
+
} else {
|
|
982
|
+
result = matchTargetGroup(
|
|
983
|
+
targetGroup,
|
|
984
|
+
findGroup,
|
|
985
|
+
parent,
|
|
986
|
+
!!parent && isNode(targetGroup, N.Ampersand),
|
|
987
|
+
evalContext
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
nodeCache.set(findGroup, result);
|
|
991
|
+
return result;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function getBranchAlternatives(node: Node): readonly Selector[] | undefined {
|
|
995
|
+
if (isNode(node, N.SelectorList)) {
|
|
996
|
+
return (node as SelectorList).get('value');
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
if (isNode(node, N.PseudoSelector) && node.get('name') === ':is' && isNode(node.get('arg'), N.Selector)) {
|
|
1000
|
+
if (isNode(node.get('arg'), N.SelectorList)) {
|
|
1001
|
+
return (node.get('arg') as SelectorList).get('value');
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return [node.get('arg') as Selector];
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
return undefined;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function matchGroupNodes(
|
|
1011
|
+
findNode: Node,
|
|
1012
|
+
targetNode: Node,
|
|
1013
|
+
findGroup: MatchGroup,
|
|
1014
|
+
groupMatchCache: GroupMatchCache,
|
|
1015
|
+
context: SelectorMatchContext,
|
|
1016
|
+
parent?: Selector
|
|
1017
|
+
): MatchWindowResult {
|
|
1018
|
+
const targetBranches = getBranchAlternatives(targetNode);
|
|
1019
|
+
const findBranches = getBranchAlternatives(findNode);
|
|
1020
|
+
if (targetBranches && findBranches) {
|
|
1021
|
+
let matched = false;
|
|
1022
|
+
let exact = false;
|
|
1023
|
+
|
|
1024
|
+
for (let i = 0; i < findBranches.length; i++) {
|
|
1025
|
+
for (let j = 0; j < targetBranches.length; j++) {
|
|
1026
|
+
const nested = selectorMatchInternal(findBranches[i]!, targetBranches[j]!, parent, context);
|
|
1027
|
+
matched ||= nested.partialMatch;
|
|
1028
|
+
exact ||= nested.fullMatch;
|
|
1029
|
+
|
|
1030
|
+
if (exact) {
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
if (exact) {
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
return { matched, exact };
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (targetBranches) {
|
|
1044
|
+
let matched = false;
|
|
1045
|
+
let exact = false;
|
|
1046
|
+
|
|
1047
|
+
for (let i = 0; i < targetBranches.length; i++) {
|
|
1048
|
+
const nested = selectorMatchInternal(findNode as Selector, targetBranches[i]!, parent, context);
|
|
1049
|
+
matched ||= nested.partialMatch;
|
|
1050
|
+
exact ||= nested.fullMatch;
|
|
1051
|
+
|
|
1052
|
+
if (exact) {
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
return { matched, exact };
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
return getCachedGroupMatch(groupMatchCache, targetNode, findGroup, parent, context.evalContext);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
function pushNestedBranchMatches(
|
|
1064
|
+
findNode: Node,
|
|
1065
|
+
target: Selector,
|
|
1066
|
+
matches: SelectorMatchLocation[],
|
|
1067
|
+
context: SelectorMatchContext,
|
|
1068
|
+
parent?: Selector
|
|
1069
|
+
): void {
|
|
1070
|
+
const branches = getBranchAlternatives(findNode);
|
|
1071
|
+
if (branches) {
|
|
1072
|
+
for (let i = 0; i < branches.length; i++) {
|
|
1073
|
+
const nested = selectorMatchInternal(target, branches[i]!, parent, context);
|
|
1074
|
+
if (!nested.fullMatch) {
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
for (let j = 0; j < nested.matches.length; j++) {
|
|
1078
|
+
const match = nested.matches[j]!;
|
|
1079
|
+
matches.push({
|
|
1080
|
+
startIndex: match.startIndex,
|
|
1081
|
+
endIndex: match.endIndex,
|
|
1082
|
+
matchedIndices: match.matchedIndices,
|
|
1083
|
+
containingNode: match.containingNode,
|
|
1084
|
+
exact: false,
|
|
1085
|
+
consumedTarget: match.consumedTarget,
|
|
1086
|
+
ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const children = findNode.children();
|
|
1094
|
+
let child = children.next();
|
|
1095
|
+
while (!child.done) {
|
|
1096
|
+
pushNestedBranchMatches(child.value, target, matches, context, parent);
|
|
1097
|
+
child = children.next();
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* Compares a contiguous slice of ordered units.
|
|
1103
|
+
*
|
|
1104
|
+
* Groups compare via unordered basic-selector consumption, while combinators
|
|
1105
|
+
* must match exactly in place.
|
|
1106
|
+
*/
|
|
1107
|
+
function matchUnitWindow(
|
|
1108
|
+
findUnits: MatchPlanUnit[],
|
|
1109
|
+
findStart: number,
|
|
1110
|
+
targetUnits: MatchPlanUnit[],
|
|
1111
|
+
targetStart: number,
|
|
1112
|
+
length: number,
|
|
1113
|
+
groupMatchCache: GroupMatchCache,
|
|
1114
|
+
context: SelectorMatchContext,
|
|
1115
|
+
parent?: Selector
|
|
1116
|
+
): MatchWindowResult {
|
|
1117
|
+
let exact = true;
|
|
1118
|
+
|
|
1119
|
+
for (let offset = 0; offset < length; offset++) {
|
|
1120
|
+
const findUnit = findUnits[findStart + offset]!;
|
|
1121
|
+
const targetUnit = targetUnits[targetStart + offset]!;
|
|
1122
|
+
|
|
1123
|
+
if (findUnit.kind !== targetUnit.kind) {
|
|
1124
|
+
return { matched: false, exact: false };
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
if (findUnit.kind === 'combinator') {
|
|
1128
|
+
if (targetUnit.kind !== 'combinator' || findUnit.value !== targetUnit.value) {
|
|
1129
|
+
return { matched: false, exact: false };
|
|
1130
|
+
}
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
const groupMatch = matchGroupNodes(
|
|
1135
|
+
findUnit.node,
|
|
1136
|
+
targetUnit.node,
|
|
1137
|
+
findUnit.group,
|
|
1138
|
+
groupMatchCache,
|
|
1139
|
+
context,
|
|
1140
|
+
parent
|
|
1141
|
+
);
|
|
1142
|
+
if (!groupMatch.matched) {
|
|
1143
|
+
return { matched: false, exact: false };
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
exact &&= groupMatch.exact;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
return { matched: true, exact };
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** True when a complex selector begins with a visible ampersand boundary. */
|
|
1153
|
+
function hasLeadingAmpersandBoundary(selector: Selector): boolean {
|
|
1154
|
+
return (
|
|
1155
|
+
isNode(selector, N.ComplexSelector)
|
|
1156
|
+
&& (selector as ComplexSelector).get('value').length > 0
|
|
1157
|
+
&& isNode((selector as ComplexSelector).get('value')[0]!, N.Ampersand)
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
function getBoundaryTailUnits(routePlan: RouteMatchPlan): MatchPlanUnit[] {
|
|
1162
|
+
if (hasLeadingAmpersandBoundary(routePlan.selector)) {
|
|
1163
|
+
return routePlan.units;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
return [{
|
|
1167
|
+
kind: 'combinator',
|
|
1168
|
+
index: -1,
|
|
1169
|
+
node: routePlan.selector,
|
|
1170
|
+
value: ' '
|
|
1171
|
+
}, ...routePlan.units];
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function locationCrossesAmpersand(location: SelectorMatchLocation): boolean {
|
|
1175
|
+
if (location.ampersandCrossings && location.ampersandCrossings.length > 0) {
|
|
1176
|
+
return true;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
if (location.crossesAmpersand) {
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const { containingNode, startIndex, endIndex } = location;
|
|
1184
|
+
|
|
1185
|
+
if (!containingNode.hasFlag(F_AMPERSAND)) {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
if (startIndex === undefined || endIndex === undefined) {
|
|
1190
|
+
return true;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
if (isNode(containingNode, N.CompoundSelector) || isNode(containingNode, N.ComplexSelector)) {
|
|
1194
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
1195
|
+
if (containingNode.get('value')[i]?.hasFlag(F_AMPERSAND)) {
|
|
1196
|
+
return true;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
if (isNode(containingNode, N.SelectorList)) {
|
|
1203
|
+
return !!(containingNode as SelectorList).get('value')[startIndex]?.hasFlag(F_AMPERSAND);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
return true;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
function getLocationAmpersandCrossings(
|
|
1210
|
+
location: SelectorMatchLocation,
|
|
1211
|
+
context?: EvalContext
|
|
1212
|
+
): SelectorMatchAmpersandCrossing[] | undefined {
|
|
1213
|
+
if (location.ampersandCrossings && location.ampersandCrossings.length > 0) {
|
|
1214
|
+
return location.ampersandCrossings;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
const { containingNode } = location;
|
|
1218
|
+
if (!containingNode.hasFlag(F_AMPERSAND)) {
|
|
1219
|
+
return undefined;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const indices = location.matchedIndices && location.matchedIndices.length > 0
|
|
1223
|
+
? location.matchedIndices
|
|
1224
|
+
: undefined;
|
|
1225
|
+
const start = location.startIndex ?? indices?.[0] ?? 0;
|
|
1226
|
+
const end = location.endIndex ?? indices?.[indices.length - 1] ?? start;
|
|
1227
|
+
const targetSegment: SelectorMatchSegment = {
|
|
1228
|
+
containingNode,
|
|
1229
|
+
startIndex: location.startIndex,
|
|
1230
|
+
endIndex: location.endIndex,
|
|
1231
|
+
matchedIndices: location.matchedIndices ? [...location.matchedIndices] : undefined
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
const crossings: SelectorMatchAmpersandCrossing[] = [];
|
|
1235
|
+
const seenAmpersands = new Set<Node>();
|
|
1236
|
+
const pushCrossing = (ampersandNode: Node): void => {
|
|
1237
|
+
if (seenAmpersands.has(ampersandNode)) {
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
seenAmpersands.add(ampersandNode);
|
|
1242
|
+
|
|
1243
|
+
let parentSegment: SelectorMatchSegment | undefined;
|
|
1244
|
+
if (isNode(ampersandNode, N.Ampersand)) {
|
|
1245
|
+
const resolved = ampersandNode.getResolvedSelector(context);
|
|
1246
|
+
if (resolved && !isNode(resolved, N.Nil)) {
|
|
1247
|
+
parentSegment = {
|
|
1248
|
+
containingNode: resolved
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
crossings.push({
|
|
1254
|
+
ampersandNode,
|
|
1255
|
+
targetSegment,
|
|
1256
|
+
parentSegment
|
|
1257
|
+
});
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
if (isNode(containingNode, N.Ampersand)) {
|
|
1261
|
+
pushCrossing(containingNode);
|
|
1262
|
+
return crossings;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
if (isNode(containingNode, N.CompoundSelector) || isNode(containingNode, N.ComplexSelector)) {
|
|
1266
|
+
if (indices) {
|
|
1267
|
+
for (let i = 0; i < indices.length; i++) {
|
|
1268
|
+
const idx = indices[i]!;
|
|
1269
|
+
const node = containingNode.get('value')[idx];
|
|
1270
|
+
if (node && isNode(node, N.Ampersand)) {
|
|
1271
|
+
pushCrossing(node);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
for (let i = start; i <= end; i++) {
|
|
1277
|
+
const node = containingNode.get('value')[i];
|
|
1278
|
+
if (node && isNode(node, N.Ampersand)) {
|
|
1279
|
+
pushCrossing(node);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
if (isNode(containingNode, N.SelectorList) && start === end) {
|
|
1285
|
+
const node = (containingNode as SelectorList).get('value')[start];
|
|
1286
|
+
if (node && isNode(node, N.Ampersand)) {
|
|
1287
|
+
pushCrossing(node);
|
|
1288
|
+
} else if (node && (isNode(node, N.CompoundSelector) || isNode(node, N.ComplexSelector))) {
|
|
1289
|
+
for (let i = 0; i < node.get('value').length; i++) {
|
|
1290
|
+
const child = node.get('value')[i];
|
|
1291
|
+
if (child && isNode(child, N.Ampersand)) {
|
|
1292
|
+
const resolved = child.getResolvedSelector(context);
|
|
1293
|
+
crossings.push({
|
|
1294
|
+
ampersandNode: child,
|
|
1295
|
+
targetSegment,
|
|
1296
|
+
parentSegment: resolved && !isNode(resolved, N.Nil)
|
|
1297
|
+
? { containingNode: resolved as Selector }
|
|
1298
|
+
: undefined
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
return crossings.length > 0 ? crossings : undefined;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Finalizes aggregate booleans from the collected match list.
|
|
1310
|
+
*
|
|
1311
|
+
* This intentionally uses a single scan to avoid repeated array passes on a
|
|
1312
|
+
* hot path.
|
|
1313
|
+
*/
|
|
1314
|
+
function finalizeMatchState(result: SelectorMatchState, context?: EvalContext): SelectorMatchState {
|
|
1315
|
+
let fullMatch = false;
|
|
1316
|
+
let crossesAmpersand = false;
|
|
1317
|
+
|
|
1318
|
+
for (let i = 0; i < result.matches.length; i++) {
|
|
1319
|
+
const match = result.matches[i]!;
|
|
1320
|
+
if (!match.ampersandCrossings) {
|
|
1321
|
+
match.ampersandCrossings = getLocationAmpersandCrossings(match, context);
|
|
1322
|
+
}
|
|
1323
|
+
fullMatch ||= !!match.exact;
|
|
1324
|
+
crossesAmpersand ||= locationCrossesAmpersand(match);
|
|
1325
|
+
|
|
1326
|
+
if (fullMatch && crossesAmpersand) {
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
result.fullMatch ||= fullMatch;
|
|
1332
|
+
result.partialMatch ||= result.matches.length > 0;
|
|
1333
|
+
result.crossesAmpersand = crossesAmpersand;
|
|
1334
|
+
return result;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Recursively searches inside searchable pseudo-selector arguments.
|
|
1339
|
+
*
|
|
1340
|
+
* These nested matches are root-like searches; they do not allow an outer
|
|
1341
|
+
* match route to continue through the pseudo boundary.
|
|
1342
|
+
*/
|
|
1343
|
+
function pushNestedPseudoMatches(
|
|
1344
|
+
find: Selector,
|
|
1345
|
+
targetNode: Node,
|
|
1346
|
+
matches: SelectorMatchLocation[],
|
|
1347
|
+
context: SelectorMatchContext
|
|
1348
|
+
): void {
|
|
1349
|
+
if (isSearchablePseudoBoundary(targetNode)) {
|
|
1350
|
+
if (isSearchablePseudoBoundary(find)) {
|
|
1351
|
+
if (find.get('name') !== targetNode.get('name')) {
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
const nested = selectorMatchInternal(find.get('arg') as Selector, targetNode.get('arg') as Selector, undefined, context);
|
|
1356
|
+
for (let i = 0; i < nested.matches.length; i++) {
|
|
1357
|
+
const match = nested.matches[i]!;
|
|
1358
|
+
matches.push({
|
|
1359
|
+
startIndex: match.startIndex,
|
|
1360
|
+
endIndex: match.endIndex,
|
|
1361
|
+
containingNode: match.containingNode,
|
|
1362
|
+
exact: match.exact,
|
|
1363
|
+
consumedTarget: match.consumedTarget,
|
|
1364
|
+
ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
const nested = selectorMatchInternal(find, targetNode.get('arg') as Selector, undefined, context);
|
|
1371
|
+
for (let i = 0; i < nested.matches.length; i++) {
|
|
1372
|
+
const match = nested.matches[i]!;
|
|
1373
|
+
matches.push({
|
|
1374
|
+
startIndex: match.startIndex,
|
|
1375
|
+
endIndex: match.endIndex,
|
|
1376
|
+
containingNode: match.containingNode,
|
|
1377
|
+
exact: false,
|
|
1378
|
+
consumedTarget: match.consumedTarget,
|
|
1379
|
+
ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const children = targetNode.children();
|
|
1386
|
+
let child = children.next();
|
|
1387
|
+
while (!child.done) {
|
|
1388
|
+
pushNestedPseudoMatches(find, child.value, matches, context);
|
|
1389
|
+
child = children.next();
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Finds all occurrences of `find` inside `target`.
|
|
1395
|
+
*
|
|
1396
|
+
* Matching is ordered at the route level, unordered only inside a single
|
|
1397
|
+
* compound-like position, and branches only at `SelectorList` alternatives.
|
|
1398
|
+
* A selector list on the find side is treated as alternate find routes: any
|
|
1399
|
+
* one alternate may match, and each matching alternate contributes its own
|
|
1400
|
+
* recorded locations.
|
|
1401
|
+
*
|
|
1402
|
+
* When `parent` is provided, it acts like an implicit prefix context joined to
|
|
1403
|
+
* `target` by a descendant combinator. Parent traversal is attempted only when
|
|
1404
|
+
* a match reaches a left-side ampersand boundary with partial-but-incomplete
|
|
1405
|
+
* progress; matches that exist only inside the parent do not get added on
|
|
1406
|
+
* their own.
|
|
1407
|
+
*
|
|
1408
|
+
* That same `parent` context is preserved when matching through nested
|
|
1409
|
+
* selector-list and `:is(...)` alternatives, because those are still the same
|
|
1410
|
+
* authored match route. It is not preserved for root-like searches inside
|
|
1411
|
+
* non-`:is()` pseudo-selector boundaries, because those searches must not
|
|
1412
|
+
* continue the outer route through that boundary.
|
|
1413
|
+
*
|
|
1414
|
+
* Complex selector branches inside `:is(...)` or selector lists are treated as
|
|
1415
|
+
* alternate branch routes. Matching may succeed inside one branch, but the
|
|
1416
|
+
* outer route cannot continue leftward through that branch unless that branch
|
|
1417
|
+
* itself was consumed end-to-end.
|
|
1418
|
+
*/
|
|
1419
|
+
function selectorMatchUncached(
|
|
1420
|
+
find: Selector,
|
|
1421
|
+
target: Selector,
|
|
1422
|
+
parent: Selector | undefined,
|
|
1423
|
+
context: SelectorMatchContext
|
|
1424
|
+
): SelectorMatchState {
|
|
1425
|
+
const evalContext = context.evalContext;
|
|
1426
|
+
|
|
1427
|
+
if (isNode(find, N.SelectorList)) {
|
|
1428
|
+
const result = emptySelectorMatchState();
|
|
1429
|
+
|
|
1430
|
+
for (let i = 0; i < (find as SelectorList).get('value').length; i++) {
|
|
1431
|
+
const nested = selectorMatchInternal((find as SelectorList).get('value')[i]!, target, parent, context);
|
|
1432
|
+
result.fullMatch ||= nested.fullMatch;
|
|
1433
|
+
result.partialMatch ||= nested.partialMatch;
|
|
1434
|
+
result.crossesAmpersand ||= nested.crossesAmpersand;
|
|
1435
|
+
pushMatches(result.matches, nested.matches);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
return finalizeMatchState(result, context.evalContext);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
if (
|
|
1442
|
+
isNode(find, N.PseudoSelector)
|
|
1443
|
+
&& find.get('name') !== ':is'
|
|
1444
|
+
&& isNode(find.get('arg'), N.Selector)
|
|
1445
|
+
) {
|
|
1446
|
+
if (isSearchablePseudoBoundary(target)) {
|
|
1447
|
+
if (find.get('name') !== target.get('name')) {
|
|
1448
|
+
return emptySelectorMatchState();
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
return selectorMatchInternal(find.get('arg') as Selector, target.get('arg') as Selector, parent, context);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
const nested = selectorMatchInternal(find.get('arg') as Selector, target, parent, context);
|
|
1455
|
+
if (!nested.partialMatch) {
|
|
1456
|
+
return nested;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const matches = new Array<SelectorMatchLocation>(nested.matches.length);
|
|
1460
|
+
for (let i = 0; i < nested.matches.length; i++) {
|
|
1461
|
+
const match = nested.matches[i]!;
|
|
1462
|
+
matches[i] = {
|
|
1463
|
+
startIndex: match.startIndex,
|
|
1464
|
+
endIndex: match.endIndex,
|
|
1465
|
+
containingNode: find.get('arg') as Node,
|
|
1466
|
+
exact: false,
|
|
1467
|
+
consumedTarget: false,
|
|
1468
|
+
ampersandCrossings: cloneAmpersandCrossings(match.ampersandCrossings)
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
return {
|
|
1473
|
+
fullMatch: false,
|
|
1474
|
+
partialMatch: true,
|
|
1475
|
+
crossesAmpersand: matches.some(locationCrossesAmpersand),
|
|
1476
|
+
matches
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
const findValue = selectorValueOf(find, evalContext);
|
|
1481
|
+
if (isNode(target, N.SelectorList)) {
|
|
1482
|
+
for (let i = 0; i < (target as SelectorList).get('value').length; i++) {
|
|
1483
|
+
const sel = (target as SelectorList).get('value')[i]!;
|
|
1484
|
+
if (findValue === selectorValueOf(sel, evalContext)) {
|
|
1485
|
+
return {
|
|
1486
|
+
fullMatch: true,
|
|
1487
|
+
partialMatch: true,
|
|
1488
|
+
crossesAmpersand: sel.hasFlag(F_AMPERSAND),
|
|
1489
|
+
matches: [{
|
|
1490
|
+
startIndex: i,
|
|
1491
|
+
endIndex: i,
|
|
1492
|
+
matchedIndices: [i],
|
|
1493
|
+
containingNode: target,
|
|
1494
|
+
exact: true,
|
|
1495
|
+
consumedTarget: (target as SelectorList).get('value').length === 1,
|
|
1496
|
+
ampersandCrossings: getLocationAmpersandCrossings({
|
|
1497
|
+
startIndex: i,
|
|
1498
|
+
endIndex: i,
|
|
1499
|
+
matchedIndices: [i],
|
|
1500
|
+
containingNode: target,
|
|
1501
|
+
exact: true,
|
|
1502
|
+
consumedTarget: (target as SelectorList).get('value').length === 1
|
|
1503
|
+
}, evalContext)
|
|
1504
|
+
}]
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
const nested = selectorMatchInternal(find, sel, parent, context);
|
|
1509
|
+
if (nested.partialMatch) {
|
|
1510
|
+
return {
|
|
1511
|
+
fullMatch: nested.fullMatch,
|
|
1512
|
+
partialMatch: true,
|
|
1513
|
+
crossesAmpersand: nested.crossesAmpersand || sel.hasFlag(F_AMPERSAND),
|
|
1514
|
+
matches: [{
|
|
1515
|
+
startIndex: i,
|
|
1516
|
+
endIndex: i,
|
|
1517
|
+
matchedIndices: [i],
|
|
1518
|
+
containingNode: target,
|
|
1519
|
+
exact: nested.fullMatch,
|
|
1520
|
+
consumedTarget: nested.fullMatch && (target as SelectorList).get('value').length === 1,
|
|
1521
|
+
ampersandCrossings: cloneAmpersandCrossings(nested.matches[0]?.ampersandCrossings)
|
|
1522
|
+
}]
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return emptySelectorMatchState();
|
|
1527
|
+
} else {
|
|
1528
|
+
if (findValue === selectorValueOf(target, evalContext)) {
|
|
1529
|
+
return {
|
|
1530
|
+
fullMatch: true,
|
|
1531
|
+
partialMatch: true,
|
|
1532
|
+
crossesAmpersand: target.hasFlag(F_AMPERSAND),
|
|
1533
|
+
matches: [{
|
|
1534
|
+
containingNode: target,
|
|
1535
|
+
exact: true,
|
|
1536
|
+
consumedTarget: true,
|
|
1537
|
+
ampersandCrossings: getLocationAmpersandCrossings({
|
|
1538
|
+
containingNode: target,
|
|
1539
|
+
exact: true,
|
|
1540
|
+
consumedTarget: true
|
|
1541
|
+
}, evalContext)
|
|
1542
|
+
}]
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
if (
|
|
1548
|
+
!parent
|
|
1549
|
+
&& find.keySetLibrary
|
|
1550
|
+
&& target.keySetLibrary
|
|
1551
|
+
) {
|
|
1552
|
+
if (evalContext) {
|
|
1553
|
+
if (safeIsDisjoint(find.getKeySet(evalContext), target.getKeySet(evalContext))) {
|
|
1554
|
+
return emptySelectorMatchState();
|
|
1555
|
+
}
|
|
1556
|
+
} else {
|
|
1557
|
+
if (
|
|
1558
|
+
!find.hasFlag(F_AMPERSAND)
|
|
1559
|
+
&& target.hasFlag(F_AMPERSAND)
|
|
1560
|
+
&& safeIsDisjoint(find.visibleKeySet, target.visibleKeySet)
|
|
1561
|
+
) {
|
|
1562
|
+
return emptySelectorMatchState();
|
|
1563
|
+
}
|
|
1564
|
+
if (
|
|
1565
|
+
!safeIsDisjoint(find.requiredKeySet, find.keySet)
|
|
1566
|
+
&& safeIsDisjoint(find.requiredKeySet, target.keySet)
|
|
1567
|
+
) {
|
|
1568
|
+
return emptySelectorMatchState();
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
const findPlan = getSelectorMatchPlan(find, undefined, evalContext);
|
|
1574
|
+
if (findPlan.kind !== 'route' || findPlan.units.length === 0) {
|
|
1575
|
+
return emptySelectorMatchState();
|
|
1576
|
+
}
|
|
1577
|
+
const groupMatchCache: GroupMatchCache = new WeakMap();
|
|
1578
|
+
|
|
1579
|
+
const matchParentRoute = (
|
|
1580
|
+
routePlan: RouteMatchPlan,
|
|
1581
|
+
parentPlan: MatchPlan
|
|
1582
|
+
): SelectorMatchState => {
|
|
1583
|
+
const result = emptySelectorMatchState();
|
|
1584
|
+
const routeUnits = routePlan.units;
|
|
1585
|
+
const findUnits = findPlan.units;
|
|
1586
|
+
|
|
1587
|
+
if (routeUnits.length === 0) {
|
|
1588
|
+
return result;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
const boundaryTailUnits = getBoundaryTailUnits(routePlan);
|
|
1592
|
+
const boundaryTailLength = boundaryTailUnits.length;
|
|
1593
|
+
const findLength = findUnits.length;
|
|
1594
|
+
const matchedAll = (
|
|
1595
|
+
findLength <= boundaryTailLength
|
|
1596
|
+
&& matchUnitWindow(
|
|
1597
|
+
findUnits,
|
|
1598
|
+
0,
|
|
1599
|
+
boundaryTailUnits,
|
|
1600
|
+
boundaryTailLength - findLength,
|
|
1601
|
+
findLength,
|
|
1602
|
+
groupMatchCache,
|
|
1603
|
+
context,
|
|
1604
|
+
parent
|
|
1605
|
+
).matched
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
if (matchedAll || boundaryTailLength > findLength) {
|
|
1609
|
+
return result;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
const targetBoundaryMatch = matchUnitWindow(
|
|
1613
|
+
findUnits,
|
|
1614
|
+
findLength - boundaryTailLength,
|
|
1615
|
+
boundaryTailUnits,
|
|
1616
|
+
0,
|
|
1617
|
+
boundaryTailLength,
|
|
1618
|
+
groupMatchCache,
|
|
1619
|
+
context,
|
|
1620
|
+
parent
|
|
1621
|
+
);
|
|
1622
|
+
|
|
1623
|
+
if (!targetBoundaryMatch.matched) {
|
|
1624
|
+
return result;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
const remainingFindLength = findLength - boundaryTailLength;
|
|
1628
|
+
if (remainingFindLength === 0) {
|
|
1629
|
+
return result;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
const matchParentPlan = (plan: MatchPlan): void => {
|
|
1633
|
+
if (plan.kind === 'list') {
|
|
1634
|
+
for (let i = 0; i < plan.alternates.length; i++) {
|
|
1635
|
+
matchParentPlan(plan.alternates[i]!);
|
|
1636
|
+
}
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
const parentUnits = plan.units;
|
|
1641
|
+
if (parentUnits.length < remainingFindLength) {
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
const parentMatch = matchUnitWindow(
|
|
1646
|
+
findUnits,
|
|
1647
|
+
0,
|
|
1648
|
+
parentUnits,
|
|
1649
|
+
parentUnits.length - remainingFindLength,
|
|
1650
|
+
remainingFindLength,
|
|
1651
|
+
groupMatchCache,
|
|
1652
|
+
context
|
|
1653
|
+
);
|
|
1654
|
+
|
|
1655
|
+
if (!parentMatch.matched) {
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
const exact = (
|
|
1660
|
+
parentMatch.exact
|
|
1661
|
+
&& targetBoundaryMatch.exact
|
|
1662
|
+
&& remainingFindLength === parentUnits.length
|
|
1663
|
+
);
|
|
1664
|
+
const firstTargetUnit = boundaryTailUnits[0]!;
|
|
1665
|
+
const lastTargetUnit = boundaryTailUnits[boundaryTailLength - 1]!;
|
|
1666
|
+
const firstParentUnit = parentUnits[parentUnits.length - remainingFindLength]!;
|
|
1667
|
+
const lastParentUnit = parentUnits[parentUnits.length - 1]!;
|
|
1668
|
+
const leadingAmpersand = hasLeadingAmpersandBoundary(routePlan.selector)
|
|
1669
|
+
? (routePlan.selector as ComplexSelector).value[0]
|
|
1670
|
+
: undefined;
|
|
1671
|
+
const ampersandCrossings: SelectorMatchAmpersandCrossing[] = [{
|
|
1672
|
+
ampersandNode: leadingAmpersand,
|
|
1673
|
+
targetSegment: {
|
|
1674
|
+
containingNode: routePlan.selector,
|
|
1675
|
+
startIndex: firstTargetUnit.index,
|
|
1676
|
+
endIndex: lastTargetUnit.index
|
|
1677
|
+
},
|
|
1678
|
+
parentSegment: {
|
|
1679
|
+
containingNode: plan.selector,
|
|
1680
|
+
startIndex: firstParentUnit.index,
|
|
1681
|
+
endIndex: lastParentUnit.index
|
|
1682
|
+
}
|
|
1683
|
+
}];
|
|
1684
|
+
|
|
1685
|
+
if (isNode(routePlan.selector, N.CompoundSelector) || isNode(routePlan.selector, N.ComplexSelector)) {
|
|
1686
|
+
result.matches.push({
|
|
1687
|
+
startIndex: 0,
|
|
1688
|
+
endIndex: (routePlan.selector as CompoundSelector | ComplexSelector).get('value').length - 1,
|
|
1689
|
+
containingNode: routePlan.selector,
|
|
1690
|
+
exact,
|
|
1691
|
+
crossesAmpersand: true,
|
|
1692
|
+
consumedTarget: !!exact,
|
|
1693
|
+
ampersandCrossings
|
|
1694
|
+
});
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
result.matches.push({
|
|
1699
|
+
containingNode: routePlan.selector,
|
|
1700
|
+
exact,
|
|
1701
|
+
crossesAmpersand: true,
|
|
1702
|
+
consumedTarget: !!exact,
|
|
1703
|
+
ampersandCrossings
|
|
1704
|
+
});
|
|
1705
|
+
};
|
|
1706
|
+
|
|
1707
|
+
matchParentPlan(parentPlan);
|
|
1708
|
+
return finalizeMatchState(result, context.evalContext);
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
const pushMidRouteAmpersandMatches = (
|
|
1712
|
+
matches: SelectorMatchLocation[],
|
|
1713
|
+
routePlan: RouteMatchPlan
|
|
1714
|
+
): void => {
|
|
1715
|
+
const routeUnits = routePlan.units;
|
|
1716
|
+
const findUnits = findPlan.units;
|
|
1717
|
+
|
|
1718
|
+
const pushMatchesForResolvedPlan = (
|
|
1719
|
+
plan: MatchPlan,
|
|
1720
|
+
ampStart: number,
|
|
1721
|
+
ampUnit: MatchPlanUnit & { kind: 'group' },
|
|
1722
|
+
tailLength: number,
|
|
1723
|
+
tailMatch: MatchWindowResult
|
|
1724
|
+
): void => {
|
|
1725
|
+
if (plan.kind === 'list') {
|
|
1726
|
+
for (let i = 0; i < plan.alternates.length; i++) {
|
|
1727
|
+
pushMatchesForResolvedPlan(plan.alternates[i]!, ampStart, ampUnit, tailLength, tailMatch);
|
|
1728
|
+
}
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
const remainingFindLength = findUnits.length - tailLength;
|
|
1733
|
+
if (remainingFindLength <= 0) {
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
const parentUnits = plan.units;
|
|
1738
|
+
if (parentUnits.length < remainingFindLength) {
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
const parentMatch = matchUnitWindow(
|
|
1743
|
+
findUnits,
|
|
1744
|
+
0,
|
|
1745
|
+
parentUnits,
|
|
1746
|
+
parentUnits.length - remainingFindLength,
|
|
1747
|
+
remainingFindLength,
|
|
1748
|
+
groupMatchCache,
|
|
1749
|
+
context
|
|
1750
|
+
);
|
|
1751
|
+
if (!parentMatch.matched) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
const tailEndUnit = routeUnits[ampStart + tailLength] ?? routeUnits[routeUnits.length - 1]!;
|
|
1756
|
+
const firstParentUnit = parentUnits[parentUnits.length - remainingFindLength]!;
|
|
1757
|
+
const lastParentUnit = parentUnits[parentUnits.length - 1]!;
|
|
1758
|
+
const exact = (
|
|
1759
|
+
ampStart === 0
|
|
1760
|
+
&& ampStart + tailLength === routeUnits.length - 1
|
|
1761
|
+
&& remainingFindLength === parentUnits.length
|
|
1762
|
+
&& parentMatch.exact
|
|
1763
|
+
&& tailMatch.exact
|
|
1764
|
+
);
|
|
1765
|
+
|
|
1766
|
+
matches.push({
|
|
1767
|
+
startIndex: ampUnit.index,
|
|
1768
|
+
endIndex: tailEndUnit.index,
|
|
1769
|
+
containingNode: routePlan.selector,
|
|
1770
|
+
exact,
|
|
1771
|
+
crossesAmpersand: true,
|
|
1772
|
+
consumedTarget: !!exact,
|
|
1773
|
+
ampersandCrossings: [{
|
|
1774
|
+
ampersandNode: ampUnit.node,
|
|
1775
|
+
targetSegment: {
|
|
1776
|
+
containingNode: routePlan.selector,
|
|
1777
|
+
startIndex: ampUnit.index,
|
|
1778
|
+
endIndex: tailEndUnit.index
|
|
1779
|
+
},
|
|
1780
|
+
parentSegment: {
|
|
1781
|
+
containingNode: plan.selector,
|
|
1782
|
+
startIndex: firstParentUnit.index,
|
|
1783
|
+
endIndex: lastParentUnit.index
|
|
1784
|
+
}
|
|
1785
|
+
}]
|
|
1786
|
+
});
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
for (let ampStart = 0; ampStart < routeUnits.length - 1; ampStart++) {
|
|
1790
|
+
const ampUnit = routeUnits[ampStart]!;
|
|
1791
|
+
if (!(ampUnit.kind === 'group' && isNode(ampUnit.node, N.Ampersand))) {
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
const resolved = ampUnit.node.getResolvedSelector(evalContext) ?? parent;
|
|
1796
|
+
if (!resolved || isNode(resolved, N.Nil)) {
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
const resolvedPlan = getSelectorMatchPlan(resolved as Selector, undefined, evalContext);
|
|
1801
|
+
const maxTailLength = Math.min(
|
|
1802
|
+
routeUnits.length - (ampStart + 1),
|
|
1803
|
+
findUnits.length - 1
|
|
1804
|
+
);
|
|
1805
|
+
|
|
1806
|
+
for (let tailLength = 1; tailLength <= maxTailLength; tailLength++) {
|
|
1807
|
+
const tailMatch = matchUnitWindow(
|
|
1808
|
+
findUnits,
|
|
1809
|
+
findUnits.length - tailLength,
|
|
1810
|
+
routeUnits,
|
|
1811
|
+
ampStart + 1,
|
|
1812
|
+
tailLength,
|
|
1813
|
+
groupMatchCache,
|
|
1814
|
+
context,
|
|
1815
|
+
parent
|
|
1816
|
+
);
|
|
1817
|
+
if (!tailMatch.matched) {
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
pushMatchesForResolvedPlan(resolvedPlan, ampStart, {
|
|
1822
|
+
...ampUnit,
|
|
1823
|
+
kind: 'group'
|
|
1824
|
+
}, tailLength, tailMatch);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
|
|
1829
|
+
const matchTargetRoute = (
|
|
1830
|
+
routePlan: RouteMatchPlan,
|
|
1831
|
+
parentPlan?: MatchPlan
|
|
1832
|
+
): SelectorMatchState => {
|
|
1833
|
+
const result = emptySelectorMatchState();
|
|
1834
|
+
const routeUnits = routePlan.units;
|
|
1835
|
+
const findUnits = findPlan.units;
|
|
1836
|
+
const singleFindGroup = findUnits.length === 1 && findUnits[0]!.kind === 'group'
|
|
1837
|
+
? findUnits[0]!
|
|
1838
|
+
: undefined;
|
|
1839
|
+
const suppressAmbiguousBranchLocations = findUnits.length > 0 && routePlan.selector !== find
|
|
1840
|
+
? findPlan.hasAmbiguousBranchTail
|
|
1841
|
+
: findPlan.hasAmbiguousBranchTail;
|
|
1842
|
+
|
|
1843
|
+
if (routeUnits.length >= findUnits.length) {
|
|
1844
|
+
const lastStart = routeUnits.length - findUnits.length;
|
|
1845
|
+
|
|
1846
|
+
for (let start = 0; start <= lastStart; start++) {
|
|
1847
|
+
let exact = start === 0 && routeUnits.length === findUnits.length;
|
|
1848
|
+
const windowMatch = matchUnitWindow(
|
|
1849
|
+
findUnits,
|
|
1850
|
+
0,
|
|
1851
|
+
routeUnits,
|
|
1852
|
+
start,
|
|
1853
|
+
findUnits.length,
|
|
1854
|
+
groupMatchCache,
|
|
1855
|
+
context,
|
|
1856
|
+
parent
|
|
1857
|
+
);
|
|
1858
|
+
let matched = windowMatch.matched;
|
|
1859
|
+
exact &&= windowMatch.exact;
|
|
1860
|
+
|
|
1861
|
+
if (!matched) {
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
if (
|
|
1866
|
+
parent
|
|
1867
|
+
&& findUnits.length === 1
|
|
1868
|
+
&& routeUnits.length > 1
|
|
1869
|
+
&& routeUnits[start]?.kind === 'group'
|
|
1870
|
+
&& isNode(routeUnits[start]!.node, N.Ampersand)
|
|
1871
|
+
) {
|
|
1872
|
+
continue;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
if (singleFindGroup) {
|
|
1876
|
+
const targetUnit = routeUnits[start]!;
|
|
1877
|
+
if (targetUnit.kind === 'group') {
|
|
1878
|
+
const groupLocations = collectGroupMatchLocations(
|
|
1879
|
+
targetUnit.node,
|
|
1880
|
+
singleFindGroup.group,
|
|
1881
|
+
parent,
|
|
1882
|
+
evalContext
|
|
1883
|
+
);
|
|
1884
|
+
for (let i = 0; i < groupLocations.length; i++) {
|
|
1885
|
+
const location = groupLocations[i]!;
|
|
1886
|
+
result.matches.push({
|
|
1887
|
+
...location,
|
|
1888
|
+
exact: exact && location.exact,
|
|
1889
|
+
consumedTarget: !!location.consumedTarget,
|
|
1890
|
+
ampersandCrossings: cloneAmpersandCrossings(location.ampersandCrossings)
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
continue;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
if (!windowMatch.exact && suppressAmbiguousBranchLocations) {
|
|
1898
|
+
result.partialMatch = true;
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
result.matches.push({
|
|
1903
|
+
startIndex: routeUnits[start]!.index,
|
|
1904
|
+
endIndex: routeUnits[start + findUnits.length - 1]!.index,
|
|
1905
|
+
containingNode: routePlan.selector as Node,
|
|
1906
|
+
exact,
|
|
1907
|
+
consumedTarget: !!exact
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
pushMidRouteAmpersandMatches(result.matches, routePlan);
|
|
1913
|
+
|
|
1914
|
+
if (parentPlan) {
|
|
1915
|
+
pushMatches(result.matches, matchParentRoute(routePlan, parentPlan).matches);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
return finalizeMatchState(result, context.evalContext);
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1921
|
+
const matchTargetPlan = (
|
|
1922
|
+
plan: MatchPlan,
|
|
1923
|
+
parentPlan?: MatchPlan
|
|
1924
|
+
): SelectorMatchState => {
|
|
1925
|
+
if (plan.kind === 'route') {
|
|
1926
|
+
return matchTargetRoute(plan, parentPlan);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
const result = emptySelectorMatchState();
|
|
1930
|
+
for (let i = 0; i < plan.alternates.length; i++) {
|
|
1931
|
+
const match = matchTargetPlan(plan.alternates[i]!, parentPlan);
|
|
1932
|
+
pushMatches(result.matches, match.matches);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
return finalizeMatchState(result, context.evalContext);
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
const parentPlan = parent ? getSelectorMatchPlan(parent, undefined, evalContext) : undefined;
|
|
1939
|
+
const result = matchTargetPlan(getSelectorMatchPlan(target, parent, evalContext), parentPlan);
|
|
1940
|
+
pushNestedPseudoMatches(find, target, result.matches, context);
|
|
1941
|
+
if (!result.partialMatch && result.matches.length === 0) {
|
|
1942
|
+
pushNestedBranchMatches(find, target, result.matches, context, parent);
|
|
1943
|
+
}
|
|
1944
|
+
return finalizeMatchState(result, context.evalContext);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
function selectorMatchInternal(
|
|
1948
|
+
find: Selector,
|
|
1949
|
+
target: Selector,
|
|
1950
|
+
parent: Selector | undefined,
|
|
1951
|
+
context: SelectorMatchContext
|
|
1952
|
+
): SelectorMatchState {
|
|
1953
|
+
if (parent) {
|
|
1954
|
+
return selectorMatchUncached(find, target, parent, context);
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
let findCache = context.pairCache.get(find);
|
|
1958
|
+
if (!findCache) {
|
|
1959
|
+
findCache = new WeakMap<Selector, SelectorMatchState>();
|
|
1960
|
+
context.pairCache.set(find, findCache);
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
const cached = findCache.get(target);
|
|
1964
|
+
if (cached) {
|
|
1965
|
+
return cached;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
const result = selectorMatchUncached(find, target, undefined, context);
|
|
1969
|
+
findCache.set(target, result);
|
|
1970
|
+
return result;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
/**
|
|
1974
|
+
* Finds all occurrences of `find` inside `target`.
|
|
1975
|
+
*
|
|
1976
|
+
* Matching is ordered at the route level, unordered only inside a single
|
|
1977
|
+
* compound-like position, and branches only at `SelectorList` alternatives.
|
|
1978
|
+
*
|
|
1979
|
+
* When `parent` is provided, it acts like an implicit prefix context joined to
|
|
1980
|
+
* `target` by a descendant combinator. Parent traversal is attempted only when
|
|
1981
|
+
* a match reaches a left-side ampersand boundary with partial-but-incomplete
|
|
1982
|
+
* progress; matches that exist only inside the parent do not get added on
|
|
1983
|
+
* their own.
|
|
1984
|
+
*
|
|
1985
|
+
* Complex selector branches inside `:is(...)` or selector lists are treated as
|
|
1986
|
+
* alternate branch routes. Matching may succeed inside one branch, but the
|
|
1987
|
+
* outer route cannot continue leftward through that branch unless that branch
|
|
1988
|
+
* itself was consumed end-to-end.
|
|
1989
|
+
*
|
|
1990
|
+
* The matcher uses normalized `valueOf()` and selector key-set fast paths only
|
|
1991
|
+
* as cheap equality / rejection signals. They are not shape-preserving and
|
|
1992
|
+
* should not be used by callers to infer the structural rewrite shape of a
|
|
1993
|
+
* successful match.
|
|
1994
|
+
*/
|
|
1995
|
+
export function selectorMatch(
|
|
1996
|
+
find: Selector,
|
|
1997
|
+
target: Selector,
|
|
1998
|
+
parent?: Selector,
|
|
1999
|
+
evalContext?: EvalContext
|
|
2000
|
+
): SelectorMatchState {
|
|
2001
|
+
return selectorMatchInternal(find, target, parent, {
|
|
2002
|
+
pairCache: new WeakMap(),
|
|
2003
|
+
evalContext
|
|
2004
|
+
});
|
|
2005
|
+
}
|