lighthouse 12.6.1 → 12.7.0-dev.20250627
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.
- package/cli/test/smokehouse/core-tests.js +2 -0
- package/cli/test/smokehouse/frontends/lib.js +0 -2
- package/cli/test/smokehouse/frontends/smokehouse-bin.js +1 -3
- package/cli/test/smokehouse/lighthouse-runners/cli.js +1 -1
- package/cli/test/smokehouse/smokehouse.js +0 -2
- package/core/audits/audit.d.ts +7 -0
- package/core/audits/audit.js +17 -2
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +6 -8
- package/core/audits/byte-efficiency/legacy-javascript.js +0 -1
- package/core/audits/byte-efficiency/render-blocking-resources.js +1 -1
- package/core/audits/deprecations.js +0 -2
- package/core/audits/insights/cache-insight.js +2 -1
- package/core/audits/insights/cls-culprits-insight.js +6 -7
- package/core/audits/insights/{interaction-to-next-paint-insight.d.ts → inp-breakdown-insight.d.ts} +3 -3
- package/core/audits/insights/{interaction-to-next-paint-insight.js → inp-breakdown-insight.js} +10 -10
- package/core/audits/insights/insight-audit.d.ts +7 -4
- package/core/audits/insights/insight-audit.js +48 -7
- package/core/audits/insights/lcp-breakdown-insight.d.ts +16 -0
- package/core/audits/insights/{lcp-phases-insight.js → lcp-breakdown-insight.js} +19 -19
- package/core/audits/insights/modern-http-insight.js +0 -2
- package/core/audits/insights/network-dependency-tree-insight.js +85 -8
- package/core/audits/insights/render-blocking-insight.js +1 -1
- package/core/audits/layout-shifts.js +5 -4
- package/core/audits/seo/crawlable-anchors.js +27 -0
- package/core/audits/seo/link-text.js +130 -83
- package/core/audits/third-party-cookies.js +0 -2
- package/core/audits/valid-source-maps.js +0 -2
- package/core/computed/js-bundles.js +0 -1
- package/core/computed/metrics/cumulative-layout-shift.js +1 -1
- package/core/computed/metrics/lantern-metric.js +4 -3
- package/core/computed/metrics/timing-summary.js +4 -1
- package/core/computed/page-dependency-graph.js +3 -3
- package/core/computed/trace-engine-result.js +1 -2
- package/core/config/config.js +1 -1
- package/core/config/default-config.js +4 -4
- package/core/config/experimental-config.js +2 -2
- package/core/config/filters.js +7 -0
- package/core/config/validation.js +4 -0
- package/core/gather/base-artifacts.js +3 -0
- package/core/gather/driver/environment.d.ts +6 -0
- package/core/gather/driver/environment.js +17 -0
- package/core/gather/driver/execution-context.d.ts +3 -1
- package/core/gather/driver/execution-context.js +3 -1
- package/core/gather/driver/navigation.js +1 -3
- package/core/gather/driver/wait-for-condition.js +0 -1
- package/core/gather/fetcher.js +0 -2
- package/core/gather/gatherers/accessibility.js +1 -1
- package/core/gather/gatherers/anchor-elements.js +61 -2
- package/core/gather/gatherers/cache-contents.js +0 -2
- package/core/gather/gatherers/css-usage.js +3 -1
- package/core/gather/gatherers/dobetterweb/doctype.js +0 -2
- package/core/gather/gatherers/dobetterweb/domstats.js +1 -1
- package/core/gather/gatherers/full-page-screenshot.js +1 -1
- package/core/gather/gatherers/image-elements.js +1 -1
- package/core/gather/gatherers/inspector-issues.js +1 -1
- package/core/gather/gatherers/link-elements.js +1 -1
- package/core/gather/gatherers/stacks.js +0 -1
- package/core/gather/gatherers/stylesheets.js +3 -1
- package/core/gather/gatherers/trace-elements.d.ts +3 -4
- package/core/gather/gatherers/trace-elements.js +13 -43
- package/core/gather/gatherers/viewport-dimensions.js +0 -2
- package/core/index.cjs +0 -1
- package/core/index.d.cts +5 -0
- package/core/lib/asset-saver.d.ts +1 -1
- package/core/lib/asset-saver.js +20 -8
- package/core/lib/bf-cache-strings.js +4 -1
- package/core/lib/deprecations-strings.d.ts +51 -47
- package/core/lib/deprecations-strings.js +14 -8
- package/core/lib/i18n/i18n.js +0 -2
- package/core/lib/lantern-trace-saver.js +1 -1
- package/core/lib/lh-error.js +0 -1
- package/core/lib/manifest-parser.js +0 -2
- package/core/lib/minify-devtoolslog.js +0 -2
- package/core/lib/sentry.d.ts +1 -1
- package/core/lib/sentry.js +2 -2
- package/core/runner.js +11 -8
- package/core/scoring.d.ts +186 -15
- package/core/scripts/manual-chrome-launcher.js +0 -1
- package/dist/report/bundle.esm.js +14 -12
- package/dist/report/flow.js +18 -16
- package/dist/report/standalone.js +15 -13
- package/eslint.config.mjs +242 -0
- package/flow-report/src/common.tsx +1 -0
- package/flow-report/src/i18n/i18n.tsx +1 -0
- package/flow-report/src/util.ts +2 -0
- package/package.json +23 -19
- package/readme.md +3 -2
- package/report/assets/styles.css +11 -9
- package/report/assets/templates.html +1 -1
- package/report/generator/file-namer.d.ts +5 -0
- package/report/generator/file-namer.js +1 -1
- package/report/generator/flow-report-assets.js +1 -1
- package/report/generator/report-assets.js +1 -1
- package/report/generator/report-generator.js +3 -3
- package/report/renderer/api.js +1 -0
- package/report/renderer/components.js +2 -2
- package/report/renderer/details-renderer.d.ts +5 -0
- package/report/renderer/details-renderer.js +35 -3
- package/report/renderer/dom.d.ts +2 -0
- package/report/renderer/dom.js +6 -0
- package/report/renderer/i18n-formatter.js +2 -1
- package/report/renderer/performance-category-renderer.js +2 -2
- package/report/renderer/report-renderer.js +1 -0
- package/report/renderer/report-ui-features.d.ts +1 -0
- package/report/renderer/report-ui-features.js +16 -0
- package/report/renderer/report-utils.js +3 -2
- package/report/renderer/text-encoding.js +0 -2
- package/report/renderer/topbar-features.js +1 -1
- package/report/types/report-renderer.d.ts +5 -0
- package/shared/localization/locales/ar-XB.json +57 -69
- package/shared/localization/locales/ar.json +57 -69
- package/shared/localization/locales/bg.json +57 -69
- package/shared/localization/locales/ca.json +57 -69
- package/shared/localization/locales/cs.json +57 -69
- package/shared/localization/locales/da.json +57 -69
- package/shared/localization/locales/de.json +57 -69
- package/shared/localization/locales/el.json +57 -69
- package/shared/localization/locales/en-GB.json +57 -69
- package/shared/localization/locales/en-US.json +73 -61
- package/shared/localization/locales/en-XL.json +73 -61
- package/shared/localization/locales/es-419.json +57 -69
- package/shared/localization/locales/es.json +56 -68
- package/shared/localization/locales/fi.json +57 -69
- package/shared/localization/locales/fil.json +57 -69
- package/shared/localization/locales/fr.json +57 -69
- package/shared/localization/locales/he.json +57 -69
- package/shared/localization/locales/hi.json +57 -69
- package/shared/localization/locales/hr.json +57 -69
- package/shared/localization/locales/hu.json +56 -68
- package/shared/localization/locales/id.json +57 -69
- package/shared/localization/locales/it.json +56 -68
- package/shared/localization/locales/ja.json +57 -69
- package/shared/localization/locales/ko.json +57 -69
- package/shared/localization/locales/lt.json +57 -69
- package/shared/localization/locales/lv.json +57 -69
- package/shared/localization/locales/nl.json +57 -69
- package/shared/localization/locales/no.json +57 -69
- package/shared/localization/locales/pl.json +56 -68
- package/shared/localization/locales/pt-PT.json +57 -69
- package/shared/localization/locales/pt.json +57 -69
- package/shared/localization/locales/ro.json +57 -69
- package/shared/localization/locales/ru.json +58 -70
- package/shared/localization/locales/sk.json +57 -69
- package/shared/localization/locales/sl.json +56 -68
- package/shared/localization/locales/sr-Latn.json +57 -69
- package/shared/localization/locales/sr.json +57 -69
- package/shared/localization/locales/sv.json +57 -69
- package/shared/localization/locales/ta.json +57 -69
- package/shared/localization/locales/te.json +57 -69
- package/shared/localization/locales/th.json +56 -68
- package/shared/localization/locales/tr.json +57 -69
- package/shared/localization/locales/uk.json +57 -69
- package/shared/localization/locales/vi.json +57 -69
- package/shared/localization/locales/zh-HK.json +57 -69
- package/shared/localization/locales/zh-TW.json +56 -68
- package/shared/localization/locales/zh.json +57 -69
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -1
- package/types/artifacts.d.ts +8 -1
- package/types/internal/test.d.ts +1 -1
- package/types/lhr/audit-details.d.ts +13 -3
- package/types/lhr/lhr.d.ts +8 -1
- package/core/audits/insights/lcp-phases-insight.d.ts +0 -16
|
@@ -61,6 +61,7 @@ import screenshot from './test-definitions/screenshot.js';
|
|
|
61
61
|
import seoFailing from './test-definitions/seo-failing.js';
|
|
62
62
|
import seoPassing from './test-definitions/seo-passing.js';
|
|
63
63
|
import seoStatus403 from './test-definitions/seo-status-403.js';
|
|
64
|
+
import seoMixedLanguage from './test-definitions/seo-mixed-language.js';
|
|
64
65
|
import serviceWorkerReloaded from './test-definitions/service-worker-reloaded.js';
|
|
65
66
|
import shiftAttribution from './test-definitions/shift-attribution.js';
|
|
66
67
|
import sourceMaps from './test-definitions/source-maps.js';
|
|
@@ -125,6 +126,7 @@ const smokeTests = [
|
|
|
125
126
|
seoFailing,
|
|
126
127
|
seoPassing,
|
|
127
128
|
seoStatus403,
|
|
129
|
+
seoMixedLanguage,
|
|
128
130
|
serviceWorkerReloaded,
|
|
129
131
|
shiftAttribution,
|
|
130
132
|
sourceMaps,
|
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
* flags, start fixture webservers, then run smokehouse.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
/* eslint-disable no-console */
|
|
14
|
-
|
|
15
13
|
import path from 'path';
|
|
16
14
|
import fs from 'fs';
|
|
17
15
|
import url from 'url';
|
|
@@ -173,7 +171,7 @@ async function begin() {
|
|
|
173
171
|
},
|
|
174
172
|
'no-headless': {
|
|
175
173
|
type: 'boolean',
|
|
176
|
-
describe: 'Launch Chrome in typical desktop headful mode, rather than our default of `--headless=new` (https://developer.chrome.com/articles/new-headless/).',
|
|
174
|
+
describe: 'Launch Chrome in typical desktop headful mode, rather than our default of `--headless=new` (https://developer.chrome.com/articles/new-headless/).',
|
|
177
175
|
},
|
|
178
176
|
})
|
|
179
177
|
.wrap(y.terminalWidth())
|
package/core/audits/audit.d.ts
CHANGED
|
@@ -83,6 +83,13 @@ export class Audit {
|
|
|
83
83
|
* @return {LH.Audit.Details.List}
|
|
84
84
|
*/
|
|
85
85
|
static makeListDetails(items: LH.Audit.Details.List["items"]): LH.Audit.Details.List;
|
|
86
|
+
/**
|
|
87
|
+
* @param {LH.IcuMessage | string=} title
|
|
88
|
+
* @param {LH.IcuMessage | string=} description
|
|
89
|
+
* @param {LH.Audit.Details.ListableDetail} value
|
|
90
|
+
* @return {LH.Audit.Details.ListSectionItem}
|
|
91
|
+
*/
|
|
92
|
+
static makeListDetailSectionItem(value: LH.Audit.Details.ListableDetail, title?: (LH.IcuMessage | string) | undefined, description?: (LH.IcuMessage | string) | undefined): LH.Audit.Details.ListSectionItem;
|
|
86
93
|
/** @typedef {{
|
|
87
94
|
* content: string;
|
|
88
95
|
* title: string;
|
package/core/audits/audit.js
CHANGED
|
@@ -164,7 +164,7 @@ class Audit {
|
|
|
164
164
|
if (results.length === 0) {
|
|
165
165
|
return {
|
|
166
166
|
type: 'table',
|
|
167
|
-
headings
|
|
167
|
+
headings,
|
|
168
168
|
items: [],
|
|
169
169
|
summary,
|
|
170
170
|
};
|
|
@@ -174,7 +174,7 @@ class Audit {
|
|
|
174
174
|
|
|
175
175
|
return {
|
|
176
176
|
type: 'table',
|
|
177
|
-
headings
|
|
177
|
+
headings,
|
|
178
178
|
items: results,
|
|
179
179
|
summary,
|
|
180
180
|
sortedBy,
|
|
@@ -194,6 +194,21 @@ class Audit {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
/**
|
|
198
|
+
* @param {LH.IcuMessage | string=} title
|
|
199
|
+
* @param {LH.IcuMessage | string=} description
|
|
200
|
+
* @param {LH.Audit.Details.ListableDetail} value
|
|
201
|
+
* @return {LH.Audit.Details.ListSectionItem}
|
|
202
|
+
*/
|
|
203
|
+
static makeListDetailSectionItem(value, title, description) {
|
|
204
|
+
return {
|
|
205
|
+
type: 'list-section',
|
|
206
|
+
title,
|
|
207
|
+
description,
|
|
208
|
+
value,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
197
212
|
/** @typedef {{
|
|
198
213
|
* content: string;
|
|
199
214
|
* title: string;
|
|
@@ -8,8 +8,8 @@ import {Audit} from '../audit.js';
|
|
|
8
8
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
9
9
|
import {NetworkRecords} from '../../computed/network-records.js';
|
|
10
10
|
import {LoadSimulator} from '../../computed/load-simulator.js';
|
|
11
|
-
import {LanternLargestContentfulPaint} from '../../computed/metrics/lantern-largest-contentful-paint.js';
|
|
12
|
-
import {LanternFirstContentfulPaint} from '../../computed/metrics/lantern-first-contentful-paint.js';
|
|
11
|
+
import {LanternLargestContentfulPaint as LanternLCP} from '../../computed/metrics/lantern-largest-contentful-paint.js';
|
|
12
|
+
import {LanternFirstContentfulPaint as LanternFCP} from '../../computed/metrics/lantern-first-contentful-paint.js';
|
|
13
13
|
import {LCPImageRecord} from '../../computed/lcp-image-record.js';
|
|
14
14
|
|
|
15
15
|
const str_ = i18n.createIcuMessageFn(import.meta.url, {});
|
|
@@ -168,12 +168,10 @@ class ByteEfficiencyAudit extends Audit {
|
|
|
168
168
|
// This is useful information in the LHR and should be preserved.
|
|
169
169
|
let wastedMs;
|
|
170
170
|
if (metricComputationInput.gatherContext.gatherMode === 'navigation') {
|
|
171
|
-
const
|
|
172
|
-
optimisticGraph
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
optimisticGraph: optimisticLCPGraph,
|
|
176
|
-
} = await LanternLargestContentfulPaint.request(metricComputationInput, context);
|
|
171
|
+
const optimisticFCPGraph = (await LanternFCP.request(metricComputationInput, context))
|
|
172
|
+
.optimisticGraph;
|
|
173
|
+
const optimisticLCPGraph = (await LanternLCP.request(metricComputationInput, context))
|
|
174
|
+
.optimisticGraph;
|
|
177
175
|
|
|
178
176
|
const {savings: fcpSavings} = this.computeWasteWithGraph(
|
|
179
177
|
results,
|
|
@@ -25,7 +25,6 @@ import {detectLegacyJavaScript} from '../../lib/legacy-javascript/legacy-javascr
|
|
|
25
25
|
const UIStrings = {
|
|
26
26
|
/** 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. */
|
|
27
27
|
title: 'Avoid serving legacy JavaScript to modern browsers',
|
|
28
|
-
// eslint-disable-next-line max-len
|
|
29
28
|
// TODO: developer.chrome.com article. this codelab is good starting place: https://web.dev/articles/codelab-serve-modern-code
|
|
30
29
|
/** 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. */
|
|
31
30
|
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/)',
|
|
@@ -291,7 +291,7 @@ class RenderBlockingResources extends Audit {
|
|
|
291
291
|
const headings = [
|
|
292
292
|
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
293
293
|
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize)},
|
|
294
|
-
{key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.
|
|
294
|
+
{key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnDuration)},
|
|
295
295
|
];
|
|
296
296
|
|
|
297
297
|
const details = Audit.makeOpportunityDetails(headings, results,
|
|
@@ -13,7 +13,6 @@ import {JSBundles} from '../computed/js-bundles.js';
|
|
|
13
13
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
14
14
|
import {getIssueDetailDescription} from '../lib/deprecation-description.js';
|
|
15
15
|
|
|
16
|
-
/* eslint-disable max-len */
|
|
17
16
|
const UIStrings = {
|
|
18
17
|
/** Title of a Lighthouse audit that provides detail on the use of deprecated APIs. This descriptive title is shown to users when the page does not use deprecated APIs. */
|
|
19
18
|
title: 'Avoids deprecated APIs',
|
|
@@ -32,7 +31,6 @@ const UIStrings = {
|
|
|
32
31
|
/** Table column header for line of code (eg. 432) that is using a deprecated API. */
|
|
33
32
|
columnLine: 'Line',
|
|
34
33
|
};
|
|
35
|
-
/* eslint-enable max-len */
|
|
36
34
|
|
|
37
35
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
38
36
|
|
|
@@ -41,7 +41,7 @@ class CacheInsight extends Audit {
|
|
|
41
41
|
/* eslint-disable max-len */
|
|
42
42
|
{key: 'url', valueType: 'url', label: str_(UIStrings.requestColumn)},
|
|
43
43
|
{key: 'cacheLifetimeMs', valueType: 'ms', label: str_(UIStrings.cacheTTL), displayUnit: 'duration'},
|
|
44
|
-
{key: '
|
|
44
|
+
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize), displayUnit: 'kb', granularity: 1},
|
|
45
45
|
/* eslint-enable max-len */
|
|
46
46
|
];
|
|
47
47
|
// TODO: this should be the sorting in the model (instead it sorts by transfer size...)
|
|
@@ -50,6 +50,7 @@ class CacheInsight extends Audit {
|
|
|
50
50
|
const items = values.map(value => ({
|
|
51
51
|
url: value.request.args.data.url,
|
|
52
52
|
cacheLifetimeMs: value.ttl * 1000,
|
|
53
|
+
totalBytes: value.request.args.data.encodedDataLength || 0,
|
|
53
54
|
wastedBytes: value.wastedBytes,
|
|
54
55
|
}));
|
|
55
56
|
return Audit.makeTableDetails(headings, items, {
|
|
@@ -19,12 +19,10 @@ const MAX_LAYOUT_SHIFTS_PER_CLUSTER = 5;
|
|
|
19
19
|
// eslint-disable-next-line max-len
|
|
20
20
|
const insightStr_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js', InsightUIStrings);
|
|
21
21
|
|
|
22
|
-
/* eslint-disable max-len */
|
|
23
22
|
const UIStrings = {
|
|
24
23
|
/** Label for a column in a data table; entries in this column will be a number representing how large the layout shift was. */
|
|
25
24
|
columnScore: 'Layout shift score',
|
|
26
25
|
};
|
|
27
|
-
/* eslint-enable max-len */
|
|
28
26
|
|
|
29
27
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
30
28
|
|
|
@@ -61,18 +59,19 @@ class CLSCulpritsInsight extends Audit {
|
|
|
61
59
|
for (const unsizedImage of culprits.unsizedImages) {
|
|
62
60
|
subItems.push({
|
|
63
61
|
extra: makeNodeItemForNodeId(TraceElements, unsizedImage.backendNodeId),
|
|
64
|
-
cause: insightStr_(InsightUIStrings.
|
|
62
|
+
cause: insightStr_(InsightUIStrings.unsizedImage),
|
|
65
63
|
});
|
|
66
64
|
}
|
|
67
|
-
for (const request of culprits.
|
|
65
|
+
for (const request of culprits.webFonts) {
|
|
68
66
|
const url = request.args.data.url;
|
|
69
67
|
subItems.push({
|
|
70
68
|
extra: {type: 'url', value: url},
|
|
71
|
-
cause: insightStr_(InsightUIStrings.
|
|
69
|
+
cause: insightStr_(InsightUIStrings.webFont),
|
|
72
70
|
});
|
|
73
71
|
}
|
|
74
|
-
|
|
72
|
+
for (const iframe of culprits.iframes) {
|
|
75
73
|
subItems.push({
|
|
74
|
+
extra: iframe.url ? {type: 'url', value: iframe.url} : undefined,
|
|
76
75
|
cause: insightStr_(InsightUIStrings.injectedIframe),
|
|
77
76
|
});
|
|
78
77
|
}
|
|
@@ -114,7 +113,7 @@ class CLSCulpritsInsight extends Audit {
|
|
|
114
113
|
/** @type {LH.Audit.Details.Table['items']} */
|
|
115
114
|
const items = events.map(event => {
|
|
116
115
|
const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent(
|
|
117
|
-
event.args.data.impacted_nodes || [], impactByNodeId
|
|
116
|
+
event.args.data.impacted_nodes || [], impactByNodeId);
|
|
118
117
|
return {
|
|
119
118
|
node: makeNodeItemForNodeId(artifacts.TraceElements, biggestImpactNodeId),
|
|
120
119
|
score: event.args.data?.weighted_score_delta,
|
package/core/audits/insights/{interaction-to-next-paint-insight.d.ts → inp-breakdown-insight.d.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export default
|
|
2
|
-
declare class
|
|
1
|
+
export default INPBreakdownInsight;
|
|
2
|
+
declare class INPBreakdownInsight extends Audit {
|
|
3
3
|
/**
|
|
4
4
|
* @param {LH.Artifacts} artifacts
|
|
5
5
|
* @param {LH.Audit.Context} context
|
|
@@ -8,4 +8,4 @@ declare class InteractionToNextPaintInsight extends Audit {
|
|
|
8
8
|
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
9
9
|
}
|
|
10
10
|
import { Audit } from '../audit.js';
|
|
11
|
-
//# sourceMappingURL=
|
|
11
|
+
//# sourceMappingURL=inp-breakdown-insight.d.ts.map
|
package/core/audits/insights/{interaction-to-next-paint-insight.js → inp-breakdown-insight.js}
RENAMED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/
|
|
7
|
+
import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/INPBreakdown.js';
|
|
8
8
|
|
|
9
9
|
import {Audit} from '../audit.js';
|
|
10
10
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
11
11
|
import {adaptInsightToAuditProduct, makeNodeItemForNodeId} from './insight-audit.js';
|
|
12
12
|
|
|
13
13
|
// eslint-disable-next-line max-len
|
|
14
|
-
const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/
|
|
14
|
+
const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/INPBreakdown.js', UIStrings);
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
class INPBreakdownInsight extends Audit {
|
|
17
17
|
/**
|
|
18
18
|
* @return {LH.Audit.Meta}
|
|
19
19
|
*/
|
|
20
20
|
static get meta() {
|
|
21
21
|
return {
|
|
22
|
-
id: '
|
|
22
|
+
id: 'inp-breakdown-insight',
|
|
23
23
|
title: str_(UIStrings.title),
|
|
24
24
|
failureTitle: str_(UIStrings.title),
|
|
25
25
|
description: str_(UIStrings.description),
|
|
@@ -35,7 +35,7 @@ class InteractionToNextPaintInsight extends Audit {
|
|
|
35
35
|
* @return {Promise<LH.Audit.Product>}
|
|
36
36
|
*/
|
|
37
37
|
static async audit(artifacts, context) {
|
|
38
|
-
return adaptInsightToAuditProduct(artifacts, context, '
|
|
38
|
+
return adaptInsightToAuditProduct(artifacts, context, 'INPBreakdown', (insight) => {
|
|
39
39
|
const event = insight.longestInteractionEvent;
|
|
40
40
|
if (!event) {
|
|
41
41
|
// TODO: show UIStrings.noInteractions?
|
|
@@ -44,16 +44,16 @@ class InteractionToNextPaintInsight extends Audit {
|
|
|
44
44
|
|
|
45
45
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
46
46
|
const headings = [
|
|
47
|
-
{key: 'label', valueType: 'text', label: str_(UIStrings.
|
|
47
|
+
{key: 'label', valueType: 'text', label: str_(UIStrings.subpart)},
|
|
48
48
|
{key: 'duration', valueType: 'ms', label: str_(i18n.UIStrings.columnDuration)},
|
|
49
49
|
];
|
|
50
50
|
|
|
51
51
|
/** @type {LH.Audit.Details.Table['items']} */
|
|
52
52
|
const items = [
|
|
53
53
|
/* eslint-disable max-len */
|
|
54
|
-
{
|
|
55
|
-
{
|
|
56
|
-
{
|
|
54
|
+
{subpart: 'inputDelay', label: str_(UIStrings.inputDelay), duration: event.inputDelay / 1000},
|
|
55
|
+
{subpart: 'processingDuration', label: str_(UIStrings.processingDuration), duration: event.mainThreadHandling / 1000},
|
|
56
|
+
{subpart: 'presentationDelay', label: str_(UIStrings.presentationDelay), duration: event.presentationDelay / 1000},
|
|
57
57
|
/* eslint-enable max-len */
|
|
58
58
|
];
|
|
59
59
|
|
|
@@ -65,4 +65,4 @@ class InteractionToNextPaintInsight extends Audit {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export default
|
|
68
|
+
export default INPBreakdownInsight;
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
export type CreateDetailsExtras = {
|
|
2
2
|
insights: import("@paulirish/trace_engine/models/trace/insights/types.js").InsightSet;
|
|
3
|
-
parsedTrace: LH.Artifacts.TraceEngineResult["
|
|
3
|
+
parsedTrace: LH.Artifacts.TraceEngineResult["parsedTrace"];
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
6
6
|
* @typedef CreateDetailsExtras
|
|
7
7
|
* @property {import('@paulirish/trace_engine/models/trace/insights/types.js').InsightSet} insights
|
|
8
|
-
* @property {LH.Artifacts.TraceEngineResult['
|
|
8
|
+
* @property {LH.Artifacts.TraceEngineResult['parsedTrace']} parsedTrace
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
11
|
* @param {LH.Artifacts} artifacts
|
|
12
12
|
* @param {LH.Audit.Context} context
|
|
13
13
|
* @param {T} insightName
|
|
14
|
-
* @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => LH.Audit.Details|undefined} createDetails
|
|
14
|
+
* @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => {details: LH.Audit.Details, warnings: Array<string | LH.IcuMessage>}|LH.Audit.Details|undefined} createDetails
|
|
15
15
|
* @template {keyof import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModelsType} T
|
|
16
16
|
* @return {Promise<LH.Audit.Product>}
|
|
17
17
|
*/
|
|
18
|
-
export function adaptInsightToAuditProduct<T extends keyof import("@paulirish/trace_engine/models/trace/insights/types.js").InsightModelsType>(artifacts: LH.Artifacts, context: LH.Audit.Context, insightName: T, createDetails: (insight: import("@paulirish/trace_engine/models/trace/insights/types.js").InsightModels[T], extras: CreateDetailsExtras) =>
|
|
18
|
+
export function adaptInsightToAuditProduct<T extends keyof import("@paulirish/trace_engine/models/trace/insights/types.js").InsightModelsType>(artifacts: LH.Artifacts, context: LH.Audit.Context, insightName: T, createDetails: (insight: import("@paulirish/trace_engine/models/trace/insights/types.js").InsightModels[T], extras: CreateDetailsExtras) => {
|
|
19
|
+
details: LH.Audit.Details;
|
|
20
|
+
warnings: Array<string | LH.IcuMessage>;
|
|
21
|
+
} | LH.Audit.Details | undefined): Promise<LH.Audit.Product>;
|
|
19
22
|
/**
|
|
20
23
|
* @param {LH.Artifacts.TraceElement[]} traceElements
|
|
21
24
|
* @param {number|null|undefined} nodeId
|
|
@@ -16,7 +16,7 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, {});
|
|
|
16
16
|
/**
|
|
17
17
|
* @param {LH.Artifacts} artifacts
|
|
18
18
|
* @param {LH.Audit.Context} context
|
|
19
|
-
* @return {Promise<{insights: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightSet|undefined, parsedTrace: LH.Artifacts.TraceEngineResult['
|
|
19
|
+
* @return {Promise<{insights: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightSet|undefined, parsedTrace: LH.Artifacts.TraceEngineResult['parsedTrace']}>}
|
|
20
20
|
*/
|
|
21
21
|
async function getInsightSet(artifacts, context) {
|
|
22
22
|
const settings = context.settings;
|
|
@@ -29,20 +29,20 @@ async function getInsightSet(artifacts, context) {
|
|
|
29
29
|
const key = navigationId ?? NO_NAVIGATION;
|
|
30
30
|
const insights = traceEngineResult.insights.get(key);
|
|
31
31
|
|
|
32
|
-
return {insights, parsedTrace: traceEngineResult.
|
|
32
|
+
return {insights, parsedTrace: traceEngineResult.parsedTrace};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* @typedef CreateDetailsExtras
|
|
37
37
|
* @property {import('@paulirish/trace_engine/models/trace/insights/types.js').InsightSet} insights
|
|
38
|
-
* @property {LH.Artifacts.TraceEngineResult['
|
|
38
|
+
* @property {LH.Artifacts.TraceEngineResult['parsedTrace']} parsedTrace
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* @param {LH.Artifacts} artifacts
|
|
43
43
|
* @param {LH.Audit.Context} context
|
|
44
44
|
* @param {T} insightName
|
|
45
|
-
* @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => LH.Audit.Details|undefined} createDetails
|
|
45
|
+
* @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T], extras: CreateDetailsExtras) => {details: LH.Audit.Details, warnings: Array<string | LH.IcuMessage>}|LH.Audit.Details|undefined} createDetails
|
|
46
46
|
* @template {keyof import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModelsType} T
|
|
47
47
|
* @return {Promise<LH.Audit.Product>}
|
|
48
48
|
*/
|
|
@@ -64,14 +64,26 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const
|
|
67
|
+
const cbResult = createDetails(insight, {
|
|
68
68
|
parsedTrace,
|
|
69
69
|
insights,
|
|
70
70
|
});
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
const warnings = [...insight.warnings ?? []];
|
|
73
|
+
|
|
74
|
+
let details;
|
|
75
|
+
if (cbResult && 'warnings' in cbResult) {
|
|
76
|
+
details = cbResult.details;
|
|
77
|
+
warnings.push(...cbResult.warnings);
|
|
78
|
+
} else {
|
|
79
|
+
details = cbResult;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!details || (details.type === 'table' && details.items.length === 0)) {
|
|
72
83
|
return {
|
|
73
84
|
scoreDisplayMode: Audit.SCORING_MODES.NOT_APPLICABLE,
|
|
74
85
|
score: null,
|
|
86
|
+
details,
|
|
75
87
|
};
|
|
76
88
|
}
|
|
77
89
|
|
|
@@ -96,8 +108,37 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
|
|
|
96
108
|
// TODO: consider adding a `estimatedSavingsText` to InsightModel, which can capture
|
|
97
109
|
// the exact i18n string used by RPP; and include the same est. timing savings.
|
|
98
110
|
let displayValue;
|
|
111
|
+
|
|
99
112
|
if (insight.wastedBytes) {
|
|
100
113
|
displayValue = str_(i18n.UIStrings.displayValueByteSavings, {wastedBytes: insight.wastedBytes});
|
|
114
|
+
} else {
|
|
115
|
+
let wastedMs;
|
|
116
|
+
|
|
117
|
+
switch (insight.insightKey) {
|
|
118
|
+
case 'DocumentLatency':
|
|
119
|
+
case 'DuplicatedJavaScript':
|
|
120
|
+
case 'FontDisplay':
|
|
121
|
+
case 'LegacyJavaScript':
|
|
122
|
+
case 'RenderBlocking': {
|
|
123
|
+
wastedMs = metricSavings?.FCP;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
case 'LCPDiscovery':
|
|
128
|
+
case 'ModernHTTP': {
|
|
129
|
+
wastedMs = metricSavings?.LCP;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case 'Viewport': {
|
|
134
|
+
wastedMs = metricSavings?.INP;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (wastedMs) {
|
|
140
|
+
displayValue = str_(i18n.UIStrings.displayValueMsSavings, {wastedMs});
|
|
141
|
+
}
|
|
101
142
|
}
|
|
102
143
|
|
|
103
144
|
let score;
|
|
@@ -115,7 +156,7 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
|
|
|
115
156
|
scoreDisplayMode,
|
|
116
157
|
score,
|
|
117
158
|
metricSavings,
|
|
118
|
-
warnings:
|
|
159
|
+
warnings: warnings.length ? warnings : undefined,
|
|
119
160
|
displayValue,
|
|
120
161
|
details,
|
|
121
162
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default LCPBreakdownInsight;
|
|
2
|
+
declare class LCPBreakdownInsight extends Audit {
|
|
3
|
+
/**
|
|
4
|
+
* @param {Required<import('@paulirish/trace_engine/models/trace/insights/LCPBreakdown.js').LCPBreakdownInsightModel>['subparts']} subparts
|
|
5
|
+
* @return {LH.Audit.Details.Table}
|
|
6
|
+
*/
|
|
7
|
+
static makeSubpartsTable(subparts: Required<import("@paulirish/trace_engine/models/trace/insights/LCPBreakdown.js").LCPBreakdownInsightModel>["subparts"]): LH.Audit.Details.Table;
|
|
8
|
+
/**
|
|
9
|
+
* @param {LH.Artifacts} artifacts
|
|
10
|
+
* @param {LH.Audit.Context} context
|
|
11
|
+
* @return {Promise<LH.Audit.Product>}
|
|
12
|
+
*/
|
|
13
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
14
|
+
}
|
|
15
|
+
import { Audit } from '../audit.js';
|
|
16
|
+
//# sourceMappingURL=lcp-breakdown-insight.d.ts.map
|
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/
|
|
7
|
+
import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/LCPBreakdown.js';
|
|
8
8
|
|
|
9
9
|
import {Audit} from '../audit.js';
|
|
10
10
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
11
11
|
import {adaptInsightToAuditProduct, makeNodeItemForNodeId} from './insight-audit.js';
|
|
12
12
|
|
|
13
13
|
// eslint-disable-next-line max-len
|
|
14
|
-
const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/
|
|
14
|
+
const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/LCPBreakdown.js', UIStrings);
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
class LCPBreakdownInsight extends Audit {
|
|
17
17
|
/**
|
|
18
18
|
* @return {LH.Audit.Meta}
|
|
19
19
|
*/
|
|
20
20
|
static get meta() {
|
|
21
21
|
return {
|
|
22
|
-
id: 'lcp-
|
|
22
|
+
id: 'lcp-breakdown-insight',
|
|
23
23
|
title: str_(UIStrings.title),
|
|
24
24
|
failureTitle: str_(UIStrings.title),
|
|
25
25
|
description: str_(UIStrings.description),
|
|
@@ -30,33 +30,33 @@ class LCPPhasesInsight extends Audit {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* @param {Required<import('@paulirish/trace_engine/models/trace/insights/
|
|
33
|
+
* @param {Required<import('@paulirish/trace_engine/models/trace/insights/LCPBreakdown.js').LCPBreakdownInsightModel>['subparts']} subparts
|
|
34
34
|
* @return {LH.Audit.Details.Table}
|
|
35
35
|
*/
|
|
36
|
-
static
|
|
37
|
-
const {ttfb, loadDelay,
|
|
36
|
+
static makeSubpartsTable(subparts) {
|
|
37
|
+
const {ttfb, loadDelay, loadDuration, renderDelay} = subparts;
|
|
38
38
|
|
|
39
39
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
40
40
|
const headings = [
|
|
41
|
-
{key: 'label', valueType: 'text', label: str_(UIStrings.
|
|
41
|
+
{key: 'label', valueType: 'text', label: str_(UIStrings.subpart)},
|
|
42
42
|
{key: 'duration', valueType: 'ms', label: str_(i18n.UIStrings.columnDuration)},
|
|
43
43
|
];
|
|
44
44
|
|
|
45
45
|
/** @type {LH.Audit.Details.Table['items']} */
|
|
46
46
|
let items = [
|
|
47
47
|
/* eslint-disable max-len */
|
|
48
|
-
{
|
|
49
|
-
{
|
|
50
|
-
{
|
|
51
|
-
{
|
|
48
|
+
{subpart: 'timeToFirstByte', label: str_(UIStrings.timeToFirstByte), duration: ttfb.range / 1000},
|
|
49
|
+
{subpart: 'resourceLoadDelay', label: str_(UIStrings.resourceLoadDelay), duration: (loadDelay?.range ?? 0) / 1000},
|
|
50
|
+
{subpart: 'resourceLoadDuration', label: str_(UIStrings.resourceLoadDuration), duration: (loadDuration?.range ?? 0) / 1000},
|
|
51
|
+
{subpart: 'elementRenderDelay', label: str_(UIStrings.elementRenderDelay), duration: renderDelay.range / 1000},
|
|
52
52
|
/* eslint-enable max-len */
|
|
53
53
|
];
|
|
54
54
|
|
|
55
55
|
if (loadDelay === undefined) {
|
|
56
|
-
items = items.filter(item => item.
|
|
56
|
+
items = items.filter(item => item.subpart !== 'resourceLoadDelay');
|
|
57
57
|
}
|
|
58
|
-
if (
|
|
59
|
-
items = items.filter(item => item.
|
|
58
|
+
if (loadDuration === undefined) {
|
|
59
|
+
items = items.filter(item => item.subpart !== 'resourceLoadDuration');
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
return Audit.makeTableDetails(headings, items);
|
|
@@ -68,17 +68,17 @@ class LCPPhasesInsight extends Audit {
|
|
|
68
68
|
* @return {Promise<LH.Audit.Product>}
|
|
69
69
|
*/
|
|
70
70
|
static async audit(artifacts, context) {
|
|
71
|
-
return adaptInsightToAuditProduct(artifacts, context, '
|
|
72
|
-
if (!insight.
|
|
71
|
+
return adaptInsightToAuditProduct(artifacts, context, 'LCPBreakdown', (insight) => {
|
|
72
|
+
if (!insight.subparts) {
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
return Audit.makeListDetails([
|
|
77
|
-
|
|
77
|
+
LCPBreakdownInsight.makeSubpartsTable(insight.subparts),
|
|
78
78
|
makeNodeItemForNodeId(artifacts.TraceElements, insight.lcpEvent?.args.data?.nodeId),
|
|
79
79
|
].filter(table => table !== undefined));
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
export default
|
|
84
|
+
export default LCPBreakdownInsight;
|
|
@@ -37,10 +37,8 @@ class ModernHTTPInsight extends Audit {
|
|
|
37
37
|
return adaptInsightToAuditProduct(artifacts, context, 'ModernHTTP', (insight) => {
|
|
38
38
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
39
39
|
const headings = [
|
|
40
|
-
/* eslint-disable max-len */
|
|
41
40
|
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
42
41
|
{key: 'protocol', valueType: 'text', label: str_(UIStrings.protocol)},
|
|
43
|
-
/* eslint-enable max-len */
|
|
44
42
|
];
|
|
45
43
|
/** @type {LH.Audit.Details.Table['items']} */
|
|
46
44
|
const items =
|