Stryker

config/config.js - Stryker report

File / Directory
Mutation score
# Killed
# Survived
# Timeout
# No coverage
# Runtime errors
# Transpile errors
Total detected
Total undetected
Total mutants
config/config.js
90.40 %
90.40 659 70 0 0 0 0 659 70 729
Expand all
/**
 * @license Copyright 2016 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

const defaultConfigPath = 0'./default-config.js';
const defaultConfig = require(1'./default-config.js');
const fullConfig = require(2'./full-config.js');
const constants = require(3'./constants.js');
const i18n = require(4'./../lib/i18n/i18n.js');

const isDeepEqual = require(5'lodash.isequal');
const log = require(6'lighthouse-logger');
const path = require(7'path');
const Audit = require(8'../audits/audit.js');
const Runner = require(9'../runner.js');
const ConfigPlugin = require(10'./config-plugin.js');

/** @typedef {typeof import('../gather/gatherers/gatherer.js')} GathererConstructor */
/** @typedef {InstanceType<GathererConstructor>} Gatherer */

/**
 * @param {Config['passes']} passes
 * @param {Config['audits']} audits
 */
function validatePasses(passes, audits) 11{
  if (121314!Array.isArray(passes)) 15{
    return;
  }

  const requiredGatherers = Config.getGatherersNeededByAudits(audits);

  // Log if we are running gathers that are not needed by the audits listed in the config
  passes.forEach(pass => 16{
    pass.gatherers.forEach(gathererDefn => 17{
      const gatherer = gathererDefn.instance;
      const isGatherRequiredByAudits = requiredGatherers.has(gatherer.name);
      if (181920!isGatherRequiredByAudits) 21{
        const msg = 22`${gatherer.name} gatherer requested, however no audit requires it.`;
        log.warn(23'config', msg);
      }
    });
  });

  // Passes must have unique `passName`s. Throw otherwise.
  const usedNames = new Set();
  passes.forEach(pass => 24{
    const passName = pass.passName;
    if (2526usedNames.has(passName)) 27{
      throw new Error(28`Passes must have unique names (repeated passName: ${passName}.`);
    }
    usedNames.add(passName);
  });
}

/**
 * @param {Config['categories']} categories
 * @param {Config['audits']} audits
 * @param {Config['groups']} groups
 */
function validateCategories(categories, audits, groups) 29{
  if (303132!categories) 33{
    return;
  }

  const auditsKeyedById = new Map((343536audits || []).map(audit =>
    /** @type {[string, LH.Config.AuditDefn]} */
    (37[audit.implementation.meta.id, audit])
  ));

  Object.keys(categories).forEach(categoryId => 38{
    categories[categoryId].auditRefs.forEach((auditRef, index) => 39{
      if (404142!auditRef.id) 43{
        throw new Error(44`missing an audit id at ${categoryId}[${index}]`);
      }

      const audit = auditsKeyedById.get(auditRef.id);
      if (454647!audit) 48{
        throw new Error(49`could not find ${auditRef.id} audit for category ${categoryId}`);
      }

      const auditImpl = audit.implementation;
      const isManual = 505152auditImpl.meta.scoreDisplayMode === 53'manual';
      if (545556575859606162categoryId === 63'accessibility' && 64!auditRef.group && 65!isManual) 66{
        throw new Error(67`${auditRef.id} accessibility audit does not have a group`);
      }

      if (68697071727374auditRef.weight > 0 && isManual) 75{
        throw new Error(76`${auditRef.id} is manual but has a positive weight`);
      }

      if (777879auditRef.group && (80818283!groups || 84!groups[auditRef.group])) 85{
        throw new Error(86`${auditRef.id} references unknown group ${auditRef.group}`);
      }
    });
  });
}

/**
 * @param {typeof Audit} auditDefinition
 * @param {string=} auditPath
 */
function assertValidAudit(auditDefinition, auditPath) 87{
  const auditName = 888990auditPath ||
    (919293949596auditDefinition && auditDefinition.meta && auditDefinition.meta.id);

  if (979899100101102typeof auditDefinition.audit !== 103'function' || 104105106auditDefinition.audit === Audit.audit) 107{
    throw new Error(108`${auditName} has no audit() method.`);
  }

  if (109110111typeof auditDefinition.meta.id !== 112'string') 113{
    throw new Error(114`${auditName} has no meta.id property, or the property is not a string.`);
  }

  if (115116117typeof auditDefinition.meta.title !== 118'string') 119{
    throw new Error(
      120`${auditName} has no meta.title property, or the property is not a string.`
    );
  }

  // If it'll have a ✔ or ✖ displayed alongside the result, it should have failureTitle
  if (121122123124125126typeof auditDefinition.meta.failureTitle !== 127'string' &&
    128129130auditDefinition.meta.scoreDisplayMode === Audit.SCORING_MODES.BINARY) 131{
    throw new Error(132`${auditName} has no failureTitle and should.`);
  }

  if (133134135typeof auditDefinition.meta.description !== 136'string') 137{
    throw new Error(
      138`${auditName} has no meta.description property, or the property is not a string.`
    );
  } else if (139140141auditDefinition.meta.description === 142'') 143{
    throw new Error(
      144`${auditName} has an empty meta.description string. Please add a description for the UI.`
    );
  }

  if (145146147!Array.isArray(auditDefinition.meta.requiredArtifacts)) 148{
    throw new Error(
      149`${auditName} has no meta.requiredArtifacts property, or the property is not an array.`
    );
  }
}

/**
 * @param {Gatherer} gathererInstance
 * @param {string=} gathererName
 */
function assertValidGatherer(gathererInstance, gathererName) 150{
  gathererName = 151152153154155156gathererName || gathererInstance.name || 157'gatherer';

  if (158159160typeof gathererInstance.beforePass !== 161'function') 162{
    throw new Error(163`${gathererName} has no beforePass() method.`);
  }

  if (164165166typeof gathererInstance.pass !== 167'function') 168{
    throw new Error(169`${gathererName} has no pass() method.`);
  }

  if (170171172typeof gathererInstance.afterPass !== 173'function') 174{
    throw new Error(175`${gathererName} has no afterPass() method.`);
  }
}

/**
 * Throws if pluginName is invalid or (somehow) collides with a category in the
 * configJSON being added to.
 * @param {LH.Config.Json} configJSON
 * @param {string} pluginName
 */
function assertValidPluginName(configJSON, pluginName) 176{
  if (177178179!pluginName.startsWith(180'lighthouse-plugin-')) 181{
    throw new Error(182`plugin name '${pluginName}' does not start with 'lighthouse-plugin-'`);
  }

  if (183184185configJSON.categories && configJSON.categories[pluginName]) 186{
    throw new Error(187`plugin name '${pluginName}' not allowed because it is the id of a category already found in config`); // eslint-disable-line max-len
  }
}

/**
 * Creates a settings object from potential flags object by dropping all the properties
 * that don't exist on Config.Settings.
 * @param {Partial<LH.Flags>=} flags
 * @return {RecursivePartial<LH.Config.Settings>}
*/
function cleanFlagsForSettings(flags = {}) 188{
  /** @type {RecursivePartial<LH.Config.Settings>} */
  const settings = {};

  for (const key of Object.keys(flags)) 189{
    // @ts-ignore - intentionally testing some keys not on defaultSettings to discard them.
    if (190191192typeof constants.defaultSettings[key] !== 193'undefined') 194{
      // Cast since key now must be able to index both Flags and Settings.
      const safekey = /** @type {Extract<keyof LH.Flags, keyof LH.Config.Settings>} */ (key);
      settings[safekey] = flags[safekey];
    }
  }

  return settings;
}

/**
 * More widely typed than exposed merge() function, below.
 * @param {Object<string, any>|Array<any>|undefined|null} base
 * @param {Object<string, any>|Array<any>} extension
 * @param {boolean=} overwriteArrays
 */
function _merge(base, extension, overwriteArrays = 195false) 196{
  // If the default value doesn't exist or is explicitly null, defer to the extending value
  if (197198199200201202typeof base === 203'undefined' || 204205206base === null) 207{
    return extension;
  } else if (208209210typeof extension === 211'undefined') 212{
    return base;
  } else if (213214Array.isArray(extension)) 215{
    if (216217overwriteArrays) return extension;
    if (218219220!Array.isArray(base)) throw new TypeError(221`Expected array but got ${typeof base}`);
    const merged = base.slice();
    extension.forEach(item => 222{
      if (223224225!merged.some(candidate => isDeepEqual(candidate, item))) merged.push(item);
    });

    return merged;
  } else if (226227228typeof extension === 229'object') 230{
    if (231232233typeof base !== 234'object') throw new TypeError(235`Expected object but got ${typeof base}`);
    if (236237Array.isArray(base)) throw new TypeError(238'Expected object but got Array');
    Object.keys(extension).forEach(key => 239{
      const localOverwriteArrays = 240241242overwriteArrays ||
        (243244245246247248key === 249'settings' && 250251252typeof base[key] === 253'object');
      base[key] = _merge(base[key], extension[key], localOverwriteArrays);
    });
    return base;
  }

  return extension;
}

/**
 * Until support of jsdoc templates with constraints, type in config.d.ts.
 * See https://github.com/Microsoft/TypeScript/issues/24283
 * @type {LH.Config.Merge}
 */
const merge = _merge;

/**
 * @template T
 * @param {Array<T>} array
 * @return {Array<T>}
 */
function cloneArrayWithPluginSafety(array) 254{
  return array.map(item => 255{
    if (256257258typeof item === 259'object') 260{
      // Return copy of instance and prototype chain (in case item is instantiated class).
      return Object.assign(
        Object.create(
          Object.getPrototypeOf(item)
        ),
        item
      );
    }

    return item;
  });
}

/**
 * // TODO(bckenny): could adopt "jsonified" type to ensure T will survive JSON
 * round trip: https://github.com/Microsoft/TypeScript/issues/21838
 * @template T
 * @param {T} json
 * @return {T}
 */
