function loadArtifacts(basePath) {
  log.log('Reading artifacts from disk:', basePath);

  if (!fs.existsSync(basePath)) {
    throw new Error('No saved artifacts found at ' + basePath);
  }

  // load artifacts.json using a reviver to deserialize any LighthouseErrors in artifacts.
  /** @type {LH.Artifacts} */
  const artifacts = readJson(path.join(basePath, artifactsFilename), LighthouseError.parseReviver);

  const filenames = fs.readdirSync(basePath);

  filenames.filter(f => endsWithSuffix(f, devtoolsLogSuffix)).forEach(filename => {
    if (!artifacts.devtoolsLogs) artifacts.devtoolsLogs = {};
    const prefix = filename.replace(devtoolsLogSuffix + '.gz', '').replace(devtoolsLogSuffix, '');
    const devtoolsLog = readJson(path.join(basePath, filename));
    artifacts.devtoolsLogs[prefix] = devtoolsLog;
    if (prefix === defaultPrefix) {
      artifacts.DevtoolsLog = devtoolsLog;
    }
    if (prefix === errorPrefix) {
      artifacts.DevtoolsLogError = devtoolsLog;
    }
  });

  filenames.filter(f => endsWithSuffix(f, traceSuffix)).forEach(filename => {
    if (!artifacts.traces) artifacts.traces = {};
    const trace = readJson(path.join(basePath, filename));
    const prefix = filename.replace(traceSuffix + '.gz', '').replace(traceSuffix, '');
    artifacts.traces[prefix] = Array.isArray(trace) ? {traceEvents: trace} : trace;
    if (prefix === defaultPrefix) {
      artifacts.Trace = artifacts.traces[prefix];
    }
    if (prefix === errorPrefix) {
      artifacts.TraceError = artifacts.traces[prefix];
    }
  });

  if (Array.isArray(artifacts.Timing)) {
    // Any Timing entries in saved artifacts will have a different timeOrigin than the auditing phase
    // The `gather` prop is read later in generate-timing-trace and they're added to a separate track of trace events
    artifacts.Timing.forEach(entry => (entry.gather = true));
  }
  return artifacts;
}
Load artifacts object from files located within basePath
Also save the traces to their own files
@param {string} basePath
@return {LH.Artifacts}