<commit_before>import postcss from "postcss";

export interface AtRules {
  fontFace: Array<{
    name: string;
    node: postcss.AtRule;
  }>;

  keyframes: postcss.AtRule[];
}

export interface RawContent {
  extension: string;
  raw: string;
}

export interface RawCSS {
  raw: string;
}

export interface ExtractorResultDetailed {
  attributes: {
    names: string[];
    values: string[];
  };
  classes: string[];
  ids: string[];
  tags: string[];
  undetermined: string[];
}

function mergeSets(into: Set<string>, from?: string[] | Set<string>) {
  if (from) {
    from.forEach(into.add, into);
  }
}

export class ExtractorResultSets {
  private undetermined = new Set<string>();
  private attrNames = new Set<string>();
  private attrValues = new Set<string>();
  private classes = new Set<string>();
  private ids = new Set<string>();
  private tags = new Set<string>();

  constructor(er: ExtractorResult) {
    this.merge(er);
  }

  merge(that: ExtractorResult | ExtractorResultSets) {
    if (Array.isArray(that)) {
      mergeSets(this.undetermined, that);
    } else if (that instanceof ExtractorResultSets) {
      mergeSets(this.undetermined, that.undetermined);
      mergeSets(this.attrNames, that.attrNames);
      mergeSets(this.attrValues, that.attrValues);
      mergeSets(this.classes, that.classes);
      mergeSets(this.ids, that.ids);
      mergeSets(this.tags, that.tags);
    } else {
      // ExtractorResultDetailed:
      mergeSets(this.undetermined, that.undetermined);
      if (that.attributes) {
        mergeSets(this.attrNames, that.attributes.names);
        mergeSets(this.attrValues, that.attributes.values);
      }
      mergeSets(this.classes, that.classes);
      mergeSets(this.ids, that.ids);
      mergeSets(this.tags, that.tags);
    }
    return this;
  }

  hasAttrName(name: string): boolean {
    return this.attrNames.has(name) || this.undetermined.has(name);
  }

  private someAttrValue(predicate: (value: string) => boolean): boolean {
    const forEachFn = (value: string) => {
      if (predicate(value)) throw true;
    };
    try {
      // The only way to break early from a Set.prototype.forEach
      // loop is to throw from the callback function.
      this.attrValues.forEach(forEachFn);
      this.undetermined.forEach(forEachFn);
    } catch (result) {
      return result;
    }
    return false;
  }

  hasAttrPrefix(prefix: string): boolean {
    return this.someAttrValue((value) => value.startsWith(prefix));
  }

  hasAttrSuffix(suffix: string): boolean {
    return this.someAttrValue((value) => value.endsWith(suffix));
  }

  hasAttrSubstr(substr: string): boolean {
    return this.someAttrValue((value) => value.includes(substr));
  }<commit_msg>Use for-of loops to implement someAttrValue.<commit_after>import postcss from "postcss";

export interface AtRules {
  fontFace: Array<{
    name: string;
    node: postcss.AtRule;
  }>;

  keyframes: postcss.AtRule[];
}

export interface RawContent {
  extension: string;
  raw: string;
}

export interface RawCSS {
  raw: string;
}

export interface ExtractorResultDetailed {
  attributes: {
    names: string[];
    values: string[];
  };
  classes: string[];
  ids: string[];
  tags: string[];
  undetermined: string[];
}

function mergeSets(into: Set<string>, from?: string[] | Set<string>) {
  if (from) {
    from.forEach(into.add, into);
  }
}

export class ExtractorResultSets {
  private undetermined = new Set<string>();
  private attrNames = new Set<string>();
  private attrValues = new Set<string>();
  private classes = new Set<string>();
  private ids = new Set<string>();
  private tags = new Set<string>();

  constructor(er: ExtractorResult) {
    this.merge(er);
  }

  merge(that: ExtractorResult | ExtractorResultSets) {
    if (Array.isArray(that)) {
      mergeSets(this.undetermined, that);
    } else if (that instanceof ExtractorResultSets) {
      mergeSets(this.undetermined, that.undetermined);
      mergeSets(this.attrNames, that.attrNames);
      mergeSets(this.attrValues, that.attrValues);
      mergeSets(this.classes, that.classes);
      mergeSets(this.ids, that.ids);
      mergeSets(this.tags, that.tags);
    } else {
      // ExtractorResultDetailed:
      mergeSets(this.undetermined, that.undetermined);
      if (that.attributes) {
        mergeSets(this.attrNames, that.attributes.names);
        mergeSets(this.attrValues, that.attributes.values);
      }
      mergeSets(this.classes, that.classes);
      mergeSets(this.ids, that.ids);
      mergeSets(this.tags, that.tags);
    }
    return this;
  }

  hasAttrName(name: string): boolean {
    return this.attrNames.has(name) || this.undetermined.has(name);
  }

  private someAttrValue(predicate: (value: string) => boolean): boolean {
    for (const val of this.attrValues) {
      if (predicate(val)) return true;
    }
    for (const val of this.undetermined) {
      if (predicate(val)) return true;
    }
    return false;
  }

  hasAttrPrefix(prefix: string): boolean {
    return this.someAttrValue((value) => value.startsWith(prefix));
  }

  hasAttrSuffix(suffix: string): boolean {
    return this.someAttrValue((value) => value.endsWith(suffix));
  }

  hasAttrSubstr(substr: string): boolean {
    return this.someAttrValue((value) => value.includes(substr));
  }
{'fraction_non_alphanumeric': 0.13270499528746466, 'fraction_numerical': 0.0, 'mean_word_length': 3.538922155688623, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3087894', 'n_tokens_mistral': 1687, 'n_tokens_neox': 1633, 'n_words': 369}