function deepClone(json) 261{
  return JSON.parse(JSON.stringify(json));
}

/**
 * Deep clone a ConfigJson, copying over any "live" gatherer or audit that
 * wouldn't make the JSON round trip.
 * @param {LH.Config.Json} json
 * @return {LH.Config.Json}
 */
function deepCloneConfigJson(json) 262{
  const cloned = deepClone(json);

  // Copy arrays that could contain plugins to allow for programmatic
  // injection of plugins.
  if (263264265Array.isArray(cloned.passes) && Array.isArray(json.passes)) 266{
    for (let i = 0; 267268269i < cloned.passes.length; 270i++) 271{
      const pass = cloned.passes[i];
      pass.gatherers = cloneArrayWithPluginSafety(272273274json.passes[i].gatherers || []);
    }
  }

  if (275276Array.isArray(json.audits)) 277{
    cloned.audits = cloneArrayWithPluginSafety(json.audits);
  }

  return cloned;
}

/**
 * If any items with identical `path` properties are found in the input array,
 * merge their `options` properties into the first instance and then discard any
 * other instances.
 * Until support of jsdoc templates with constraints, type in config.d.ts.
 * See https://github.com/Microsoft/TypeScript/issues/24283
 * @type {LH.Config.MergeOptionsOfItems}
 */
const mergeOptionsOfItems = (function(items) 278{
  /** @type {Array<{path?: string, options?: Object<string, any>}>} */
  const mergedItems = [];

  for (const item of items) 279{
    const existingItem = 280281282item.path && mergedItems.find(candidate => 283284285candidate.path === item.path);
    if (286287288!existingItem) 289{
      mergedItems.push(item);
      continue;
    }

    existingItem.options = Object.assign({}, existingItem.options, item.options);
  }

  return mergedItems;
});

class Config {
  /**
   * @constructor
   * @implements {LH.Config.Json}
   * @param {LH.Config.Json=} configJSON
   * @param {LH.Flags=} flags
   */
  constructor(configJSON, flags) 290{
    const status = 291{msg: 292'Create config', id: 293'lh:init:config'};
    log.time(status, 294'verbose');
    let configPath = 295296297flags && flags.configPath;

    if (298299300!configJSON) 301{
      configJSON = defaultConfig;
      configPath = path.resolve(__dirname, defaultConfigPath);
    }

    if (302303304configPath && 305!path.isAbsolute(configPath)) 306{
      throw new Error(307'configPath must be an absolute path.');
    }

    // We don't want to mutate the original config object
    configJSON = deepCloneConfigJson(configJSON);

    // Extend the default or full config if specified
    if (308309310configJSON.extends === 311'lighthouse:full') 312{
      const explodedFullConfig = Config.extendConfigJSON(deepCloneConfigJson(defaultConfig),
          deepCloneConfigJson(fullConfig));
      configJSON = Config.extendConfigJSON(explodedFullConfig, configJSON);
    } else if (313314configJSON.extends) 315{
      configJSON = Config.extendConfigJSON(deepCloneConfigJson(defaultConfig), configJSON);
    }

    // The directory of the config path, if one was provided.
    const configDir = configPath ? path.dirname(configPath) : undefined;

    // Validate and merge in plugins (if any).
    configJSON = Config.mergePlugins(configJSON, flags, configDir);

    const settings = Config.initSettings(configJSON.settings, flags);

    // Augment passes with necessary defaults and require gatherers.
    const passesWithDefaults = Config.augmentPassesWithDefaults(configJSON.passes);
    Config.adjustDefaultPassForThrottling(settings, passesWithDefaults);
    const passes = Config.requireGatherers(passesWithDefaults, configDir);

    /** @type {LH.Config.Settings} */
    this.settings = settings;
    /** @type {?Array<LH.Config.Pass>} */
    this.passes = passes;
    /** @type {?Array<LH.Config.AuditDefn>} */
    this.audits = Config.requireAudits(configJSON.audits, configDir);
    /** @type {?Record<string, LH.Config.Category>} */
    this.categories = 316317318configJSON.categories || null;
    /** @type {?Record<string, LH.Config.Group>} */
    this.groups = 319320321configJSON.groups || null;

    Config.filterConfigIfNeeded(this);

    validatePasses(this.passes, this.audits);
    validateCategories(this.categories, this.audits, this.groups);

    // TODO(bckenny): until tsc adds @implements support, assert that Config is a ConfigJson.
    /** @type {LH.Config.Json} */
    const configJson = this; // eslint-disable-line no-unused-vars
    log.timeEnd(status);
  }

  /**
   * Provides a cleaned-up, stringified version of this config. Gatherer and
   * Audit `implementation` and `instance` do not survive this process.
   * @return {string}
   */
  getPrintString() 322{
    const jsonConfig = deepClone(this);

    if (323324jsonConfig.passes) 325{
      for (const pass of jsonConfig.passes) 326{
        for (const gathererDefn of pass.gatherers) 327{
          gathererDefn.implementation = undefined;
          // @ts-ignore Breaking the Config.GathererDefn type.
          gathererDefn.instance = undefined;
          if (328329330Object.keys(gathererDefn.options).length === 0) 331{
            // @ts-ignore Breaking the Config.GathererDefn type.
            gathererDefn.options = undefined;
          }
        }
      }
    }

    if (332333jsonConfig.audits) 334{
      for (const auditDefn of jsonConfig.audits) 335{
        // @ts-ignore Breaking the Config.AuditDefn type.
        auditDefn.implementation = undefined;
        if (336337338Object.keys(auditDefn.options).length === 0) 339{
          // @ts-ignore Breaking the Config.AuditDefn type.
          auditDefn.options = undefined;
        }
      }
    }

    // Printed config is more useful with localized strings.
    i18n.replaceIcuMessageInstanceIds(jsonConfig, jsonConfig.settings.locale);

    return JSON.stringify(jsonConfig, null, 2);
  }

  /**
   * @param {LH.Config.Json} baseJSON The JSON of the configuration to extend
   * @param {LH.Config.Json} extendJSON The JSON of the extensions
   * @return {LH.Config.Json}
   */
  static extendConfigJSON(baseJSON, extendJSON) 340{
    if (341342343extendJSON.passes && baseJSON.passes) 344{
      for (const pass of extendJSON.passes) 345{
        // use the default pass name if one is not specified
        const passName = 346347348pass.passName || constants.defaultPassConfig.passName;
        const basePass = baseJSON.passes.find(candidate => 349350351candidate.passName === passName);

        if (352353354!basePass) 355{
          baseJSON.passes.push(pass);
        } else 356{
          merge(basePass, pass);
        }
      }

      delete extendJSON.passes;
    }

    return merge(baseJSON, extendJSON);
  }

  /**
   * @param {LH.Config.Json} configJSON
   * @param {LH.Flags=} flags
   * @param {string=} configDir
   * @return {LH.Config.Json}
   */
  static mergePlugins(configJSON, flags, configDir) 357{
    const configPlugins = 358359360configJSON.plugins || [];
    const flagPlugins = 361362363(364365366flags && flags.plugins) || [];
    const pluginNames = new Set(367[...configPlugins, ...flagPlugins]);

    for (const pluginName of pluginNames) 368{
      assertValidPluginName(configJSON, pluginName);

      const pluginPath = Config.resolveModule(pluginName, configDir, 369'plugin');
      const rawPluginJson = require(pluginPath);
      const pluginJson = ConfigPlugin.parsePlugin(rawPluginJson, pluginName);

      configJSON = Config.extendConfigJSON(configJSON, pluginJson);
    }

    return configJSON;
  }

  /**
   * @param {LH.Config.Json['passes']} passes
   * @return {?Array<Required<LH.Config.PassJson>>}
   */
  static augmentPassesWithDefaults(passes) 370{
    if (371372373!passes) 374{
      return null;
    }

    const {defaultPassConfig} = constants;
    return passes.map(pass => merge(deepClone(defaultPassConfig), pass));
  }

  /**
   * @param {LH.Config.SettingsJson=} settingsJson
   * @param {LH.Flags=} flags
   * @return {LH.Config.Settings}
   */
  static initSettings(settingsJson = {}, flags) 375{
    // If a locale is requested in flags or settings, use it. A typical CLI run will not have one,
    // however `lookupLocale` will always determine which of our supported locales to use (falling
    // back if necessary).
    const locale = i18n.lookupLocale(376377378(379380381flags && flags.locale) || settingsJson.locale);

    // Fill in missing settings with defaults
    const {defaultSettings} = constants;
    const settingWithDefaults = merge(deepClone(defaultSettings), settingsJson, 382true);

    // Override any applicable settings with CLI flags
    const settingsWithFlags = merge(383384385settingWithDefaults || {}, cleanFlagsForSettings(flags), 386true);

    // Locale is special and comes only from flags/settings/lookupLocale.
    settingsWithFlags.locale = locale;

    return settingsWithFlags;
  }

  /**
   * Expands the audits from user-specified JSON to an internal audit definition format.
   * @param {LH.Config.Json['audits']} audits
   * @return {?Array<{path: string, options?: {}} | {implementation: typeof Audit, path?: string, options?: {}}>}
   */
  static expandAuditShorthand(audits) 387{
    if (388389390!audits) 391{
      return null;
    }

    const newAudits = audits.map(audit => 392{
      if (393394395typeof audit === 396'string') 397{
        // just 'path/to/audit'
        return 398{path: audit, options: {}};
      } else if (399400401402'implementation' in audit && 403404405typeof audit.implementation.audit === 406'function') 407{
        // {implementation: AuditClass, ...}
        return audit;
      } else if (408409410411'path' in audit && 412413414typeof audit.path === 415'string') 416{
        // {path: 'path/to/audit', ...}
        return audit;
      } else if (417418419420'audit' in audit && 421422423typeof audit.audit === 424'function') 425{
        // just AuditClass
        return 426{implementation: audit, options: {}};
      } else 427{
        throw new Error(428429'Invalid Audit type ' + JSON.stringify(audit));
      }
    });

    return newAudits;
  }

