lighthouse 12.4.0 → 12.5.0-dev.20250325

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/core/audits/audit.js +3 -1
  2. package/core/audits/bootup-time.js +1 -1
  3. package/core/audits/byte-efficiency/duplicated-javascript.js +1 -1
  4. package/core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  5. package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -65
  6. package/core/audits/byte-efficiency/legacy-javascript.js +11 -363
  7. package/core/audits/byte-efficiency/modern-image-formats.js +1 -1
  8. package/core/audits/byte-efficiency/offscreen-images.js +4 -3
  9. package/core/audits/byte-efficiency/render-blocking-resources.js +6 -3
  10. package/core/audits/byte-efficiency/unminified-css.js +2 -1
  11. package/core/audits/byte-efficiency/unminified-javascript.js +2 -1
  12. package/core/audits/byte-efficiency/unused-css-rules.js +1 -1
  13. package/core/audits/byte-efficiency/unused-javascript.js +2 -2
  14. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +1 -1
  15. package/core/audits/byte-efficiency/uses-optimized-images.js +1 -1
  16. package/core/audits/byte-efficiency/uses-responsive-images.js +1 -1
  17. package/core/audits/byte-efficiency/uses-text-compression.js +2 -1
  18. package/core/audits/critical-request-chains.js +5 -3
  19. package/core/audits/dobetterweb/dom-size.js +1 -1
  20. package/core/audits/dobetterweb/uses-http2.js +1 -1
  21. package/core/audits/insights/cls-culprits-insight.js +1 -1
  22. package/core/audits/insights/document-latency-insight.js +1 -1
  23. package/core/audits/insights/dom-size-insight.js +1 -1
  24. package/core/audits/insights/{long-critical-network-tree-insight.d.ts → duplicated-javascript-insight.d.ts} +3 -3
  25. package/core/audits/insights/duplicated-javascript-insight.js +54 -0
  26. package/core/audits/insights/font-display-insight.js +8 -7
  27. package/core/audits/insights/forced-reflow-insight.d.ts +10 -0
  28. package/core/audits/insights/forced-reflow-insight.js +64 -12
  29. package/core/audits/insights/image-delivery-insight.js +9 -10
  30. package/core/audits/insights/insight-audit.js +9 -4
  31. package/core/audits/insights/interaction-to-next-paint-insight.js +1 -1
  32. package/core/audits/insights/lcp-discovery-insight.js +1 -1
  33. package/core/audits/insights/lcp-phases-insight.js +1 -1
  34. package/core/audits/insights/network-dependency-tree-insight.d.ts +11 -0
  35. package/core/audits/insights/{long-critical-network-tree-insight.js → network-dependency-tree-insight.js} +7 -7
  36. package/core/audits/insights/render-blocking-insight.js +1 -1
  37. package/core/audits/insights/slow-css-selector-insight.js +1 -1
  38. package/core/audits/insights/third-parties-insight.js +1 -1
  39. package/core/audits/insights/use-cache-insight.d.ts +11 -0
  40. package/core/audits/insights/use-cache-insight.js +61 -0
  41. package/core/audits/insights/viewport-insight.js +1 -1
  42. package/core/audits/largest-contentful-paint-element.js +7 -3
  43. package/core/audits/layout-shifts.js +5 -2
  44. package/core/audits/lcp-lazy-loaded.js +1 -1
  45. package/core/audits/long-tasks.js +6 -4
  46. package/core/audits/mainthread-work-breakdown.js +1 -1
  47. package/core/audits/metrics/first-contentful-paint.js +4 -2
  48. package/core/audits/metrics/interactive.js +6 -3
  49. package/core/audits/metrics/largest-contentful-paint.js +7 -3
  50. package/core/audits/metrics/max-potential-fid.js +6 -3
  51. package/core/audits/metrics/speed-index.js +6 -3
  52. package/core/audits/metrics/total-blocking-time.js +6 -3
  53. package/core/audits/metrics.js +4 -3
  54. package/core/audits/predictive-perf.js +4 -3
  55. package/core/audits/prioritize-lcp-image.js +5 -3
  56. package/core/audits/redirects.js +4 -2
  57. package/core/audits/script-treemap-data.js +8 -4
  58. package/core/audits/third-party-facades.js +1 -1
  59. package/core/audits/third-party-summary.js +1 -1
  60. package/core/audits/uses-rel-preconnect.js +7 -5
  61. package/core/audits/uses-rel-preload.js +5 -3
  62. package/core/computed/computed-artifact.d.ts +5 -1
  63. package/core/computed/computed-artifact.js +23 -2
  64. package/core/computed/critical-request-chains.d.ts +5 -1
  65. package/core/computed/critical-request-chains.js +4 -4
  66. package/core/computed/js-bundles.d.ts +1 -1
  67. package/core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
  68. package/core/computed/metrics/first-contentful-paint.js +1 -1
  69. package/core/computed/metrics/interactive.js +1 -1
  70. package/core/computed/metrics/lantern-first-contentful-paint.js +1 -1
  71. package/core/computed/metrics/lantern-interactive.js +1 -1
  72. package/core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  73. package/core/computed/metrics/lantern-max-potential-fid.js +1 -1
  74. package/core/computed/metrics/lantern-metric.js +1 -1
  75. package/core/computed/metrics/lantern-speed-index.js +1 -1
  76. package/core/computed/metrics/lantern-total-blocking-time.js +1 -1
  77. package/core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
  78. package/core/computed/metrics/largest-contentful-paint.js +1 -1
  79. package/core/computed/metrics/lcp-breakdown.js +1 -1
  80. package/core/computed/metrics/max-potential-fid.js +1 -1
  81. package/core/computed/metrics/metric.js +2 -0
  82. package/core/computed/metrics/speed-index.js +1 -1
  83. package/core/computed/metrics/time-to-first-byte.js +1 -1
  84. package/core/computed/metrics/timing-summary.d.ts +5 -2
  85. package/core/computed/metrics/timing-summary.js +8 -4
  86. package/core/computed/metrics/total-blocking-time.js +1 -1
  87. package/core/computed/module-duplication.d.ts +1 -1
  88. package/core/computed/navigation-insights.d.ts +11 -3
  89. package/core/computed/navigation-insights.js +7 -4
  90. package/core/computed/page-dependency-graph.d.ts +7 -3
  91. package/core/computed/page-dependency-graph.js +6 -5
  92. package/core/computed/tbt-impact-tasks.js +1 -1
  93. package/core/computed/trace-engine-result.d.ts +36 -2
  94. package/core/computed/trace-engine-result.js +119 -25
  95. package/core/computed/unused-javascript-summary.d.ts +2 -2
  96. package/core/computed/unused-javascript-summary.js +1 -1
  97. package/core/config/default-config.js +19 -15
  98. package/core/config/experimental-config.js +19 -0
  99. package/core/gather/gatherers/source-maps.d.ts +1 -0
  100. package/core/gather/gatherers/source-maps.js +3 -0
  101. package/core/gather/gatherers/trace-elements.d.ts +4 -4
  102. package/core/gather/gatherers/trace-elements.js +8 -4
  103. package/core/gather/gatherers/trace.js +5 -0
  104. package/core/lib/bf-cache-strings.d.ts +0 -122
  105. package/core/lib/bf-cache-strings.js +1 -2
  106. package/core/lib/deprecation-description.js +2 -1
  107. package/core/lib/legacy-javascript/legacy-javascript.d.ts +29 -0
  108. package/core/lib/legacy-javascript/legacy-javascript.js +351 -0
  109. package/core/lib/legacy-javascript/polyfill-graph-data.json +93 -0
  110. package/core/lib/legacy-javascript/polyfill-module-data.json +623 -0
  111. package/core/lib/trace-engine.d.ts +6 -1
  112. package/core/lib/trace-engine.js +1 -2
  113. package/package.json +10 -8
  114. package/shared/localization/locales/ar-XB.json +783 -513
  115. package/shared/localization/locales/ar.json +783 -513
  116. package/shared/localization/locales/bg.json +933 -663
  117. package/shared/localization/locales/ca.json +925 -655
  118. package/shared/localization/locales/cs.json +926 -656
  119. package/shared/localization/locales/da.json +926 -656
  120. package/shared/localization/locales/de.json +803 -533
  121. package/shared/localization/locales/el.json +928 -658
  122. package/shared/localization/locales/en-GB.json +929 -659
  123. package/shared/localization/locales/en-US.json +593 -551
  124. package/shared/localization/locales/en-XA.json +28 -508
  125. package/shared/localization/locales/en-XL.json +593 -551
  126. package/shared/localization/locales/es-419.json +928 -658
  127. package/shared/localization/locales/es.json +787 -517
  128. package/shared/localization/locales/fi.json +925 -655
  129. package/shared/localization/locales/fil.json +929 -659
  130. package/shared/localization/locales/fr.json +927 -657
  131. package/shared/localization/locales/he.json +795 -528
  132. package/shared/localization/locales/hi.json +798 -528
  133. package/shared/localization/locales/hr.json +929 -659
  134. package/shared/localization/locales/hu.json +926 -656
  135. package/shared/localization/locales/id.json +926 -656
  136. package/shared/localization/locales/it.json +930 -660
  137. package/shared/localization/locales/ja.json +927 -657
  138. package/shared/localization/locales/ko.json +936 -666
  139. package/shared/localization/locales/lt.json +933 -663
  140. package/shared/localization/locales/lv.json +809 -539
  141. package/shared/localization/locales/nl.json +925 -655
  142. package/shared/localization/locales/no.json +928 -658
  143. package/shared/localization/locales/pl.json +927 -657
  144. package/shared/localization/locales/pt-PT.json +841 -571
  145. package/shared/localization/locales/pt.json +841 -571
  146. package/shared/localization/locales/ro.json +925 -655
  147. package/shared/localization/locales/ru.json +935 -668
  148. package/shared/localization/locales/sk.json +925 -655
  149. package/shared/localization/locales/sl.json +927 -657
  150. package/shared/localization/locales/sr-Latn.json +925 -655
  151. package/shared/localization/locales/sr.json +925 -655
  152. package/shared/localization/locales/sv.json +936 -666
  153. package/shared/localization/locales/ta.json +935 -668
  154. package/shared/localization/locales/te.json +785 -515
  155. package/shared/localization/locales/th.json +813 -543
  156. package/shared/localization/locales/tr.json +927 -657
  157. package/shared/localization/locales/uk.json +795 -525
  158. package/shared/localization/locales/vi.json +929 -659
  159. package/shared/localization/locales/zh-HK.json +926 -656
  160. package/shared/localization/locales/zh-TW.json +784 -514
  161. package/shared/localization/locales/zh.json +926 -656
  162. package/tsconfig.json +2 -1
  163. package/types/artifacts.d.ts +2 -1
  164. package/core/audits/byte-efficiency/polyfill-graph-data.json +0 -53
