findBestRuleSetAndAdd method

void findBestRuleSetAndAdd(
  1. Selector selector,
  2. CSSRule rule
)

Implementation

void findBestRuleSetAndAdd(Selector selector, CSSRule rule) {
  // Enforce CSS rule: a pseudo-element must be the last simple selector
  // in a compound selector. If any simple selector appears after a
  // pseudo-element, the selector is invalid and must not match.
  final List<SimpleSelectorSequence> seqs = selector.simpleSelectorSequences;
  for (int i = 0; i < seqs.length; i++) {
    final s = seqs[i].simpleSelector;
    if (s is PseudoElementSelector || s is PseudoElementFunctionSelector) {
      if (i != seqs.length - 1) {
        // Invalid selector like `P:first-line.three`; drop this rule.
        return;
      }
      break;
    }
  }

  // Choose the best indexing key from the RIGHTMOST COMPOUND only, with
  // priority: id > class > attribute > tag > legacy pseudo > universal.
  // Build the rightmost compound by walking from the end until a combinator
  // boundary.
  final List<SimpleSelector> rightmost = <SimpleSelector>[];
  for (final seq in seqs.reversed) {
    rightmost.add(seq.simpleSelector);
    if (seq.combinator != TokenKind.COMBINATOR_NONE) break;
  }

  String? id;
  String? className;
  String? attributeName;
  String? tagName;
  String? legacyPseudo;

  // Scan for best key across the compound (no early break on pseudo).
  for (final simple in rightmost) {
    if (simple is IdSelector) {
      id ??= simple.name;
    } else if (simple is ClassSelector) {
      className ??= simple.name;
    } else if (simple is AttributeSelector) {
      attributeName ??= simple.name;
    } else if (simple is ElementSelector && !simple.isWildcard) {
      tagName ??= simple.name;
    } else if (simple is PseudoClassSelector || simple is PseudoElementSelector) {
      final name = (simple as dynamic).name as String; // both have name
      if (_isLegacyPsuedoClass(name)) legacyPseudo ??= name;
    } else if (simple is PseudoClassFunctionSelector) {
      // ignore function pseudos for bucketing
    } else if (simple is NegationSelector) {
      // ignore :not() for bucketing; prefer other keys if present
    }
  }

  void insertRule(String key, CSSRule rule, CSSMap map) {
    List<CSSRule>? rules = map[key] ?? [];
    rules.add(rule);
    map[key] = rules;
  }

  if (id != null && id.isNotEmpty) {
    insertRule(id, rule, idRules);
    return;
  }

  if (className != null && className.isNotEmpty) {
    insertRule(className, rule, classRules);
    return;
  }

  if (attributeName != null && attributeName.isNotEmpty) {
    insertRule(attributeName.toUpperCase(), rule, attributeRules);
    return;
  }

  if (tagName != null && tagName.isNotEmpty) {
    insertRule(tagName.toUpperCase(), rule, tagRules);
    return;
  }

  if (legacyPseudo != null) {
    pseudoRules.add(rule);
    return;
  }

  universalRules.add(rule);
}