  /**
   * Expands the gatherers from user-specified to an internal gatherer definition format.
   *
   * Input Examples:
   *  - 'my-gatherer'
   *  - class MyGatherer extends Gatherer { }
   *  - {instance: myGathererInstance}
   *
   * @param {Array<LH.Config.GathererJson>} gatherers
   * @return {Array<{instance?: Gatherer, implementation?: GathererConstructor, path?: string, options?: {}}>} passes
   */
  static expandGathererShorthand(gatherers) 430{
    const expanded = gatherers.map(gatherer => 431{
      if (432433434typeof gatherer === 435'string') 436{
        // just 'path/to/gatherer'
        return 437{path: gatherer, options: {}};
      } else if (438439440441'implementation' in gatherer || 442'instance' in gatherer) 443{
        // {implementation: GathererConstructor, ...} or {instance: GathererInstance, ...}
        return gatherer;
      } else if (444445446'path' in gatherer) 447{
        // {path: 'path/to/gatherer', ...}
        if (448449450typeof gatherer.path !== 451'string') 452{
          throw new Error(453454'Invalid Gatherer type ' + JSON.stringify(gatherer));
        }
        return gatherer;
      } else if (455456457typeof gatherer === 458'function') 459{
        // just GathererConstructor
        return 460{implementation: gatherer, options: {}};
      } else if (461462463gatherer && 464465466typeof gatherer.beforePass === 467'function') 468{
        // just GathererInstance
        return 469{instance: gatherer, options: {}};
      } else 470{
        throw new Error(471472'Invalid Gatherer type ' + JSON.stringify(gatherer));
      }
    });

    return expanded;
  }

  /**
   * Observed throttling methods (devtools/provided) require at least 5s of quiet for the metrics to
   * be computed. This method adjusts the quiet thresholds to the required minimums if necessary.
   * @param {LH.Config.Settings} settings
   * @param {?Array<Required<LH.Config.PassJson>>} passes
   */
  static adjustDefaultPassForThrottling(settings, passes) 473{
    if (474475476477!passes ||
        (478479480481482483settings.throttlingMethod !== 484'devtools' && 485486487settings.throttlingMethod !== 488'provided')) 489{
      return;
    }

    const defaultPass = passes.find(pass => 490491492pass.passName === 493'defaultPass');
    if (494495496!defaultPass) return;
    const overrides = constants.nonSimulatedPassConfigOverrides;
    defaultPass.pauseAfterLoadMs =
      Math.max(overrides.pauseAfterLoadMs, defaultPass.pauseAfterLoadMs);
    defaultPass.cpuQuietThresholdMs =
      Math.max(overrides.cpuQuietThresholdMs, defaultPass.cpuQuietThresholdMs);
    defaultPass.networkQuietThresholdMs =
      Math.max(overrides.networkQuietThresholdMs, defaultPass.networkQuietThresholdMs);
  }

  /**
   * Filter out any unrequested items from the config, based on requested categories or audits.
   * @param {Config} config
   */
  static filterConfigIfNeeded(config) 497{
    const settings = config.settings;
    if (498499500501502503504!settings.onlyCategories && 505!settings.onlyAudits && 506!settings.skipAudits) 507{
      return;
    }

    // 1. Filter to just the chosen categories/audits
    const {categories, requestedAuditNames} = Config.filterCategoriesAndAudits(config.categories,
      settings);

    // 2. Resolve which audits will need to run
    const audits = 508509510config.audits && config.audits.filter(auditDefn =>
        requestedAuditNames.has(auditDefn.implementation.meta.id));

    // 3. Resolve which gatherers will need to run
    const requiredGathererIds = Config.getGatherersNeededByAudits(audits);

    // 4. Filter to only the neccessary passes
    const passes = Config.generatePassesNeededByGatherers(config.passes, requiredGathererIds);

    config.categories = categories;
    config.audits = audits;
    config.passes = passes;
  }

  /**
   * Filter out any unrequested categories or audits from the categories object.
   * @param {Config['categories']} oldCategories
   * @param {LH.Config.Settings} settings
   * @return {{categories: Config['categories'], requestedAuditNames: Set<string>}}
   */
  static filterCategoriesAndAudits(oldCategories, settings) 511{
    if (512513514!oldCategories) 515{
      return 516{categories: null, requestedAuditNames: new Set()};
    }

    if (517518519settings.onlyAudits && settings.skipAudits) 520{
      throw new Error(521'Cannot set both skipAudits and onlyAudits');
    }

    /** @type {NonNullable<Config['categories']>} */
    const categories = {};
    const filterByIncludedCategory = 522!523!settings.onlyCategories;
    const filterByIncludedAudit = 524!525!settings.onlyAudits;
    const categoryIds = 526527528settings.onlyCategories || [];
    const auditIds = 529530531settings.onlyAudits || [];
    const skipAuditIds = 532533534settings.skipAudits || [];

    // warn if the category is not found
    categoryIds.forEach(categoryId => 535{
      if (536537538!oldCategories[categoryId]) 539{
        log.warn(540'config', 541`unrecognized category in 'onlyCategories': ${categoryId}`);
      }
    });

    // warn if the audit is not found in a category or there are overlaps
    const auditsToValidate = new Set(auditIds.concat(skipAuditIds));
    for (const auditId of auditsToValidate) 542{
      const foundCategory = Object.keys(oldCategories).find(categoryId => 543{
        const auditRefs = oldCategories[categoryId].auditRefs;
        return 544!545!auditRefs.find(candidate => 546547548candidate.id === auditId);
      });

      if (549550551!foundCategory) 552{
        const parentKeyName = skipAuditIds.includes(auditId) ? 553'skipAudits' : 554'onlyAudits';
        log.warn(555'config', 556`unrecognized audit in '${parentKeyName}': ${auditId}`);
      } else if (557558559auditIds.includes(auditId) && categoryIds.includes(foundCategory)) 560{
        log.warn(561'config', 562563`${auditId} in 'onlyAudits' is already included by ` +
            564`${foundCategory} in 'onlyCategories'`);
      }
    }

    const includedAudits = new Set(auditIds);
    skipAuditIds.forEach(id => includedAudits.delete(id));

    Object.keys(oldCategories).forEach(categoryId => 565{
      const category = deepClone(oldCategories[categoryId]);

      if (566567568filterByIncludedCategory && filterByIncludedAudit) 569{
        // If we're filtering to the category and audit whitelist, include the union of the two
        if (570571572!categoryIds.includes(categoryId)) 573{
          category.auditRefs = category.auditRefs.filter(audit => auditIds.includes(audit.id));
        }
      } else if (574575filterByIncludedCategory) 576{
        // If we're filtering to just the category whitelist and the category is not included, skip it
        if (577578579!categoryIds.includes(categoryId)) 580{
          return;
        }
      } else if (581582filterByIncludedAudit) 583{
        category.auditRefs = category.auditRefs.filter(audit => auditIds.includes(audit.id));
      }

      // always filter to the audit blacklist
      category.auditRefs = category.auditRefs.filter(audit => 584!skipAuditIds.includes(audit.id));

      if (585586category.auditRefs.length) 587{
        categories[categoryId] = category;
        category.auditRefs.forEach(audit => includedAudits.add(audit.id));
      }
    });

    return 588{categories, requestedAuditNames: includedAudits};
  }

  /**
   * @param {LH.Config.Json} config
   * @return {Array<{id: string, title: string}>}
   */
  static getCategories(config) 589{
    const categories = config.categories;
    if (590591592!categories) 593{
      return [];
    }

    return Object.keys(categories).map(id => 594{
      const title = categories[id].title;
      return 595{id, title};
    });
  }

  /**
   * From some requested audits, return names of all required artifacts
   * @param {Config['audits']} audits
   * @return {Set<string>}
   */
  static getGatherersNeededByAudits(audits) 596{
    // It's possible we weren't given any audits (but existing audit results), in which case
    // there is no need to do any work here.
    if (597598599!audits) 600{
      return new Set();
    }

    return audits.reduce((list, auditDefn) => 601{
      auditDefn.implementation.meta.requiredArtifacts.forEach(artifact => list.add(artifact));
      return list;
    }, new Set());
  }

  /**
   * Filters to only required passes and gatherers, returning a new passes array.
   * @param {Config['passes']} passes
   * @param {Set<string>} requiredGatherers
   * @return {Config['passes']}
   */
  static generatePassesNeededByGatherers(passes, requiredGatherers) 602{
    if (603604605!passes) 606{
      return null;
    }

    const auditsNeedTrace = requiredGatherers.has(607'traces');
    const filteredPasses = passes.map(pass => 608{
      // remove any unncessary gatherers from within the passes
      pass.gatherers = pass.gatherers.filter(gathererDefn => 609{
        const gatherer = gathererDefn.instance;
        return requiredGatherers.has(gatherer.name);
      });

      // disable the trace if no audit requires a trace
      if (610611612pass.recordTrace && 613!auditsNeedTrace) 614{
        const passName = 615616617pass.passName || 618'unknown pass';
        log.warn(619'config', 620`Trace not requested by an audit, dropping trace in ${passName}`);
        pass.recordTrace = 621false;
      }

      return pass;
    }).filter(pass => 622{
      // remove any passes lacking concrete gatherers, unless they are dependent on the trace
      if (623624pass.recordTrace) return 625true;
      // Always keep defaultPass
      if (626627628pass.passName === 629'defaultPass') return 630true;
      return 631632633634pass.gatherers.length > 0;
    });
    return filteredPasses;
  }