@@ -491,7 +491,9 @@ class Audit {
491
491
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
492
492
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
493
493
  const gatherContext = artifacts.GatherContext;
494
- return {trace, devtoolsLog, gatherContext, settings: context.settings, URL: artifacts.URL};
494
+ const {URL, SourceMaps} = artifacts;
495
+ // eslint-disable-next-line max-len
496
+ return {trace, devtoolsLog, gatherContext, settings: context.settings, URL, SourceMaps, simulator: null};
495
497
  }
496
498
  }
497
499
 
@@ -49,7 +49,7 @@ class BootupTime extends Audit {
49
49
  description: str_(UIStrings.description),
50
50
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
51
51
  guidanceLevel: 1,
52
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
52
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext', 'SourceMaps'],
53
53
  };
54
54
  }
55
55
 
@@ -30,7 +30,7 @@ const UIStrings = {
30
30
 
31
31
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
32
32
 
33
- const IGNORE_THRESHOLD_IN_BYTES = 1024;
33
+ const IGNORE_THRESHOLD_IN_BYTES = 1024 * 10;
34
34
 
35
35
  /**
36
36
  * @param {string} haystack
@@ -38,7 +38,7 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit {
38
38
  description: str_(UIStrings.description),
39
39
  scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
40
40
  guidanceLevel: 3,
41
- requiredArtifacts: ['devtoolsLogs', 'traces', 'GatherContext', 'URL'],
41
+ requiredArtifacts: ['devtoolsLogs', 'traces', 'GatherContext', 'URL', 'SourceMaps'],
42
42
  };
43
43
  }
44
44
 
@@ -1,15 +1,4 @@
1
1
  export default LegacyJavascript;
2
- export type Pattern = {
3
- name: string;
4
- expression: string;
5
- estimateBytes?: (result: PatternMatchResult) => number;
6
- };
7
- export type PatternMatchResult = {
8
- name: string;
9
- line: number;
10
- column: number;
11
- count: number;
12
- };
13
2
  export type ByteEfficiencyProduct = import("./byte-efficiency-audit.js").ByteEfficiencyProduct;
14
3
  export type Item = LH.Audit.ByteEfficiencyItem & {
15
4
  subItems: {
@@ -22,43 +11,6 @@ export type SubItem = {
22
11
  location: LH.Audit.Details.SourceLocationValue;
23
12
  };
24
13
  declare class LegacyJavascript extends ByteEfficiencyAudit {
25
- /**
26
- * @param {string?} object
27
- * @param {string} property
28
- */
29
- static buildPolyfillExpression(object: string | null, property: string): string;
30
- static getPolyfillData(): {
31
- name: string;
32
- modules: string[];
33
- corejs?: boolean;
34
- }[];
35
- static getCoreJsPolyfillData(): {
36
- name: string;
37
- coreJs2Module: string;
38
- coreJs3Module: string;
39
- }[];
40
- /**
41
- * @return {Pattern[]}
42
- */
43
- static getPolyfillPatterns(): Pattern[];
44
- /**
45
- * @return {Pattern[]}
46
- */
47
- static getTransformPatterns(): Pattern[];
48
- /**
49
- * Returns a collection of match results grouped by script url.
50
- *
51
- * @param {CodePatternMatcher} matcher
52
- * @param {LH.Artifacts['Scripts']} scripts
53
- * @param {LH.Artifacts.Bundle[]} bundles
54
- * @return {Map<LH.Artifacts.Script, PatternMatchResult[]>}
55
- */
56
- static detectAcrossScripts(matcher: CodePatternMatcher, scripts: LH.Artifacts["Scripts"], bundles: LH.Artifacts.Bundle[]): Map<LH.Artifacts.Script, PatternMatchResult[]>;
57
- /**
58
- * @param {PatternMatchResult[]} matches
59
- * @return {number}
60
- */
61
- static estimateWastedBytes(matches: PatternMatchResult[]): number;
62
14
  /**
63
15
  * @param {LH.Artifacts} artifacts
64
16
  * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
@@ -73,21 +25,4 @@ export namespace UIStrings {
73
25
  let detectedCoreJs2Warning: string;
74
26
  }
75
27
  import { ByteEfficiencyAudit } from './byte-efficiency-audit.js';
76
- /**
77
- * Takes a list of patterns (consisting of a name identifier and a RegExp expression string)
78
- * and returns match results with line / column information for a given code input.
79
- */
80
- declare class CodePatternMatcher {
81
- /**
82
- * @param {Pattern[]} patterns
83
- */
84
- constructor(patterns: Pattern[]);
85
- re: RegExp;
86
- patterns: Pattern[];
87
- /**
88
- * @param {string} code
89
- * @return {PatternMatchResult[]}
90
- */
91
- match(code: string): PatternMatchResult[];
92
- }
93
28
  //# sourceMappingURL=legacy-javascript.d.ts.map
@@ -5,33 +5,23 @@
5
5
  */
6
6
 
7
7
  /**
8
- * @fileoverview Identifies polyfills and transforms that should not be present if using module/nomodule pattern.
9
- * @see https://docs.google.com/document/d/1ItjJwAd6e0Ts6yMbvh8TN3BBh_sAd58rYE1whnpuxaA/edit Design document
8
+ * @fileoverview Identifies polyfills and transforms that should not be present if needing to support only Baseline browsers.
9
+ * @see https://docs.google.com/document/d/1ItjJwAd6e0Ts6yMbvh8TN3BBh_sAd58rYE1whnpuxaA/edit Design document (old, based on module/nomodule pattern)
10
10
  * @see https://docs.google.com/spreadsheets/d/1z28Au8wo8-c2UsM2lDVEOJcI3jOkb2c951xEBqzBKCc/edit?usp=sharing Legacy babel transforms / polyfills
11
11
  * ./core/scripts/legacy-javascript - verification tool.
12
12
  */
13
13
 
14
- /** @typedef {{name: string, expression: string, estimateBytes?: (result: PatternMatchResult) => number}} Pattern */
15
- /** @typedef {{name: string, line: number, column: number, count: number}} PatternMatchResult */
16
14
  /** @typedef {import('./byte-efficiency-audit.js').ByteEfficiencyProduct} ByteEfficiencyProduct */
17
15
  /** @typedef {LH.Audit.ByteEfficiencyItem & {subItems: {type: 'subitems', items: SubItem[]}}} Item */
18
16
  /** @typedef {{signal: string, location: LH.Audit.Details.SourceLocationValue}} SubItem */
19
17
 
20
- import fs from 'fs';
21
-
22
18
  import {Audit} from '../audit.js';
23
19
  import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
24
20
  import {EntityClassification} from '../../computed/entity-classification.js';
25
21
  import {JSBundles} from '../../computed/js-bundles.js';
26
22
  import * as i18n from '../../lib/i18n/i18n.js';
27
23
  import {estimateCompressionRatioForContent} from '../../lib/script-helpers.js';
28
- import {LH_ROOT} from '../../../shared/root.js';
29
-
30
- const graphJson = fs.readFileSync(
31
- `${LH_ROOT}/core/audits/byte-efficiency/polyfill-graph-data.json`, 'utf-8');
32
-
33
- /** @type {import('../../scripts/legacy-javascript/create-polyfill-size-estimation.js').PolyfillSizeEstimator} */
34
- const graph = JSON.parse(graphJson);
24
+ import {detectLegacyJavaScript} from '../../lib/legacy-javascript/legacy-javascript.js';
35
25
 
36
26
  const UIStrings = {
37
27
  /** Title of a Lighthouse audit that tells the user about legacy polyfills and transforms used on the page. This is displayed in a list of audit titles that Lighthouse generates. */
@@ -39,7 +29,7 @@ const UIStrings = {
39
29
  // eslint-disable-next-line max-len
40
30
  // TODO: developer.chrome.com article. this codelab is good starting place: https://web.dev/articles/codelab-serve-modern-code
41
31
  /** Description of a Lighthouse audit that tells the user about old JavaScript that is no longer needed. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
42
- description: 'Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren\'t necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using [module/nomodule feature detection](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn how to serve modern JavaScript](https://web.dev/articles/codelab-serve-modern-code)',
32
+ description: 'Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren\'t necessary for modern browsers. Consider modifying your JavaScript build process to not transpile [Baseline](https://web.dev/baseline) features, unless you know you must support legacy browsers. [Learn why most sites can deploy ES6+ code without transpiling](https://philipwalton.com/articles/the-state-of-es5-on-the-web/)',
43
33
  /** Warning text that an outdated version of the library "core-js" was found, and the developer should upgrade. */
44
34
  // eslint-disable-next-line max-len
45
35
  detectedCoreJs2Warning: 'Version 2 of core-js was detected on the page. You should upgrade to version 3 for many performance improvements.',
@@ -47,70 +37,6 @@ const UIStrings = {
47
37
 
48
38
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
49
39
 
50
- /**
51
- * Takes a list of patterns (consisting of a name identifier and a RegExp expression string)
52
- * and returns match results with line / column information for a given code input.
53
- */
54
- class CodePatternMatcher {
55
- /**
56
- * @param {Pattern[]} patterns
57
- */
58
- constructor(patterns) {
59
- const patternsExpression = patterns.map(pattern => `(${pattern.expression})`).join('|');
60
- this.re = new RegExp(`(^\r\n|\r|\n)|${patternsExpression}`, 'g');
61
- this.patterns = patterns;
62
- }
63
-
64
- /**
65
- * @param {string} code
66
- * @return {PatternMatchResult[]}
67
- */
68
- match(code) {
69
- // Reset RegExp state.
70
- this.re.lastIndex = 0;
71
-
72
- const seen = new Set();
73
- /** @type {PatternMatchResult[]} */
74
- const matches = [];
75
- /** @type {RegExpExecArray | null} */
76
- let result;
77
- let line = 0;
78
- let lineBeginsAtIndex = 0;
79
- // Each pattern maps to one subgroup in the generated regex. For each iteration of RegExp.exec,
80
- // only one subgroup will be defined. Exec until no more matches.
81
- while ((result = this.re.exec(code)) !== null) {
82
- // Discard first value in `result` - it's just the entire match.
83
- const captureGroups = result.slice(1);
84
- // isNewline - truthy if matching a newline, used to track the line number.
85
- // `patternExpressionMatches` maps to each possible pattern in `this.patterns`.
86
- // Only one of [isNewline, ...patternExpressionMatches] is ever truthy.
87
- const [isNewline, ...patternExpressionMatches] = captureGroups;
88
- if (isNewline) {
89
- line++;
90
- lineBeginsAtIndex = result.index + 1;
91
- continue;
92
- }
93
- const pattern = this.patterns[patternExpressionMatches.findIndex(Boolean)];
94
-
95
- if (seen.has(pattern)) {
96
- const existingMatch = matches.find(m => m.name === pattern.name);
97
- if (existingMatch) existingMatch.count += 1;
98
- continue;
99
- }
100
- seen.add(pattern);
101
-
102
- matches.push({
103
- name: pattern.name,
104
- line,
105
- column: result.index - lineBeginsAtIndex,
106
- count: 1,
107
- });
108
- }
109
-
110
- return matches;
111
- }
112
- }
113
-
114
40
  class LegacyJavascript extends ByteEfficiencyAudit {
115
41
  /**
116
42
  * @return {LH.Audit.Meta}
@@ -127,281 +53,6 @@ class LegacyJavascript extends ByteEfficiencyAudit {
127
53
  };
128
54
  }
129
55
 
130
- /**
131
- * @param {string?} object
132
- * @param {string} property
133
- */
134
- static buildPolyfillExpression(object, property) {
135
- const qt = (/** @type {string} */ token) =>
136
- `['"]${token}['"]`; // don't worry about matching string delims
137
-
138
- let expression = '';
139
-
140
- if (object) {
141
- // String.prototype.startsWith =
142
- expression += `${object}\\.${property}\\s?=[^=]`;
143
- } else {
144
- // Promise =
145
- // window.Promise =// Promise =Z
146
- // but not: SomePromise =
147
- expression += `(?:window\\.|[\\s;]+)${property}\\s?=[^=]`;
148
- }
149
-
150
- // String.prototype['startsWith'] =
151
- if (object) {
152
- expression += `|${object}\\[${qt(property)}\\]\\s?=[^=]`;
153
- }
154
-
155
- // Object.defineProperty(String.prototype, 'startsWith'
156
- expression += `|defineProperty\\(${object || 'window'},\\s?${qt(property)}`;
157
-
158
- // es-shims
159
- // no(Object,{entries:r},{entries:function
160
- if (object) {
161
- expression += `|\\(${object},\\s*{${property}:.*},\\s*{${property}`;
162
- }
163
-
164
- // core-js
165
- if (object) {
166
- const objectWithoutPrototype = object.replace('.prototype', '');
167
- // e(e.S,"Object",{values
168
- // Minified + mangled pattern found in CDN babel-polyfill.
169
- // see https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.min.js
170
- // TODO: perhaps this is the wrong place to check for a CDN polyfill. Remove?
171
- // expression += `|;e\\([^,]+,${qt(objectWithoutPrototype)},{${property}:`;
172
-
173
- // core-js@2 minified pattern.
174
- // $export($export.S,"Date",{now:function
175
- expression += `|\\$export\\([^,]+,${qt(objectWithoutPrototype)},{${property}:`;
176
-
177
- // core-js@3 minified pattern.
178
- // {target:"Array",proto:true},{fill:fill
179
- // {target:"Array",proto:true,forced:!HAS_SPECIES_SUPPORT||!USES_TO_LENGTH},{filter:
180
- expression += `|{target:${qt(objectWithoutPrototype)}\\S*},{${property}:`;
181
- } else {
182
- // WeakSet, etc.
183
- expression += `|function ${property}\\(`;
184
- }
185
-
186
- return expression;
187
- }
188
-
189
- static getPolyfillData() {
190
- /** @type {Array<{name: string, modules: string[], corejs?: boolean}>} */
191
- const data = [
192
- {name: 'focus-visible', modules: ['focus-visible']},
193
- ];
194
-
195
- const coreJsPolyfills = [
196
- ['Array.prototype.fill', 'es6.array.fill'],
197
- ['Array.prototype.filter', 'es6.array.filter'],
198
- ['Array.prototype.find', 'es6.array.find'],
199
- ['Array.prototype.findIndex', 'es6.array.find-index'],
200
- ['Array.prototype.forEach', 'es6.array.for-each'],
201
- ['Array.from', 'es6.array.from'],
202
- ['Array.isArray', 'es6.array.is-array'],
203
- ['Array.prototype.map', 'es6.array.map'],
204
- ['Array.of', 'es6.array.of'],
205
- ['Array.prototype.some', 'es6.array.some'],
206
- ['Date.now', 'es6.date.now'],
207
- ['Date.prototype.toISOString', 'es6.date.to-iso-string'],
208
- ['Date.prototype.toJSON', 'es6.date.to-json'],
209
- ['Date.prototype.toString', 'es6.date.to-string'],
210
- ['Function.prototype.name', 'es6.function.name'],
211
- ['Number.isInteger', 'es6.number.is-integer'],
212
- ['Number.isSafeInteger', 'es6.number.is-safe-integer'],
213
- ['Object.defineProperties', 'es6.object.define-properties'],
214
- ['Object.defineProperty', 'es6.object.define-property'],
215
- ['Object.freeze', 'es6.object.freeze'],
216
- ['Object.getPrototypeOf', 'es6.object.get-prototype-of'],
217
- ['Object.isExtensible', 'es6.object.is-extensible'],
218
- ['Object.isFrozen', 'es6.object.is-frozen'],
219
- ['Object.isSealed', 'es6.object.is-sealed'],
220
- ['Object.keys', 'es6.object.keys'],
221
- ['Object.preventExtensions', 'es6.object.prevent-extensions'],
222
- ['Object.seal', 'es6.object.seal'],
223
- ['Object.setPrototypeOf', 'es6.object.set-prototype-of'],
224
- ['Reflect.apply', 'es6.reflect.apply'],
225
- ['Reflect.construct', 'es6.reflect.construct'],
226
- ['Reflect.defineProperty', 'es6.reflect.define-property'],
227
- ['Reflect.deleteProperty', 'es6.reflect.delete-property'],
228
- ['Reflect.get', 'es6.reflect.get'],
229
- ['Reflect.getOwnPropertyDescriptor', 'es6.reflect.get-own-property-descriptor'],
230
- ['Reflect.getPrototypeOf', 'es6.reflect.get-prototype-of'],
231
- ['Reflect.has', 'es6.reflect.has'],
232
- ['Reflect.isExtensible', 'es6.reflect.is-extensible'],
233
- ['Reflect.ownKeys', 'es6.reflect.own-keys'],
234
- ['Reflect.preventExtensions', 'es6.reflect.prevent-extensions'],
235
- ['Reflect.setPrototypeOf', 'es6.reflect.set-prototype-of'],
236
- ['String.prototype.codePointAt', 'es6.string.code-point-at'],
237
- ['String.raw', 'es6.string.raw'],
238
- ['String.prototype.repeat', 'es6.string.repeat'],
239
- ['Object.entries', 'es7.object.entries'],
240
- ['Object.getOwnPropertyDescriptors', 'es7.object.get-own-property-descriptors'],
241
- ['Object.values', 'es7.object.values'],
242
- ];
243
-
244
- for (const [name, coreJs2Module] of coreJsPolyfills) {
245
- // es-shims follows a pattern for its packages.
246
- // Tack it onto the corejs size estimation, as it is likely close in size.
247
- const esShimModule = name.toLowerCase();
248
- data.push({
249
- name,
250
- modules: [
251
- coreJs2Module,
252
- // corejs 3 module name
253
- coreJs2Module
254
- .replace('es6.', 'es.')
255
- .replace('es7.', 'es.')
256
- .replace('typed.', 'typed-array.'),
257
- esShimModule,
258
- ],
259
- corejs: true,
260
- });
261
- }
262
-
263
- return data;
264
- }
265
-
266
- static getCoreJsPolyfillData() {
267
- return this.getPolyfillData().filter(d => d.corejs).map(d => {
268
- return {
269
- name: d.name,
270
- coreJs2Module: d.modules[0],
271
- coreJs3Module: d.modules[1],
272
- };
273
- });
274
- }
275
-
276
- /**
277
- * @return {Pattern[]}
278
- */
279
- static getPolyfillPatterns() {
280
- /** @type {Pattern[]} */
281
- const patterns = [];
282
-
283
- for (const {name} of this.getCoreJsPolyfillData()) {
284
- const parts = name.split('.');
285
- const object = parts.length > 1 ? parts.slice(0, parts.length - 1).join('.') : null;
286
- const property = parts[parts.length - 1];
287
- patterns.push({
288
- name,
289
- expression: this.buildPolyfillExpression(object, property),
290
- });
291
- }
292
-
293
- return patterns;
294
- }
295
-
296
- /**
297
- * @return {Pattern[]}
298
- */
299
- static getTransformPatterns() {
300
- return [
301
- {
302
- name: '@babel/plugin-transform-classes',
303
- expression: 'Cannot call a class as a function',
304
- estimateBytes: result => 150 + result.count * '_classCallCheck()'.length,
305
- },
306
- {
307
- name: '@babel/plugin-transform-regenerator',
308
- expression: /regeneratorRuntime\(?\)?\.a?wrap/.source,
309
- // Example of this transform: https://gist.github.com/connorjclark/af8bccfff377ac44efc104a79bc75da2
310
- // `regeneratorRuntime.awrap` is generated for every usage of `await`, and adds ~80 bytes each.
311
- estimateBytes: result => result.count * 80,
312
- },
313
- {
314
- name: '@babel/plugin-transform-spread',
315
- expression: /\.apply\(void 0,\s?_toConsumableArray/.source,
316
- estimateBytes: result => 1169 + result.count * '_toConsumableArray()'.length,
317
- },
318
- ];
319
- }
320
-
321
- /**
322
- * Returns a collection of match results grouped by script url.
323
- *
324
- * @param {CodePatternMatcher} matcher
325
- * @param {LH.Artifacts['Scripts']} scripts
326
- * @param {LH.Artifacts.Bundle[]} bundles
327
- * @return {Map<LH.Artifacts.Script, PatternMatchResult[]>}
328
- */
329
- static detectAcrossScripts(matcher, scripts, bundles) {
330
- /** @type {Map<LH.Artifacts.Script, PatternMatchResult[]>} */
331
- const scriptToMatchResults = new Map();
332
- const polyfillData = this.getPolyfillData();
333
-
334
- for (const script of Object.values(scripts)) {
335
- if (!script.content) continue;
336
-
337
- // Start with pattern matching against the downloaded script.
338
- const matches = matcher.match(script.content);
339
-
340
- // If it's a bundle with source maps, add in the polyfill modules by name too.
341
- const bundle = bundles.find(b => b.script.scriptId === script.scriptId);
342
- if (bundle) {
343
- for (const {name, modules} of polyfillData) {
344
- // Skip if the pattern matching found a match for this polyfill.
345
- if (matches.some(m => m.name === name)) continue;
346
-
347
- const source = bundle.rawMap.sources.find(source => modules.some(module => {
348
- return source.endsWith(`/${module}.js`) || source.includes(`node_modules/${module}/`);
349
- }));
350
- if (!source) continue;
351
-
352
- const mapping = bundle.map.mappings().find(m => m.sourceURL === source);
353
- if (mapping) {
354
- matches.push({name, line: mapping.lineNumber, column: mapping.columnNumber, count: 1});
355
- } else {
356
- matches.push({name, line: 0, column: 0, count: 1});
357
- }
358
- }
359
- }
360
-
361
- if (!matches.length) continue;
362
- scriptToMatchResults.set(script, matches);
363
- }
364
-
365
- return scriptToMatchResults;
366
- }
367
-
368
- /**
369
- * @param {PatternMatchResult[]} matches
370
- * @return {number}
371
- */
372
- static estimateWastedBytes(matches) {
373
- // Split up results based on polyfill / transform. Only transforms start with @.
374
- const polyfillResults = matches.filter(m => !m.name.startsWith('@'));
375
- const transformResults = matches.filter(m => m.name.startsWith('@'));
376
-
377
- let estimatedWastedBytesFromPolyfills = 0;
378
- const modulesSeen = new Set();
379
- for (const result of polyfillResults) {
380
- const modules = graph.dependencies[result.name];
381
- if (!modules) continue; // Shouldn't happen.
382
- for (const module of modules) {
383
- modulesSeen.add(module);
384
- }
385
- }
386
-
387
- estimatedWastedBytesFromPolyfills += [...modulesSeen].reduce((acc, moduleIndex) => {
388
- return acc + graph.moduleSizes[moduleIndex];
389
- }, 0);
390
- estimatedWastedBytesFromPolyfills = Math.min(estimatedWastedBytesFromPolyfills, graph.maxSize);
391
-
392
- let estimatedWastedBytesFromTransforms = 0;
393
-
394
- for (const result of transformResults) {
395
- const pattern = this.getTransformPatterns().find(p => p.name === result.name);
396
- if (!pattern || !pattern.estimateBytes) continue;
397
- estimatedWastedBytesFromTransforms += pattern.estimateBytes(result);
398
- }
399
-
400
- const estimatedWastedBytes =
401
- estimatedWastedBytesFromPolyfills + estimatedWastedBytesFromTransforms;
402
- return estimatedWastedBytes;
403
- }
404
-
405
56
  /**
406
57
  * @param {LH.Artifacts} artifacts
407
58
  * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
@@ -418,20 +69,18 @@ class LegacyJavascript extends ByteEfficiencyAudit {
418
69
  /** @type {Item[]} */
419
70
  const items = [];
420
71
 
421
- const matcher = new CodePatternMatcher([
422
- ...this.getPolyfillPatterns(),
423
- ...this.getTransformPatterns(),
424
- ]);
425
-
426
72
  /** @type {Map<string, number>} */
427
73
  const compressionRatioByUrl = new Map();
428
74
 
429
- const scriptToMatchResults =
430
- this.detectAcrossScripts(matcher, artifacts.Scripts, bundles);
431
- for (const [script, matches] of scriptToMatchResults.entries()) {
75
+ for (const script of artifacts.Scripts) {
76
+ const bundle = bundles.find(bundle => bundle.script.scriptId === script.scriptId);
77
+ const {matches, estimatedByteSavings} =
78
+ detectLegacyJavaScript(script.content ?? '', bundle?.map ?? null);
79
+ if (matches.length === 0) continue;
80
+
432
81
  const compressionRatio = estimateCompressionRatioForContent(
433
82
  compressionRatioByUrl, script.url, artifacts, networkRecords);
434
- const wastedBytes = Math.round(this.estimateWastedBytes(matches) * compressionRatio);
83
+ const wastedBytes = Math.round(estimatedByteSavings * compressionRatio);
435
84
  /** @type {typeof items[number]} */
436
85
  const item = {
437
86
  url: script.url,
@@ -444,7 +93,6 @@ class LegacyJavascript extends ByteEfficiencyAudit {
444
93
  totalBytes: 0,
445
94
  };
446
95
 
447
- const bundle = bundles.find(bundle => bundle.script.scriptId === script.scriptId);
448
96
  for (const match of matches) {
449
97
  const {name, line, column} = match;
450
98
  /** @type {SubItem} */
@@ -37,7 +37,7 @@ class ModernImageFormats extends ByteEfficiencyAudit {
37
37
  scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
38
38
  guidanceLevel: 3,
39
39
  requiredArtifacts: ['OptimizedImages', 'devtoolsLogs', 'traces', 'URL', 'GatherContext',
40
- 'ImageElements'],
40
+ 'ImageElements', 'SourceMaps'],
41
41
  };
42
42
  }
43
43
 
@@ -52,7 +52,7 @@ class OffscreenImages extends ByteEfficiencyAudit {
52
52
  supportedModes: ['navigation'],
53
53
  guidanceLevel: 2,
54
54
  requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext', 'devtoolsLogs',
55
- 'traces', 'URL'],
55
+ 'traces', 'URL', 'SourceMaps'],
56
56
  };
57
57
  }
58
58
 
@@ -163,12 +163,12 @@ class OffscreenImages extends ByteEfficiencyAudit {
163
163
  * @return {Promise<import('./byte-efficiency-audit.js').ByteEfficiencyProduct>}
164
164
  */
165
165
  static async audit_(artifacts, networkRecords, context) {
166
+ const {URL, SourceMaps} = artifacts;
166
167
  const images = artifacts.ImageElements;
167
168
  const viewportDimensions = artifacts.ViewportDimensions;
168
169
  const gatherContext = artifacts.GatherContext;
169
170
  const trace = artifacts.traces[ByteEfficiencyAudit.DEFAULT_PASS];
170
171
  const devtoolsLog = artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS];
171
- const URL = artifacts.URL;
172
172
 
173
173
  /** @type {string[]} */
174
174
  const warnings = [];
@@ -199,7 +199,8 @@ class OffscreenImages extends ByteEfficiencyAudit {
199
199
  const unfilteredResults = Array.from(resultsMap.values());
200
200
  // get the interactive time or fallback to getting the end of trace time
201
201
  try {
202
- const metricComputationData = {trace, devtoolsLog, gatherContext, settings, URL};
202
+ const metricComputationData =
203
+ {trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, simulator: null};
203
204
  const interactive = await Interactive.request(metricComputationData, context);
204
205
 
205
206
  // use interactive to generate items
@@ -112,7 +112,8 @@ class RenderBlockingResources extends Audit {
112
112
  // TODO: look into adding an `optionalArtifacts` property that captures the non-required nature
113
113
  // of CSSUsage
114
114
  requiredArtifacts:
115
- ['URL', 'traces', 'devtoolsLogs', 'Stylesheets', 'CSSUsage', 'GatherContext', 'Stacks'],
115
+ // eslint-disable-next-line max-len
116
+ ['URL', 'traces', 'devtoolsLogs', 'Stylesheets', 'CSSUsage', 'GatherContext', 'Stacks', 'SourceMaps'],
116
117
  };
117
118
  }
118
119
 
@@ -122,13 +123,15 @@ class RenderBlockingResources extends Audit {
122
123
  * @return {Promise<{fcpWastedMs: number, lcpWastedMs: number, results: Array<{url: string, totalBytes: number, wastedMs: number}>}>}
123
124
  */
124
125
  static async computeResults(artifacts, context) {
126
+ const settings = context.settings;
125
127
  const gatherContext = artifacts.GatherContext;
126
128
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
127
129
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
130
+ const SourceMaps = artifacts.SourceMaps;
128
131
  const simulatorData = {devtoolsLog, settings: context.settings};
129
132
  const simulator = await LoadSimulator.request(simulatorData, context);
130
133
  const wastedCssBytes = await RenderBlockingResources.computeWastedCSSBytes(artifacts, context);
131
- const navInsights = await NavigationInsights.request(trace, context);
134
+ const navInsights = await NavigationInsights.request({trace, settings, SourceMaps}, context);
132
135
 
133
136
  const renderBlocking = navInsights.model.RenderBlocking;
134
137
  if (renderBlocking instanceof Error) throw renderBlocking;
@@ -140,7 +143,7 @@ class RenderBlockingResources extends Audit {
140
143
  };
141
144
 
142
145
  const metricComputationData = {trace, devtoolsLog, gatherContext, simulator,
143
- settings: metricSettings, URL: artifacts.URL};
146
+ settings: metricSettings, URL: artifacts.URL, SourceMaps: artifacts.SourceMaps};
144
147
 
145
148
  // Cast to just `LanternMetric` since we explicitly set `throttlingMethod: 'simulate'`.
146
149
  const fcpSimulation = /** @type {LH.Artifacts.LanternMetric} */
@@ -37,7 +37,8 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
37
37
  description: str_(UIStrings.description),
38
38
  scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
39
39
  guidanceLevel: 3,
40
- requiredArtifacts: ['Stylesheets', 'devtoolsLogs', 'traces', 'URL', 'GatherContext'],
40
+ requiredArtifacts: ['Stylesheets', 'devtoolsLogs', 'traces', 'URL', 'GatherContext',
41
+ 'SourceMaps'],
41
42
  };
42
43
  }
43
44
 
@@ -44,7 +44,8 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
44
44
  description: str_(UIStrings.description),
45
45
  scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
46
46
  guidanceLevel: 3,
47
- requiredArtifacts: ['Scripts', 'devtoolsLogs', 'traces', 'GatherContext', 'URL'],
47
+ requiredArtifacts: ['Scripts', 'devtoolsLogs', 'traces', 'GatherContext', 'URL',
48
+ 'SourceMaps'],
48
49
  };
49
50
  }
50
51
 
@@ -35,7 +35,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
35
35
  scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
36
36
  guidanceLevel: 1,
37
37
  requiredArtifacts:
38
- ['Stylesheets', 'CSSUsage', 'URL', 'devtoolsLogs', 'traces', 'GatherContext'],
38
+ ['Stylesheets', 'CSSUsage', 'URL', 'devtoolsLogs', 'traces', 'GatherContext', 'SourceMaps'],
39
39
  };
40
40
  }
41
41