static async audit_(artifacts, networkRecords, context) {
    const devtoolsLog = artifacts.DevtoolsLog;
    const classifiedEntities = await EntityClassification.request(
      {URL: artifacts.URL, devtoolsLog}, context);

    const bundles = await JSBundles.request(artifacts, context);

    /** @type {Item[]} */
    const items = [];

    /** @type {Map<string, number>} */
    const compressionRatioByUrl = new Map();

    for (const script of artifacts.Scripts) {
      const bundle = bundles.find(bundle => bundle.script.scriptId === script.scriptId);
      const {matches, estimatedByteSavings} =
        detectLegacyJavaScript(script.content ?? '', bundle?.map ?? null);
      if (matches.length === 0) continue;

      const compressionRatio = estimateCompressionRatioForContent(
        compressionRatioByUrl, script.url, artifacts, networkRecords);
      const wastedBytes = Math.round(estimatedByteSavings * compressionRatio);
      /** @type {typeof items[number]} */
      const item = {
        url: script.url,
        wastedBytes,
        subItems: {
          type: 'subitems',
          items: [],
        },
        // Not needed, but keeps typescript happy.
        totalBytes: 0,
      };

      for (const match of matches) {
        const {name, line, column} = match;
        /** @type {SubItem} */
        const subItem = {
          signal: name,
          location: ByteEfficiencyAudit.makeSourceLocation(script.url, line, column, bundle),
        };
        item.subItems.items.push(subItem);
      }
      items.push(item);
    }

    const warnings = [];
    for (const bundle of bundles) {
      if (classifiedEntities.isFirstParty(bundle.script.url)) {
        if (bundle.rawMap.sources.some(s => s.match(/node_modules\/core-js\/modules\/es[67]/))) {
          warnings.push(str_(UIStrings.detectedCoreJs2Warning));
          break;
        }
      }
    }

    /** @type {Map<string, number>} */
    const wastedBytesByUrl = new Map();
    for (const item of items) {
      // Only estimate savings if first party code has legacy code.
      if (classifiedEntities.isFirstParty(item.url)) {
        wastedBytesByUrl.set(item.url, item.wastedBytes);
      }
    }

    /** @type {LH.Audit.Details.TableColumnHeading[]} */
    const headings = [
      /* eslint-disable max-len */
      {key: 'url', valueType: 'url', subItemsHeading: {key: 'location', valueType: 'source-location'}, label: str_(i18n.UIStrings.columnURL)},
      {key: null, valueType: 'code', subItemsHeading: {key: 'signal'}, label: ''},
      {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
      /* eslint-enable max-len */
    ];

    return {
      items,
      headings,
      wastedBytesByUrl,
      warnings,
    };
  }
@param {LH.Artifacts} artifacts
@param {Array<LH.Artifacts.NetworkRequest>} networkRecords
@param {LH.Audit.Context} context
@return {Promise<ByteEfficiencyProduct>}