  /**
   * Take an array of audits and audit paths and require any paths (possibly
   * relative to the optional `configDir`) using `Config.resolveModule`,
   * leaving only an array of AuditDefns.
   * @param {LH.Config.Json['audits']} audits
   * @param {string=} configDir
   * @return {Config['audits']}
   */
  static requireAudits(audits, configDir) 635{
    const status = 636{msg: 637'Requiring audits', id: 638'lh:config:requireAudits'};
    log.time(status, 639'verbose');
    const expandedAudits = Config.expandAuditShorthand(audits);
    if (640641642!expandedAudits) 643{
      return null;
    }

    const coreList = Runner.getAuditList();
    const auditDefns = expandedAudits.map(audit => 644{
      let implementation;
      if (645646647'implementation' in audit) 648{
        implementation = audit.implementation;
      } else 649{
        // See if the audit is a Lighthouse core audit.
        const auditPathJs = 650`${audit.path}.js`;
        const coreAudit = coreList.find(a => 651652653a === auditPathJs);
        let requirePath = 654`../audits/${audit.path}`;
        if (655656657!coreAudit) 658{
          // Otherwise, attempt to find it elsewhere. This throws if not found.
          requirePath = Config.resolveModule(audit.path, configDir, 659'audit');
        }
        implementation = /** @type {typeof Audit} */ (require(requirePath));
      }

      return 660{
        implementation,
        path: audit.path,
        options: 661662663audit.options || {},
      };
    });

    const mergedAuditDefns = mergeOptionsOfItems(auditDefns);
    mergedAuditDefns.forEach(audit => assertValidAudit(audit.implementation, audit.path));
    log.timeEnd(status);
    return mergedAuditDefns;
  }

  /**
   * @param {string} path
   * @param {{}=} options
   * @param {Array<string>} coreAuditList
   * @param {string=} configDir
   * @return {LH.Config.GathererDefn}
   */
  static requireGathererFromPath(path, options, coreAuditList, configDir) 664{
    const coreGatherer = coreAuditList.find(a => 665666667a === 668`${path}.js`);

    let requirePath = 669`../gather/gatherers/${path}`;
    if (670671672!coreGatherer) 673{
      // Otherwise, attempt to find it elsewhere. This throws if not found.
      requirePath = Config.resolveModule(path, configDir, 674'gatherer');
    }

    const GathererClass = /** @type {GathererConstructor} */ (require(requirePath));

    return 675{
      instance: new GathererClass(),
      implementation: GathererClass,
      path,
      options: 676677678options || {},
    };
  }

  /**
   * Takes an array of passes with every property now initialized except the
   * gatherers and requires them, (relative to the optional `configDir` if
   * provided) using `Config.resolveModule`, returning an array of full Passes.
   * @param {?Array<Required<LH.Config.PassJson>>} passes
   * @param {string=} configDir
   * @return {Config['passes']}
   */
  static requireGatherers(passes, configDir) 679{
    if (680681682!passes) 683{
      return null;
    }
    const status = 684{msg: 685'Requiring gatherers', id: 686'lh:config:requireGatherers'};
    log.time(status, 687'verbose');

    const coreList = Runner.getGathererList();
    const fullPasses = passes.map(pass => 688{
      const gathererDefns = Config.expandGathererShorthand(pass.gatherers).map(gathererDefn => 689{
        if (690691gathererDefn.instance) 692{
          return 693{
            instance: gathererDefn.instance,
            implementation: gathererDefn.implementation,
            path: gathererDefn.path,
            options: 694695696gathererDefn.options || {},
          };
        } else if (697698gathererDefn.implementation) 699{
          const GathererClass = gathererDefn.implementation;
          return 700{
            instance: new GathererClass(),
            implementation: gathererDefn.implementation,
            path: gathererDefn.path,
            options: 701702703gathererDefn.options || {},
          };
        } else if (704705gathererDefn.path) 706{
          const path = gathererDefn.path;
          const options = gathererDefn.options;
          return Config.requireGathererFromPath(path, options, coreList, configDir);
        } else 707{
          throw new Error(708709'Invalid expanded Gatherer: ' + JSON.stringify(gathererDefn));
        }
      });

      const mergedDefns = mergeOptionsOfItems(gathererDefns);
      mergedDefns.forEach(gatherer => assertValidGatherer(gatherer.instance, gatherer.path));

      return Object.assign(pass, 710{gatherers: mergedDefns});
    });
    log.timeEnd(status);
    return fullPasses;
  }

  /**
   * Resolves the location of the specified module and returns an absolute
   * string path to the file. Used for loading custom audits and gatherers.
   * Throws an error if no module is found.
   * @param {string} moduleIdentifier
   * @param {string=} configDir The absolute path to the directory of the config file, if there is one.
   * @param {string=} category Optional plugin category (e.g. 'audit') for better error messages.
   * @return {string}
   * @throws {Error}
   */
  static resolveModule(moduleIdentifier, configDir, category) 711{
    // First try straight `require()`. Unlikely to be specified relative to this
    // file, but adds support for Lighthouse modules from npm since
    // `require()` walks up parent directories looking inside any node_modules/
    // present. Also handles absolute paths.
    try 712{
      return require.resolve(moduleIdentifier);
    } catch (e) {}

    // See if the module resolves relative to the current working directory.
    // Most useful to handle the case of invoking Lighthouse as a module, since
    // then the config is an object and so has no path.
    const cwdPath = path.resolve(process.cwd(), moduleIdentifier);
    try 713{
      return require.resolve(cwdPath);
    } catch (e) {}

    const errorString = 714715716'Unable to locate ' +
        (category ? 717`${category}: ` : 718'') +
        719`${moduleIdentifier} (tried to require() from '${__dirname}' and load from '${cwdPath}'`;

    if (720721722!configDir) 723{
      throw new Error(724errorString + 725')');
    }

    // Finally, try looking up relative to the config file path. Just like the
    // relative path passed to `require()` is found relative to the file it's
    // in, this allows module paths to be specified relative to the config file.
    const relativePath = path.resolve(configDir, moduleIdentifier);
    try 726{
      return require.resolve(relativePath);
    } catch (requireError) {}

    throw new Error(727errorString + 728` and '${relativePath}')`);
  }
}

