Repository: angular/angular
File: packages/compiler/src/shadow_css.ts (TypeScript)

Code before:
        for (const hostSelector of hostSelectorArray) {
          if (!hostSelector) break;
          const convertedSelector =
            _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors;
          convertedSelectors.push(convertedSelector);
        }
        return convertedSelectors.join(',');
      } else {
        return _polyfillHostNoCombinator + otherSelectors;
      }
    });
  }

  /**
   * Generator function that splits a string on top-level commas (commas that are not inside parentheses).
   * Yields each part of the string between top-level commas. Terminates if an extra closing paren is found.
   *
   * @param text The string to split
   */
  private *_splitOnTopLevelCommas(text: string): Generator<string> {
    const length = text.length;
    let parens = 0;
    let prev = 0;

    for (let i = 0; i < length; i++) {
      const char = text[i];

      if (char === '(') {
        parens++;
      } else if (char === ')') {
        parens--;
        if (parens < 0) {
          // Found an extra closing paren. Assume we want the list terminated here
          yield text.slice(prev, i);
          return;
        }
      } else if (char === ',' && parens === 0) {
        // Found a top-level comma, yield the current chunk
        yield text.slice(prev, i);
        prev = i + 1;
      }
    }

    // Yield the final chunk
    yield text.slice(prev);
  }

  /*
   * convert a rule like :host-context(.foo) > .bar { }
   *
   * to

Review comment (suggestion):
nit: since we only check if the `char` is a specific symbol, we could also use `.charCodeAt(i) === someCode` instead. We have constants for common char codes in `chars.ts`. I don't feel strongly about changing it.

Code after:
        const hostSelectorArray = hostSelectors.split(',').map((p) => p.trim());
        for (const hostSelector of hostSelectorArray) {
          if (!hostSelector) break;
          const convertedSelector =
            _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors;
          convertedSelectors.push(convertedSelector);
        }
        return convertedSelectors.join(',');
      } else {
        return _polyfillHostNoCombinator + otherSelectors;
      }
    });
  }

  /**
   * Generator function that splits a string on top-level commas (commas that are not inside parentheses).
   * Yields each part of the string between top-level commas. Terminates if an extra closing paren is found.
   *
   * @param text The string to split
   */
  private *_splitOnTopLevelCommas(text: string): Generator<string> {
    const length = text.length;
    let parens = 0;
    let prev = 0;

    for (let i = 0; i < length; i++) {
      const charCode = text.charCodeAt(i);

      if (charCode === chars.$LPAREN) {
        parens++;
      } else if (charCode === chars.$RPAREN) {
        parens--;
        if (parens < 0) {
          // Found an extra closing paren. Assume we want the list terminated here
          yield text.slice(prev, i);
          return;
        }
      } else if (charCode === chars.$COMMA && parens === 0) {
        // Found a top-level comma, yield the current chunk
        yield text.slice(prev, i);
        prev = i + 1;
      }
    }

    // Yield the final chunk
    yield text.slice(prev);
  }

  /*
   * convert a rule like :host-context(.foo) > .bar { }
   *