<commit_before>'use strict';

import { L10nError } from '../../lib/errors';

export function serializeEntries(lang, langEntries, sourceEntries) {
  const errors = [];
  const entries = Object.keys(sourceEntries).map(id => {
    const sourceEntry = sourceEntries[id];
    const langEntry = langEntries[id];

    if (!langEntry) {
      errors.push(new L10nError(
        '"' + id + '"' + ' not found in ' + lang.code, id, lang));
      return serializeEntry(sourceEntry, id);
    }

    if (!areEntityStructsEqual(sourceEntry, langEntry)) {
      errors.push(new L10nError(
        '"' + id + '"' + ' is malformed in ' + lang.code, id, lang));
      return serializeEntry(sourceEntry, id);
    }

    return serializeEntry(langEntry, id);
  });

  return [errors, entries];
}

function serializeEntry(entry, id) {
  if (typeof entry === 'string') {
    return { $i: id, $v: entry };
  }

  const node = {
    $i: id,
  };

  if (entry.value !== null) {
    node.$v = entry.value;
  }

  if (entry.index !== null) {
    node.$x = entry.index;
  }

  for (let key in entry.attrs) {
    node[key] = serializeAttribute(entry.attrs[key]);
  }

  return node;
}

function serializeAttribute(attr) {
  if (typeof attr === 'string') {
    return attr;
  }

  const node = {};

  if (attr.value !== null) {
    node.$v = attr.value;
  }

  if (attr.index !== null) {
    node.$x = attr.index;
  }

  return node;
}

function resolvesToString(entity) {
  return typeof entity === 'string' || // a simple string
    Array.isArray(entity.value) ||     // a complex string
    entity.index !== null;             // a dict with an index
}

function areEntityStructsEqual(entity1, entity2) {
  if (resolvesToString(entity1) && resolvesToString(entity2)) {
    return true;
  }

  const keys1 = Object.keys(entity1);
  const keys2 = Object.keys(entity2);

  if (keys1.length !== keys2.length) {
    return false;<commit_msg>gaiabuild serialize runtime entries<commit_after>'use strict';

import { L10nError } from '../../lib/errors';

export function serializeEntries(lang, langEntries, sourceEntries) {
  const errors = [];
  const entries = Object.create(null);

  for (let id in sourceEntries) {
    const sourceEntry = sourceEntries[id];
    const langEntry = langEntries[id];

    if (!langEntry) {
      errors.push(new L10nError(
        '"' + id + '"' + ' not found in ' + lang.code, id, lang));
      entries[id] = sourceEntry;
      continue;
    }

    if (!areEntityStructsEqual(sourceEntry, langEntry)) {
      errors.push(new L10nError(
        '"' + id + '"' + ' is malformed in ' + lang.code, id, lang));
      entries[id] = sourceEntry;
      continue;
    }

    entries[id] = langEntry;
  }

  return [errors, entries];
}

function resolvesToString(entity) {
  return typeof entity === 'string' ||  // a simple string
    typeof entity.value === 'string' || // a simple string, entity with attrs
    Array.isArray(entity.value) ||      // a complex string
    entity.index !== null;              // a dict with an index
}

function areEntityStructsEqual(entity1, entity2) {
  if (resolvesToString(entity1) && resolvesToString(entity2)) {
    return true;
  }

  const keys1 = Object.keys(entity1);
  const keys2 = Object.keys(entity2);

  if (keys1.length !== keys2.length) {
    return false;
{'fraction_non_alphanumeric': 0.14779969650986344, 'fraction_numerical': 0.009711684370257967, 'mean_word_length': 2.952038369304556, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, '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': '5507815', 'n_tokens_mistral': 1072, 'n_tokens_neox': 1047, 'n_words': 264}