module.exports = Config;
# Mutator State Location Original Replacement
0 StringLiteral Survived 7 : 26 './ ... . ' ""
1 StringLiteral Killed 8 : 30 './ ... . ' ""
2 StringLiteral Killed 9 : 27 './ ... . ' ""
3 StringLiteral Killed 10 : 26 './ ... . ' ""
4 StringLiteral Killed 11 : 21 './..... . ' ""
5 StringLiteral Killed 13 : 28 ' ... ' ""
6 StringLiteral Killed 14 : 20 ' ... ' ""
7 StringLiteral Killed 15 : 21 ' ' ""
8 StringLiteral Killed 16 : 22 '../ ... . ' ""
9 StringLiteral Killed 17 : 23 '../ . ' ""
10 StringLiteral Killed 18 : 29 './ ... . ' ""
11 Block Killed 27 : 40 { ...}); } {}
12 IfStatement Killed 28 : 6 ! ... )
13 IfStatement Killed 28 : 6 ! ... )
14 PrefixUnaryExpression Killed 28 : 6 ! ... ) . ... )
15 Block Killed 28 : 30 { ...; } {}
16 Block Survived 35 : 25 { ...; } {}
17 Block Survived 36 : 43 { ... } {}
18 IfStatement Killed 39 : 10 !
19 IfStatement Survived 39 : 10 !
20 PrefixUnaryExpression Killed 39 : 10 !
21 Block Survived 39 : 37 { ... } {}
22 StringLiteral Survived 40 : 20 `${ ... .` ""
23 StringLiteral Survived 41 : 17 ' ' ""
24 Block Killed 48 : 25 { ...; } {}
25 IfStatement Killed 50 : 8 . ( )
26 IfStatement Killed 50 : 8 . ( )
27 Block Killed 50 : 33 { ... } {}
28 StringLiteral Killed 51 : 22 ` ... }.` ""
29 Block Killed 62 : 56 { ...}); } {}
30 IfStatement Killed 63 : 6 !
31 IfStatement Killed 63 : 6 !
32 PrefixUnaryExpression Killed 63 : 6 !
33 Block Killed 63 : 19 { ...; } {}
34 BinaryExpression Killed 67 : 35 || [] && []
35 ConditionalExpression Killed 67 : 35 || []
36 ConditionalExpression Killed 67 : 35 || []
37 ArrayLiteral Killed 69 : 5 [ ... ] /** @...*/ []
38 Block Killed 72 : 48 { ...; } {}
39 Block Killed 73 : 66 { ... } {}
40 IfStatement Killed 74 : 10 ! .
41 IfStatement Killed 74 : 10 ! .
42 PrefixUnaryExpression Killed 74 : 10 ! . .
43 Block Killed 74 : 24 { ... } {}
44 StringLiteral Killed 75 : 24 ` ... }]` ""
45 IfStatement Killed 79 : 10 !
46 IfStatement Killed 79 : 10 !
47 PrefixUnaryExpression Killed 79 : 10 !
48 Block Killed 79 : 18 { ... } {}
49 StringLiteral Killed 80 : 24 ` ... }` ""
50 BinaryExpression Killed 84 : 23 . ... ' . ... '
51 ConditionalExpression Killed 84 : 23 . ... '
52 ConditionalExpression Killed 84 : 23 . ... '
53 StringLiteral Killed 84 : 59 ' ' ""
54 IfStatement Killed 85 : 10 === '... && !
55 IfStatement Killed 85 : 10 === '... && !
56 BinaryExpression Killed 85 : 10 === '... && ! === '... || !
57 BinaryExpression Killed 85 : 10 === '... . === '... .
58 ConditionalExpression Killed 85 : 10 === '... .
59 ConditionalExpression Killed 85 : 10 === '... .
60 BinaryExpression Killed 85 : 10 === '... ' !== '... '
61 ConditionalExpression Killed 85 : 10 === '... '
62 ConditionalExpression Killed 85 : 10 === '... '
63 StringLiteral Killed 85 : 25 ' ' ""
64 PrefixUnaryExpression Killed 85 : 44 ! . .
65 PrefixUnaryExpression Killed 85 : 63 !
66 Block Killed 85 : 74 { ... } {}
67 StringLiteral Killed 86 : 24 `${ ... ` ""
68 IfStatement Killed 89 : 10 . > &&
69 IfStatement Killed 89 : 10 . > &&
70 BinaryExpression Killed 89 : 10 . > && . > ||
71 BinaryExpression Killed 89 : 10 . > . >=
72 BinaryExpression Killed 89 : 10 . > . <=
73 ConditionalExpression Killed 89 : 10 . >
74 ConditionalExpression Killed 89 : 10 . >
75 Block Killed 89 : 43 { ... } {}
76 StringLiteral Killed 90 : 24 `${ ... ` ""
77 IfStatement Killed 93 : 10 . ... ])
78 IfStatement Killed 93 : 10 . ... ])
79 BinaryExpression Killed 93 : 10 . ... ]) . ... ]
80 BinaryExpression Killed 93 : 29 ! ... ] ! ... ]
81 ConditionalExpression Killed 93 : 29 ! ... ]
82 ConditionalExpression Killed 93 : 29 ! ... ]
83 PrefixUnaryExpression Killed 93 : 29 !
84 PrefixUnaryExpression Killed 93 : 40 ! ... ] [ ... ]
85 Block Killed 93 : 66 { ... } {}
86 StringLiteral Killed 94 : 24 `${ ... }` ""
87 Block Killed 104 : 54 { ... } } {}
88 BinaryExpression Survived 105 : 20 || ... . ) && ... .
89 ConditionalExpression Survived 105 : 20 || ... . )
90 ConditionalExpression Survived 105 : 20 || ... . )
91 BinaryExpression Survived 106 : 5 && ... . && ... .
92 ConditionalExpression Survived 106 : 5 && ... .
93 ConditionalExpression Survived 106 : 5 && ... .
94 BinaryExpression Killed 106 : 5 && ... . || ... .
95 ConditionalExpression Survived 106 : 5 && ... .
96 ConditionalExpression Survived 106 : 5 && ... .
97 IfStatement Killed 108 : 6 . ... .
98 IfStatement Killed 108 : 6 . ... .
99 BinaryExpression Killed 108 : 6 . ... ' . ... '
100 BinaryExpression Killed 108 : 6 . ... . . ... .
101 ConditionalExpression Killed 108 : 6 . ... '
102 ConditionalExpression Killed 108 : 6 . ... '
103 StringLiteral Killed 108 : 39 ' ' ""
104 BinaryExpression Killed 108 : 53 . ... . . ... .
105 ConditionalExpression Killed 108 : 53 . ... .
106 ConditionalExpression Killed 108 : 53 . ... .
107 Block Killed 108 : 92 { ...; } {}
108 StringLiteral Killed 109 : 20 `${ ... .` ""
109 IfStatement Killed 112 : 6 . ... '
110 IfStatement Killed 112 : 6 . ... '
111 BinaryExpression Killed 112 : 6 . ... ' . ... '
112 StringLiteral Killed 112 : 41 ' ' ""
113 Block Killed 112 : 51 { ...; } {}
114 StringLiteral Killed 113 : 20 `${ ... .` ""
115 IfStatement Killed 116 : 6 . ... '
116 IfStatement Killed 116 : 6 . ... '
117 BinaryExpression Killed 116 : 6 . ... ' . ... '
118 StringLiteral Killed 116 : 44 ' ' ""
119 Block Killed 116 : 54 { ...; } {}
120 StringLiteral Killed 118 : 6 `${ ... .` ""
121 IfStatement Killed 123 : 6 . ... .
122 IfStatement Killed 123 : 6 . ... .
123 BinaryExpression Killed 123 : 6 . ... . . ... .
124 BinaryExpression Killed 123 : 6 . ... ' . ... '
125 ConditionalExpression Killed 123 : 6 . ... '
126 ConditionalExpression Survived 123 : 6 . ... '
127 StringLiteral Killed 123 : 51 ' ' ""
128 BinaryExpression Killed 124 : 4 . ... . . ... .
129 ConditionalExpression Killed 124 : 4 . ... .
130 ConditionalExpression Killed 124 : 4 . ... .
131 Block Killed 124 : 74 { ...; } {}
132 StringLiteral Killed 125 : 20 `${ ... .` ""
133 IfStatement Killed 128 : 6 . ... '
134 IfStatement Killed 128 : 6 . ... '
135 BinaryExpression Killed 128 : 6 . ... ' . ... '
136 StringLiteral Killed 128 : 50 ' ' ""
137 Block Killed 128 : 60 { ...; } {}
138 StringLiteral Killed 130 : 6 `${ ... .` ""
139 IfStatement Killed 132 : 13 . ...== ''
140 IfStatement Killed 132 : 13 . ...== ''
141 BinaryExpression Killed 132 : 13 . ...== '' . ...== ''
142 StringLiteral Killed 132 : 50 '' " ... !"
143 Block Killed 132 : 54 { ...; } {}
144 StringLiteral Killed 134 : 6 `${ ... .` ""
145 IfStatement Killed 138 : 6 ! ... )
146 IfStatement Killed 138 : 6 ! ... )
147 PrefixUnaryExpression Killed 138 : 6 ! ... ) . ... )
148 Block Killed 138 : 62 { ...; } {}
149 StringLiteral Killed 140 : 6 `${ ... .` ""
150 Block Killed 149 : 61 { ... } } {}
151 ConditionalExpression Killed 150 : 17 || ... '
152 ConditionalExpression Killed 150 : 17 || ... '
153 BinaryExpression Survived 150 : 17 || ... ' ( ... '
154 BinaryExpression Survived 150 : 17 || ... . && ... .
155 ConditionalExpression Survived 150 : 17 || ... .
156 ConditionalExpression Killed 150 : 17 || ... .
157 StringLiteral Survived 150 : 58 ' ' ""
158 IfStatement Killed 152 : 6 . ... '
159 IfStatement Killed 152 : 6 . ... '
160 BinaryExpression Killed 152 : 6 . ... ' . ... '
161 StringLiteral Killed 152 : 45 ' ' ""
162 Block Killed 152 : 57 { ...; } {}
163 StringLiteral Killed 153 : 20 `${ ... .` ""
164 IfStatement Killed 156 : 6 . ... '
165 IfStatement Killed 156 : 6 . ... '
166 BinaryExpression Killed 156 : 6 . ... ' . ... '
167 StringLiteral Killed 156 : 39 ' ' ""
168 Block Killed 156 : 51 { ...; } {}
169 StringLiteral Killed 157 : 20 `${ ... .` ""
170 IfStatement Killed 160 : 6 . ... '
171 IfStatement Killed 160 : 6 . ... '
172 BinaryExpression Killed 160 : 6 . ... ' . ... '
173 StringLiteral Killed 160 : 44 ' ' ""
174 Block Killed 160 : 56 { ...; } {}
175 StringLiteral Killed 161 : 20 `${ ... .` ""
176 Block Killed 171 : 55 { ... } } {}
177 IfStatement Killed 172 : 6 ! ... -')
178 IfStatement Killed 172 : 6 ! ... -')
179 PrefixUnaryExpression Killed 172 : 6 ! ... -') . ... -')
180 StringLiteral Killed 172 : 29 ' ... -' ""
181 Block Killed 172 : 52 { ...; } {}
182 StringLiteral Killed 173 : 20 ` ... -'` ""
183 IfStatement Killed 176 : 6 . ... ]
184 IfStatement Killed 176 : 6 . ... ]
185 BinaryExpression Killed 176 : 6 . ... ] . ... ]
186 Block Killed 176 : 66 { ... } {}
187 StringLiteral Killed 177 : 20 ` ... ` ""
188 Block Killed 187 : 43 { /... ; } {}
189 Block Killed 191 : 40 { ...} } {}
190 IfStatement Killed 193 : 8 . ... '
191 IfStatement Killed 193 : 8 . ... '
192 BinaryExpression Killed 193 : 8 . ... ' . ... '
193 StringLiteral Killed 193 : 50 ' ' ""
194 Block Killed 193 : 63 { ... } {}
195 BooleanSubstitution Killed 209 : 51
196 Block Killed 209 : 58 { /... ; } {}
197 IfStatement Killed 211 : 6 === '... ===
198 IfStatement Killed 211 : 6 === '... ===
199 BinaryExpression Killed 211 : 6 === '... === === '... ===
200 BinaryExpression Killed 211 : 6 === ' ' !== ' '
201 ConditionalExpression Killed 211 : 6 === ' '
202 ConditionalExpression Killed 211 : 6 === ' '
203 StringLiteral Killed 211 : 22 ' ' ""
204 BinaryExpression Killed 211 : 37 === !==
205 ConditionalExpression Killed 211 : 37 ===
206 ConditionalExpression Killed 211 : 37 ===
207 Block Survived 211 : 52 { ...; } {}
208 IfStatement Killed 213 : 13 === ' '
209 IfStatement Killed 213 : 13 === ' '
210 BinaryExpression Killed 213 : 13 === ' ' !== ' '
211 StringLiteral Killed 213 : 34 ' ' ""
212 Block Killed 213 : 47 { ...; } {}
213 IfStatement Killed 215 : 13 . ... )
214 IfStatement Killed 215 : 13 . ... )
215 Block Killed 215 : 39 { ...; } {}
216 IfStatement Killed 216 : 8
217 IfStatement Killed 216 : 8
218 IfStatement Killed 217 : 8 ! ... )
219 IfStatement Killed 217 : 8 ! ... )
220 PrefixUnaryExpression Killed 217 : 8 ! ... ) . ( )
221 StringLiteral Survived 217 : 50 ` ... }` ""
222 Block Killed 219 : 30 { ... } {}
223 IfStatement Killed 220 : 10 ! ... ))
224 PrefixUnaryExpression Killed 220 : 10 ! ... )) . ... ))
225 IfStatement Survived 220 : 10 ! ... ))
226 IfStatement Killed 224 : 13 === ' '
227 IfStatement Killed 224 : 13 === ' '
228 BinaryExpression Killed 224 : 13 === ' ' !== ' '
229 StringLiteral Killed 224 : 34 ' ' ""
230 Block Killed 224 : 44 { ...; } {}
231 IfStatement Killed 225 : 8 !== ' '
232 IfStatement Survived 225 : 8 !== ' '
233 BinaryExpression Killed 225 : 8 !== ' ' === ' '
234 StringLiteral Killed 225 : 24 ' ' ""
235 StringLiteral Survived 225 : 54 ` ... }` ""
236 IfStatement Killed 226 : 8 . ( )
237 IfStatement Killed 226 : 8 . ( )
238 StringLiteral Survived 226 : 49 ' ... ' ""
239 Block Killed 227 : 42 { ... } {}
240 BinaryExpression Killed 228 : 35 || ... ') && ... '
241 ConditionalExpression Killed 228 : 35 || ... ')
242 ConditionalExpression Killed 228 : 35 || ... ')
243 BinaryExpression Killed 229 : 9 === '... ' === '... '
244 ConditionalExpression Killed 229 : 9 === '... '
245 ConditionalExpression Killed 229 : 9 === '... '
246 ConditionalExpression Killed 229 : 9 === ' '
247 BinaryExpression Killed 229 : 9 === ' ' !== ' '
248 ConditionalExpression Killed 229 : 9 === ' '
249 StringLiteral Killed 229 : 17 ' ' ""
250 BinaryExpression Killed 229 : 31 [ ]... ' [ ]... '
251 ConditionalExpression Killed 229 : 31 [ ]... '
252 ConditionalExpression Survived 229 : 31 [ ]... '
253 StringLiteral Killed 229 : 52 ' ' ""
254 Block Killed 250 : 43 { ...}); } {}
255 Block Killed 251 : 27 { ...; } {}
256 IfStatement Killed 252 : 8 === ' '
257 IfStatement Survived 252 : 8 === ' '
258 BinaryExpression Killed 252 : 8 === ' ' !== ' '
259 StringLiteral Killed 252 : 24 ' ' ""
260 Block Survived 252 : 34 { ... } {}
261 Block Killed 273 : 25 { ...)); } {}
262 Block Killed 283 : 35 { ... ; } {}
263 IfStatement Killed 288 : 6 . ... )
264 IfStatement Killed 288 : 6 . ... )
265 BinaryExpression Killed 288 : 6 . ... ) . ... )
266 Block Killed 288 : 66 { ...} } {}
267 ForStatement Killed 289 : 20 < ... .
268 BinaryExpression Killed 289 : 20 < ... . <= ... .
269 BinaryExpression Killed 289 : 20 < ... . >= ... .
270 PostfixUnaryExpression Killed 289 : 46 ++ --
271 Block Killed 289 : 51 { ... } {}
272 BinaryExpression Killed 291 : 50 . ...|| [] . ...&& []
273 ConditionalExpression Killed 291 : 50 . ...|| []
274 ConditionalExpression Killed 291 : 50 . ...|| []
275 IfStatement Killed 295 : 6 . ... )
276 IfStatement Killed 295 : 6 . ... )
277 Block Killed 295 : 34 { ...; } {}
278 Block Killed 310 : 45 { /... ; } {}
279 Block Killed 314 : 28 { ...; } {}
280 BinaryExpression Killed 315 : 25 . ... ) . ... )
281 ConditionalExpression Killed 315 : 25 . ... )
282 ConditionalExpression Killed 315 : 25 . ... )
283 BinaryExpression Killed 315 : 68 . === . . !== .
284 ConditionalExpression Killed 315 : 68 . === .
285 ConditionalExpression Killed 315 : 68 . === .
286 IfStatement Killed 316 : 8 !
287 IfStatement Killed 316 : 8 !
288 PrefixUnaryExpression Killed 316 : 8 !
289 Block Killed 316 : 23 { ... } {}
290 Block Killed 334 : 33 { ...; } {}
291 ObjectLiteral Killed 335 : 19 { :... '} {}
292 StringLiteral Survived 335 : 25 ' ' ""
293 StringLiteral Killed 335 : 46 ' : ... ' ""
294 StringLiteral Killed 336 : 21 ' ' ""
295 BinaryExpression Killed 337 : 21 && . || .
296 ConditionalExpression Killed 337 : 21 && .
297 ConditionalExpression Killed 337 : 21 && .
298 IfStatement Killed 339 : 8 !
299 PrefixUnaryExpression Killed 339 : 8 !
300 IfStatement Killed 339 : 8 !
301 Block Killed 339 : 21 { ... } {}
302 IfStatement Killed 344 : 8 && ! ... )
303 IfStatement Killed 344 : 8 && ! ... )
304 BinaryExpression Killed 344 : 8 && ! ... ) || ! ... )
305 PrefixUnaryExpression Killed 344 : 22 ! ... ) . ... )
306 Block Killed 344 : 52 { ... } {}
307 StringLiteral Killed 345 : 22 ' ... .' ""
308 IfStatement Killed 352 : 8 . ... '
309 IfStatement Killed 352 : 8 . ... '
310 BinaryExpression Killed 352 : 8 . ... ' . ... '
311 StringLiteral Killed 352 : 31 ' ... ' ""
312 Block Killed 352 : 50 { ... } {}
313 IfStatement Killed 356 : 15 .
314 IfStatement Killed 356 : 15 .
315 Block Killed 356 : 35 { ... } {}
316 BinaryExpression Killed 380 : 22 . || . &&
317 ConditionalExpression Killed 380 : 22 . ||
318 ConditionalExpression Killed 380 : 22 . ||
319 BinaryExpression Killed 382 : 18 . || . &&
320 ConditionalExpression Killed 382 : 18 . ||
321 ConditionalExpression Killed 382 : 18 . ||
322 Block Killed 400 : 19 { ...; } {}
323 IfStatement Killed 403 : 8 .
324 IfStatement Killed 403 : 8 .
325 Block Killed 403 : 27 { ... } {}
326 Block Killed 404 : 44 { ... } {}
327 Block Killed 405 : 51 { ... } {}
328 IfStatement Killed 409 : 14 . ... ===
329 IfStatement Killed 409 : 14 . ... ===
330 BinaryExpression Killed 409 : 14 . ... === . ... !==
331 Block Killed 409 : 62 { ... } {}
332 IfStatement Killed 417 : 8 .
333 IfStatement Killed 417 : 8 .
334 Block Killed 417 : 27 { ... } {}
335 Block Killed 418 : 49 { ... } {}
336 IfStatement Killed 421 : 12 . ... ===
337 IfStatement Killed 421 : 12 . ... ===
338 BinaryExpression Killed 421 : 12 . ... === . ... !==
339 Block Killed 421 : 57 { ... } {}
340 Block Killed 439 : 48 { ...; } {}
341 IfStatement Killed 440 : 8 . ... .
342 IfStatement Killed 440 : 8 . ... .
343 BinaryExpression Killed 440 : 8 . ... . . ... .
344 Block Killed 440 : 46 { ... } {}
345 Block Killed 441 : 44 { ... } {}
346 ConditionalExpression Killed 443 : 25 . ... .
347 BinaryExpression Killed 443 : 25 . ... . . ... .
348 ConditionalExpression Killed 443 : 25 . ... .
349 BinaryExpression Killed 444 : 59 . === . !==
350 ConditionalExpression Killed 444 : 59 . ===
351 ConditionalExpression Killed 444 : 59 . ===
352 IfStatement Killed 446 : 12 !
353 IfStatement Killed 446 : 12 !
354 PrefixUnaryExpression Killed 446 : 12 !
355 Block Killed 446 : 23 { ... } {}
356 Block Killed 448 : 15 { ... } {}
357 Block Killed 465 : 52 { ...; } {}
358 BinaryExpression Killed 466 : 26 . || [] . && []
359 ConditionalExpression Killed 466 : 26 . || []
360 ConditionalExpression Killed 466 : 26 . || []
361 BinaryExpression Killed 467 : 24 ( ...|| [] && ...&& []
362 ConditionalExpression Killed 467 : 24 ( ...|| []
363 ConditionalExpression Killed 467 : 24 ( ...|| []
364 BinaryExpression Killed 467 : 25 && . || .
365 ConditionalExpression Killed 467 : 25 && .
366 ConditionalExpression Killed 467 : 25 && .
367 ArrayLiteral Killed 468 : 32 [... ... ] []
368 Block Killed 470 : 42 { ... } {}
369 StringLiteral Killed 473 : 69 ' ' ""
370 Block Killed 487 : 43 { ...; } {}
371 IfStatement Killed 488 : 8 !
372 IfStatement Killed 488 : 8 !
373 PrefixUnaryExpression Killed 488 : 8 !
374 Block Killed 488 : 17 { ... } {}
375 Block Killed 501 : 48 { ...; } {}
376 ConditionalExpression Killed 505 : 37 ( ... .
377 BinaryExpression Killed 505 : 37 ( ... . && ... .
378 ConditionalExpression Killed 505 : 37 ( ... .
379 BinaryExpression Killed 505 : 38 && . || .
380 ConditionalExpression Killed 505 : 38 && .
381 ConditionalExpression Killed 505 : 38 && .
382 BooleanSubstitution Killed 509 : 80
383 BinaryExpression Killed 512 : 36 || {} && {}
384 ConditionalExpression Killed 512 : 36 || {}
385 ConditionalExpression Killed 512 : 36 || {}
386 BooleanSubstitution Killed 512 : 93
387 Block Killed 525 : 38 { ...; } {}
388 IfStatement Killed 526 : 8 !
389 IfStatement Killed 526 : 8 !
390 PrefixUnaryExpression Killed 526 : 8 !
391 Block Killed 526 : 17 { ... } {}
392 Block Killed 530 : 42 { ... } {}
393 IfStatement Killed 531 : 10 === ' '
394 IfStatement Killed 531 : 10 === ' '
395 BinaryExpression Killed 531 : 10 === ' ' !== ' '
396 StringLiteral Killed 531 : 27 ' ' ""
397 Block Killed 531 : 37 { ... } {}
398 ObjectLiteral Killed 533 : 15 { ...: {}} {}
399 IfStatement Killed 534 : 17 ' ... '
400 IfStatement Killed 534 : 17 ' ... '
401 BinaryExpression Killed 534 : 17 ' ... ' ' ... '
402 StringLiteral Killed 534 : 17 ' ... ' ""
403 ConditionalExpression Killed 534 : 46 . ... '
404 BinaryExpression Killed 534 : 46 . ... ' . ... '
405 ConditionalExpression Survived 534 : 46 . ... '
406 StringLiteral Killed 534 : 84 ' ' ""
407 Block Killed 534 : 96 { ... } {}
408 IfStatement Killed 537 : 17 ' ... '
409 IfStatement Killed 537 : 17 ' ... '
410 BinaryExpression Killed 537 : 17 ' ... ' ' ... '
411 StringLiteral Killed 537 : 17 ' ' ""
412 ConditionalExpression Killed 537 : 36 . ... '
413 BinaryExpression Killed 537 : 36 . ... ' . ... '
414 ConditionalExpression Survived 537 : 36 . ... '
415 StringLiteral Killed 537 : 58 ' ' ""
416 Block Killed 537 : 68 { ... } {}
417 IfStatement Killed 540 : 17 ' ... '
418 IfStatement Killed 540 : 17 ' ... '
419 BinaryExpression Killed 540 : 17 ' ... ' ' ... '
420 StringLiteral Killed 540 : 17 ' ' ""
421 BinaryExpression Killed 540 : 37 . ... ' . ... '
422 ConditionalExpression Killed 540 : 37 . ... '
423 ConditionalExpression Survived 540 : 37 . ... '
424 StringLiteral Killed 540 : 60 ' ' ""
425 Block Killed 540 : 72 { ... } {}
426 ObjectLiteral Killed 542 : 15 { ...: {}} {}
427 Block Killed 543 : 13 { ... } {}
428 BinaryExpression Killed 544 : 24 ' ... ) ' ... )
429 StringLiteral Killed 544 : 24 ' ... ' ""
430 Block Killed 562 : 44 { ...; } {}
431 Block Killed 563 : 47 { ... } {}
432 IfStatement Killed 564 : 10 === ' '
433 IfStatement Killed 564 : 10 === ' '
434 BinaryExpression Killed 564 : 10 === ' ' !== ' '
435 StringLiteral Killed 564 : 30 ' ' ""
436 Block Killed 564 : 40 { ... } {}
437 ObjectLiteral Killed 566 : 15 { ...: {}} {}
438 IfStatement Killed 567 : 17 ' ... '
439 IfStatement Killed 567 : 17 ' ... '
440 BinaryExpression Killed 567 : 17 ' ... ' ' ... '
441 StringLiteral Killed 567 : 17 ' ... ' ""
442 StringLiteral Killed 567 : 49 ' ' ""
443 Block Killed 567 : 73 { ... } {}
444 IfStatement Killed 570 : 17 ' '
445 IfStatement Killed 570 : 17 ' '
446 StringLiteral Killed 570 : 17 ' ' ""
447 Block Killed 570 : 37 { ... } {}
448 IfStatement Killed 572 : 12 . ... '
449 IfStatement Killed 572 : 12 . ... '
450 BinaryExpression Killed 572 : 12 . ... ' . ... '
451 StringLiteral Killed 572 : 37 ' ' ""
452 Block Killed 572 : 47 { ... } {}
453 BinaryExpression Killed 573 : 26 ' ... ) ' ... )
454 StringLiteral Killed 573 : 26 ' ... ' ""
455 IfStatement Killed 576 : 17 === ' '
456 IfStatement Killed 576 : 17 === ' '
457 BinaryExpression Killed 576 : 17 === ' ' !== ' '
458 StringLiteral Killed 576 : 37 ' ' ""
459 Block Killed 576 : 49 { ... } {}
460 ObjectLiteral Killed 578 : 15 { ...: {}} {}
461 IfStatement Killed 579 : 17 && ... '
462 BinaryExpression Killed 579 : 17 && ... ' || ... '
463 IfStatement Killed 579 : 17 && ... '
464 ConditionalExpression Killed 579 : 29 . ... '
465 BinaryExpression Killed 579 : 29 . ... ' . ... '
466 ConditionalExpression Killed 579 : 29 . ... '
467 StringLiteral Killed 579 : 60 ' ' ""
468 Block Killed 579 : 72 { ... } {}
469 ObjectLiteral Killed 581 : 15 { ...: {}} {}
470 Block Killed 582 : 13 { ... } {}
471 BinaryExpression Killed 583 : 24 ' ... ) ' ... )
472 StringLiteral Killed 583 : 24 ' ... ' ""
473 Block Killed 596 : 58 { ...; } {}
474 IfStatement Killed 597 : 8 ! ... ')
475 IfStatement Killed 597 : 8 ! ... ')
476 BinaryExpression Killed 597 : 8 ! ... ') ! ... '
477 PrefixUnaryExpression Killed 597 : 8 !
478 BinaryExpression Killed 598 : 9 . ... ' . ... '
479 ConditionalExpression Killed 598 : 9 . ... '
480 BinaryExpression Killed 598 : 9 . ... ' . ... '
481 ConditionalExpression Survived 598 : 9 . ... '
482 ConditionalExpression Killed 598 : 9 . ... '
483 ConditionalExpression Survived 598 : 9 . ... '
484 StringLiteral Killed 598 : 39 ' ' ""
485 BinaryExpression Survived 598 : 53 . ... ' . ... '
486 ConditionalExpression Survived 598 : 53 . ... '
487 ConditionalExpression Survived 598 : 53 . ... '
488 StringLiteral Killed 598 : 83 ' ' ""
489 Block Killed 598 : 96 { ... } {}
490 BinaryExpression Killed 602 : 44 . ... ' . ... '
491 ConditionalExpression Killed 602 : 44 . ... '
492 ConditionalExpression Survived 602 : 44 . ... '
493 StringLiteral Killed 602 : 62 ' ' ""
494 IfStatement Killed 603 : 8 !
495 IfStatement Killed 603 : 8 !
496 PrefixUnaryExpression Killed 603 : 8 !
497 Block Killed 617 : 38 { ...; } {}
498 IfStatement Killed 619 : 8 ! ... .
499 IfStatement Killed 619 : 8 ! ... .
500 BinaryExpression Killed 619 : 8 ! ... . ! ... .
501 BinaryExpression Killed 619 : 8 ! ... . ! ... .
502 ConditionalExpression Killed 619 : 8 ! ... .
503 ConditionalExpression Killed 619 : 8 ! ... .
504 PrefixUnaryExpression Killed 619 : 8 ! . .
505 PrefixUnaryExpression Killed 619 : 36 ! . .
506 PrefixUnaryExpression Killed 619 : 60 ! . .
507 Block Killed 619 : 82 { ... } {}
508 BinaryExpression Killed 628 : 19 . .... )) . .... ))
509 ConditionalExpression Killed 628 : 19 . .... ))
510 ConditionalExpression Killed 628 : 19 . .... ))
511 Block Killed 648 : 60 { ...; } {}
512 IfStatement Killed 649 : 8 !
513 IfStatement Killed 649 : 8 !
514 PrefixUnaryExpression Killed 649 : 8 !
515 Block Killed 649 : 24 { ... } {}
516 ObjectLiteral Killed 650 : 13 { ... ()} {}
517 IfStatement Killed 653 : 8 . ... .
518 IfStatement Killed 653 : 8 . ... .
519 BinaryExpression Killed 653 : 8 . ... . . ... .
520 Block Killed 653 : 52 { ... } {}
521 StringLiteral Survived 654 : 22 ' ... ' ""
522 PrefixUnaryExpression Killed 659 : 37 !! . ! .
523 PrefixUnaryExpression Killed 659 : 38 ! . .
524 PrefixUnaryExpression Killed 660 : 34 !! . ! .
525 PrefixUnaryExpression Killed 660 : 35 ! . .
526 BinaryExpression Killed 661 : 24 . ...|| [] . ...&& []
527 ConditionalExpression Killed 661 : 24 . ...|| []
528 ConditionalExpression Killed 661 : 24 . ...|| []
529 BinaryExpression Killed 662 : 21 . ...|| [] . ...&& []
530 ConditionalExpression Killed 662 : 21 . ...|| []
531 ConditionalExpression Killed 662 : 21 . ...|| []
532 BinaryExpression Killed 663 : 25 . ...|| [] . ...&& []
533 ConditionalExpression Killed 663 : 25 . ...|| []
534 ConditionalExpression Killed 663 : 25 . ...|| []
535 Block Killed 666 : 38 { ... } {}
536 IfStatement Killed 667 : 10 ! ... ]
537 IfStatement Killed 667 : 10 ! ... ]
538 PrefixUnaryExpression Killed 667 : 10 ! ... ] [ ]
539 Block Killed 667 : 38 { ... } {}
540 StringLiteral Survived 668 : 17 ' ' ""
541 StringLiteral Survived 668 : 27 ` ... }` ""
542 Block Killed 674 : 44 { ... } {}
543 Block Killed 675 : 74 { ... } {}
544 PrefixUnaryExpression Killed 677 : 15 !! ... ) ! ... )
545 PrefixUnaryExpression Killed 677 : 16 ! ... ) . ... )
546 BinaryExpression Survived 677 : 45 . === . !==
547 ConditionalExpression Survived 677 : 45 . ===
548 ConditionalExpression Survived 677 : 45 . ===
549 IfStatement Killed 680 : 10 !
550 IfStatement Killed 680 : 10 !
551 PrefixUnaryExpression Killed 680 : 10 !
552 Block Killed 680 : 26 { ... } {}
553 StringLiteral Survived 681 : 63 ' ' ""
554 StringLiteral Survived 681 : 78 ' ' ""
555 StringLiteral Survived 682 : 17 ' ' ""
556 StringLiteral Survived 682 : 27 ` ... }` ""
557 IfStatement Killed 683 : 17 . ... )
558 IfStatement Survived 683 : 17 . ... )
559 BinaryExpression Survived 683 : 17 . ... ) . ... )
560 Block Killed 683 : 84 { ... } {}
561 StringLiteral Survived 684 : 17 ' ' ""
562 BinaryExpression Killed 684 : 27 `${ ... '` `${ ... '`
563 StringLiteral Survived 684 : 27 `${ ... ` ""
564 StringLiteral Survived 685 : 12 `${ ... '` ""
565 Block Killed 692 : 53 { ... } {}
566 IfStatement Killed 695 : 10 &&
567 IfStatement Killed 695 : 10 &&
568 BinaryExpression Survived 695 : 10 && ||
569 Block Killed 695 : 61 { ... } {}
570 IfStatement Killed 697 : 12 ! ... )
571 IfStatement Killed 697 : 12 ! ... )
572 PrefixUnaryExpression Killed 697 : 12 ! ... ) . ... )
573 Block Killed 697 : 47 { ... } {}
574 IfStatement Killed 700 : 17
575 IfStatement Killed 700 : 17
576 Block Killed 700 : 43 { ... } {}
577 IfStatement Killed 702 : 12 ! ... )
578 IfStatement Killed 702 : 12 ! ... )
579 PrefixUnaryExpression Killed 702 : 12 ! ... ) . ... )
580 Block Killed 702 : 47 { ... } {}
581 IfStatement Killed 705 : 17
582 IfStatement Killed 705 : 17
583 Block Killed 705 : 40 { ... } {}
584 PrefixUnaryExpression Killed 710 : 62 ! ... . ) . ... . )
585 IfStatement Killed 712 : 10 . .
586 IfStatement Killed 712 : 10 . .
587 Block Killed 712 : 37 { ... } {}
588 ObjectLiteral Killed 718 : 11 { ... } {}
589 Block Killed 725 : 31 { ...; } {}
590 IfStatement Killed 727 : 8 !
591 IfStatement Killed 727 : 8 !
592 PrefixUnaryExpression Killed 727 : 8 !
593 Block Killed 727 : 21 { ... } {}
594 Block Killed 731 : 45 { ... } {}
595 ObjectLiteral Killed 733 : 13 { , } {}
596 Block Killed 742 : 44 { ...; } {}
597 IfStatement Killed 745 : 8 !
598 IfStatement Killed 745 : 8 !
599 PrefixUnaryExpression Killed 745 : 8 !
600 Block Killed 745 : 17 { ... } {}
601 Block Killed 749 : 46 { ... } {}
602 Block Killed 761 : 68 { ...; } {}
603 IfStatement Killed 762 : 8 !
604 IfStatement Killed 762 : 8 !
605 PrefixUnaryExpression Killed 762 : 8 !
606 Block Killed 762 : 17 { ... } {}
607 StringLiteral Killed 766 : 50 ' ' ""
608 Block Killed 767 : 46 { ... } {}
609 Block Killed 769 : 61 { ... } {}
610 IfStatement Killed 775 : 10 . ... && !
611 IfStatement Killed 775 : 10 . ... && !
612 BinaryExpression Killed 775 : 10 . ... && ! . ... || !
613 PrefixUnaryExpression Killed 775 : 30 !
614 Block Killed 775 : 48 { ... } {}
615 BinaryExpression Survived 776 : 25 . ... ' . ... '
616 ConditionalExpression Survived 776 : 25 . ... '
617 ConditionalExpression Survived 776 : 25 . ... '
618 StringLiteral Survived 776 : 42 ' ' ""
619 StringLiteral Survived 777 : 17 ' ' ""
620 StringLiteral Survived 777 : 27 ` ... }` ""
621 BooleanSubstitution Killed 778 : 27
622 Block Killed 782 : 22 { ... } {}
623 IfStatement Killed 784 : 10 .
624 IfStatement Survived 784 : 10 .
625 BooleanSubstitution Killed 784 : 35
626 IfStatement Killed 786 : 10 . ... '
627 IfStatement Killed 786 : 10 . ... '
628 BinaryExpression Killed 786 : 10 . ... ' . ... '
629 StringLiteral Killed 786 : 28 ' ' ""
630 BooleanSubstitution Killed 786 : 50
631 BinaryExpression Killed 787 : 13 . ... > . ... >=
632 BinaryExpression Killed 787 : 13 . ... > . ... <=
633 ConditionalExpression Killed 787 : 13 . ... >
634 ConditionalExpression Killed 787 : 13 . ... >
635 Block Killed 800 : 42 { ...; } {}
636 ObjectLiteral Killed 801 : 19 { :... '} {}
637 StringLiteral Survived 801 : 25 ' ... ' ""
638 StringLiteral Killed 801 : 49 ' : ... ' ""
639 StringLiteral Killed 802 : 21 ' ' ""
640 IfStatement Killed 804 : 8 !
641 IfStatement Killed 804 : 8 !
642 PrefixUnaryExpression Killed 804 : 8 !
643 Block Killed 804 : 25 { ... } {}
644 Block Killed 809 : 51 { ... } {}
645 IfStatement Killed 811 : 10 ' ... '
646 IfStatement Killed 811 : 10 ' ... '
647 StringLiteral Killed 811 : 10 ' ... ' ""
648 Block Killed 811 : 37 { ... } {}
649 Block Killed 813 : 13 { ... } {}
650 StringLiteral Killed 815 : 28 `${ ...}. ` ""
651 BinaryExpression Killed 816 : 45 === !==
652 ConditionalExpression Killed 816 : 45 ===
653 ConditionalExpression Killed 816 : 45 ===
654 StringLiteral Killed 817 : 26 `../ ... }` ""
655 IfStatement Killed 818 : 12 !
656 PrefixUnaryExpression Killed 818 : 12 !
657 IfStatement Killed 818 : 12 !
658 Block Killed 818 : 24 { ... } {}
659 StringLiteral Killed 820 : 68 ' ' ""
660 ObjectLiteral Killed 825 : 13 { ... } {}
661 BinaryExpression Killed 828 : 17 . || {} . && {}
662 ConditionalExpression Killed 828 : 17 . || {}
663 ConditionalExpression Killed 828 : 17 . || {}
664 Block Killed 845 : 74 { ...; } {}
665 BinaryExpression Killed 846 : 49 === `...}. ` !== `...}. `
666 ConditionalExpression Killed 846 : 49 === `...}. `
667 ConditionalExpression Killed 846 : 49 === `...}. `
668 StringLiteral Killed 846 : 55 `${ }. ` ""
669 StringLiteral Killed 848 : 22 `../ ... }` ""
670 IfStatement Killed 849 : 8 !
671 IfStatement Killed 849 : 8 !
672 PrefixUnaryExpression Killed 849 : 8 !
673 Block Killed 849 : 23 { ... } {}
674 StringLiteral Killed 851 : 58 ' ' ""
675 ObjectLiteral Killed 856 : 11 { ... } {}
676 BinaryExpression Killed 860 : 15 || {} && {}
677 ConditionalExpression Killed 860 : 15 || {}
678 ConditionalExpression Killed 860 : 15 || {}
679 Block Killed 872 : 45 { ...; } {}
680 IfStatement Killed 873 : 8 !
681 IfStatement Killed 873 : 8 !
682 PrefixUnaryExpression Killed 873 : 8 !
683 Block Killed 873 : 17 { ... } {}
684 ObjectLiteral Killed 876 : 19 { :... '} {}
685 StringLiteral Survived 876 : 25 ' ... ' ""
686 StringLiteral Killed 876 : 52 ' : ... ' ""
687 StringLiteral Killed 877 : 21 ' ' ""
688 Block Killed 880 : 42 { ... } {}
689 Block Killed 881 : 95 { ... } {}
690 IfStatement Killed 882 : 12 .
691 IfStatement Killed 882 : 12 .
692 Block Killed 882 : 35 { ... } {}
693 ObjectLiteral Killed 883 : 17 { ... } {}
694 BinaryExpression Killed 887 : 21 . || {} . && {}
695 ConditionalExpression Killed 887 : 21 . || {}
696 ConditionalExpression Killed 887 : 21 . || {}
697 IfStatement Killed 889 : 19 .
698 IfStatement Killed 889 : 19 .
699 Block Killed 889 : 48 { ... } {}
700 ObjectLiteral Killed 891 : 17 { ... } {}
701 BinaryExpression Killed 895 : 21 . || {} . && {}
702 ConditionalExpression Survived 895 : 21 . || {}
703 ConditionalExpression Survived 895 : 21 . || {}
704 IfStatement Killed 897 : 19 .
705 IfStatement Killed 897 : 19 .
706 Block Killed 897 : 38 { ... } {}
707 Block Killed 901 : 15 { ... } {}
708 BinaryExpression Killed 902 : 26 ' ... ) ' ... )
709 StringLiteral Survived 902 : 26 ' ... : ' ""
710 ObjectLiteral Killed 909 : 33 { ... } {}
711 Block Killed 925 : 62 { ...; } {}
712 Block Killed 930 : 8 { ... } {}
713 Block Killed 938 : 8 { ... } {}
714 BinaryExpression Killed 942 : 24 ' ... }'` ' ... }'`
715 BinaryExpression Killed 942 : 24 ' ...: '') ' ...: '')
716 StringLiteral Killed 942 : 24 ' ... ' ""
717 StringLiteral Killed 943 : 20 `${ }: ` ""
718 StringLiteral Survived 943 : 38 '' " ... !"
719 StringLiteral Killed 944 : 8 `${ ... }'` ""
720 IfStatement Killed 946 : 8 !
721 IfStatement Killed 946 : 8 !
722 PrefixUnaryExpression Killed 946 : 8 !
723 Block Killed 946 : 20 { ... } {}
724 BinaryExpression Killed 947 : 22 + ')' - ')'
725 StringLiteral Survived 947 : 36 ')' ""
726 Block Killed 954 : 8 { ... } {}
727 BinaryExpression Killed 958 : 20 + ` ... }')` - ` ... }')`
728 StringLiteral Survived 958 : 34 ` ... }')` ""