lighthouse 12.8.2 → 13.0.0-dev.20251010
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/cli-flags.js +1 -1
- package/cli/test/smokehouse/config/exclusions.js +0 -2
- package/cli/test/smokehouse/version-check.d.ts +1 -1
- package/core/audits/accessibility/accesskeys.js +3 -3
- package/core/audits/accessibility/aria-allowed-attr.js +3 -3
- package/core/audits/accessibility/aria-allowed-role.js +2 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-conditional-attr.js +1 -1
- package/core/audits/accessibility/aria-deprecated-role.js +1 -1
- package/core/audits/accessibility/aria-dialog-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +3 -3
- package/core/audits/accessibility/aria-hidden-focus.js +3 -3
- package/core/audits/accessibility/aria-input-field-name.js +3 -3
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
- package/core/audits/accessibility/aria-required-attr.js +3 -3
- package/core/audits/accessibility/aria-required-children.js +3 -3
- package/core/audits/accessibility/aria-required-parent.js +3 -3
- package/core/audits/accessibility/aria-roles.js +3 -3
- package/core/audits/accessibility/aria-text.js +3 -3
- package/core/audits/accessibility/aria-toggle-field-name.js +3 -3
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +3 -3
- package/core/audits/accessibility/aria-valid-attr.js +3 -3
- package/core/audits/accessibility/button-name.js +3 -3
- package/core/audits/accessibility/bypass.js +3 -3
- package/core/audits/accessibility/color-contrast.js +3 -3
- package/core/audits/accessibility/definition-list.js +3 -3
- package/core/audits/accessibility/dlitem.js +3 -3
- package/core/audits/accessibility/document-title.js +3 -3
- package/core/audits/accessibility/duplicate-id-aria.js +3 -3
- package/core/audits/accessibility/empty-heading.js +3 -3
- package/core/audits/accessibility/form-field-multiple-labels.js +3 -3
- package/core/audits/accessibility/frame-title.js +3 -3
- package/core/audits/accessibility/heading-order.js +3 -3
- package/core/audits/accessibility/html-has-lang.js +3 -3
- package/core/audits/accessibility/html-lang-valid.js +3 -3
- package/core/audits/accessibility/html-xml-lang-mismatch.js +3 -3
- package/core/audits/accessibility/identical-links-same-purpose.js +3 -3
- package/core/audits/accessibility/image-alt.js +3 -3
- package/core/audits/accessibility/image-redundant-alt.js +4 -3
- package/core/audits/accessibility/input-button-name.js +3 -3
- package/core/audits/accessibility/input-image-alt.js +3 -3
- package/core/audits/accessibility/label-content-name-mismatch.js +3 -3
- package/core/audits/accessibility/label.js +3 -3
- package/core/audits/accessibility/landmark-one-main.js +3 -4
- package/core/audits/accessibility/link-in-text-block.js +3 -3
- package/core/audits/accessibility/link-name.js +3 -3
- package/core/audits/accessibility/list.js +3 -3
- package/core/audits/accessibility/listitem.js +3 -3
- package/core/audits/accessibility/meta-refresh.js +3 -3
- package/core/audits/accessibility/meta-viewport.js +3 -3
- package/core/audits/accessibility/object-alt.js +3 -3
- package/core/audits/accessibility/select-name.js +3 -3
- package/core/audits/accessibility/skip-link.js +3 -3
- package/core/audits/accessibility/tabindex.js +3 -3
- package/core/audits/accessibility/table-duplicate-name.js +4 -3
- package/core/audits/accessibility/table-fake-caption.js +3 -3
- package/core/audits/accessibility/target-size.js +3 -3
- package/core/audits/accessibility/td-has-header.js +3 -3
- package/core/audits/accessibility/td-headers-attr.js +3 -3
- package/core/audits/accessibility/th-has-data-cells.js +3 -3
- package/core/audits/accessibility/valid-lang.js +3 -3
- package/core/audits/accessibility/video-caption.js +3 -3
- package/core/audits/audit.d.ts +0 -4
- package/core/audits/audit.js +2 -13
- package/core/audits/insights/cls-culprits-insight.js +1 -1
- package/core/audits/insights/dom-size-insight.js +11 -7
- package/core/audits/insights/font-display-insight.js +3 -1
- package/core/audits/insights/image-delivery-insight.js +4 -1
- package/core/audits/insights/insight-audit.d.ts +6 -4
- package/core/audits/insights/insight-audit.js +27 -8
- package/core/audits/insights/third-parties-insight.js +1 -1
- package/core/audits/layout-shifts.js +1 -1
- package/core/audits/predictive-perf.js +2 -2
- package/core/audits/seo/crawlable-anchors.js +2 -3
- package/core/audits/seo/manual/structured-data.js +1 -1
- package/core/audits/server-response-time.d.ts +0 -5
- package/core/audits/server-response-time.js +12 -26
- package/core/computed/metrics/cumulative-layout-shift.js +2 -2
- package/core/computed/metrics/lantern-metric.js +3 -3
- package/core/computed/metrics/lcp-breakdown.d.ts +10 -5
- package/core/computed/metrics/lcp-breakdown.js +50 -22
- package/core/computed/metrics/time-to-first-byte.js +33 -10
- package/core/computed/metrics/timing-summary.js +3 -2
- package/core/computed/page-dependency-graph.js +1 -1
- package/core/computed/trace-engine-result.js +2 -2
- package/core/config/default-config.js +110 -152
- package/core/config/experimental-config.js +1 -32
- package/core/config/filters.js +6 -9
- package/core/config/lr-desktop-config.js +0 -1
- package/core/config/lr-mobile-config.js +0 -1
- package/core/gather/driver/target-manager.d.ts +1 -1
- package/core/gather/driver.d.ts +1 -1
- package/core/gather/gatherers/anchor-elements.js +8 -24
- package/core/gather/gatherers/image-elements.js +32 -6
- package/core/gather/gatherers/inspector-issues.js +1 -28
- package/core/gather/gatherers/trace-elements.d.ts +2 -11
- package/core/gather/gatherers/trace-elements.js +9 -39
- package/core/gather/navigation-runner.js +0 -3
- package/core/gather/session.d.ts +1 -1
- package/core/lib/asset-saver.d.ts +2 -2
- package/core/lib/asset-saver.js +33 -43
- package/core/lib/bf-cache-strings.js +10 -9
- package/core/lib/deprecations-strings.js +5 -5
- package/core/lib/emulation.d.ts +10 -0
- package/core/lib/emulation.js +21 -6
- package/core/lib/legacy-javascript/legacy-javascript.js +4 -11
- package/core/lib/network-request.d.ts +0 -7
- package/core/lib/network-request.js +0 -16
- package/core/lib/proto-preprocessor.js +10 -25
- package/core/runner.js +1 -8
- package/core/scoring.js +1 -1
- package/dist/report/bundle.esm.js +10 -49
- package/dist/report/flow.js +12 -51
- package/dist/report/standalone.js +11 -50
- package/flow-report/src/i18n/i18n.d.ts +4 -6
- package/package.json +16 -19
- package/readme.md +2 -2
- package/report/assets/styles.css +0 -39
- package/report/renderer/api.js +0 -1
- package/report/renderer/category-renderer.js +6 -0
- package/report/renderer/components.js +1 -1
- package/report/renderer/details-renderer.d.ts +1 -2
- package/report/renderer/details-renderer.js +0 -1
- package/report/renderer/dom.d.ts +0 -13
- package/report/renderer/dom.js +0 -38
- package/report/renderer/performance-category-renderer.d.ts +0 -26
- package/report/renderer/performance-category-renderer.js +10 -142
- package/report/renderer/report-ui-features.d.ts +0 -1
- package/report/renderer/report-ui-features.js +2 -13
- package/report/renderer/report-utils.d.ts +2 -3
- package/report/renderer/report-utils.js +4 -6
- package/report/types/report-renderer.d.ts +0 -6
- package/shared/localization/locales/ar-XB.json +107 -455
- package/shared/localization/locales/ar.json +107 -455
- package/shared/localization/locales/bg.json +96 -444
- package/shared/localization/locales/ca.json +96 -444
- package/shared/localization/locales/cs.json +96 -444
- package/shared/localization/locales/da.json +96 -444
- package/shared/localization/locales/de.json +96 -444
- package/shared/localization/locales/el.json +96 -444
- package/shared/localization/locales/en-GB.json +96 -444
- package/shared/localization/locales/en-US.json +116 -467
- package/shared/localization/locales/en-XA.json +93 -441
- package/shared/localization/locales/en-XL.json +116 -467
- package/shared/localization/locales/es-419.json +96 -444
- package/shared/localization/locales/es.json +96 -444
- package/shared/localization/locales/fi.json +96 -444
- package/shared/localization/locales/fil.json +96 -444
- package/shared/localization/locales/fr.json +96 -444
- package/shared/localization/locales/he.json +118 -466
- package/shared/localization/locales/hi.json +96 -444
- package/shared/localization/locales/hr.json +100 -448
- package/shared/localization/locales/hu.json +96 -444
- package/shared/localization/locales/id.json +96 -444
- package/shared/localization/locales/it.json +96 -444
- package/shared/localization/locales/ja.json +96 -444
- package/shared/localization/locales/ko.json +97 -445
- package/shared/localization/locales/lt.json +96 -444
- package/shared/localization/locales/lv.json +97 -445
- package/shared/localization/locales/nl.json +96 -444
- package/shared/localization/locales/no.json +96 -444
- package/shared/localization/locales/pl.json +96 -444
- package/shared/localization/locales/pt-PT.json +96 -444
- package/shared/localization/locales/pt.json +97 -445
- package/shared/localization/locales/ro.json +97 -445
- package/shared/localization/locales/ru.json +96 -444
- package/shared/localization/locales/sk.json +96 -444
- package/shared/localization/locales/sl.json +96 -444
- package/shared/localization/locales/sr-Latn.json +96 -444
- package/shared/localization/locales/sr.json +96 -444
- package/shared/localization/locales/sv.json +96 -444
- package/shared/localization/locales/ta.json +96 -444
- package/shared/localization/locales/te.json +97 -445
- package/shared/localization/locales/th.json +96 -444
- package/shared/localization/locales/tr.json +96 -444
- package/shared/localization/locales/uk.json +96 -444
- package/shared/localization/locales/vi.json +96 -444
- package/shared/localization/locales/zh-HK.json +96 -444
- package/shared/localization/locales/zh-TW.json +97 -445
- package/shared/localization/locales/zh.json +96 -444
- package/shared/localization/locales.d.ts +2 -0
- package/shared/localization/locales.js +130 -139
- package/shared/tsconfig.json +2 -0
- package/tsconfig-base.json +2 -2
- package/tsconfig.json +1 -4
- package/types/artifacts.d.ts +6 -81
- package/types/audit.d.ts +1 -1
- package/types/lhr/settings.d.ts +1 -1
- package/core/audits/byte-efficiency/duplicated-javascript.d.ts +0 -45
- package/core/audits/byte-efficiency/duplicated-javascript.js +0 -223
- package/core/audits/byte-efficiency/efficient-animated-content.d.ts +0 -22
- package/core/audits/byte-efficiency/efficient-animated-content.js +0 -93
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -28
- package/core/audits/byte-efficiency/legacy-javascript.js +0 -144
- package/core/audits/byte-efficiency/modern-image-formats.d.ts +0 -38
- package/core/audits/byte-efficiency/modern-image-formats.js +0 -187
- package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -63
- package/core/audits/byte-efficiency/offscreen-images.js +0 -240
- package/core/audits/byte-efficiency/render-blocking-resources.d.ts +0 -53
- package/core/audits/byte-efficiency/render-blocking-resources.js +0 -312
- package/core/audits/byte-efficiency/uses-long-cache-ttl.d.ts +0 -59
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +0 -293
- package/core/audits/byte-efficiency/uses-optimized-images.d.ts +0 -33
- package/core/audits/byte-efficiency/uses-optimized-images.js +0 -146
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.d.ts +0 -16
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -106
- package/core/audits/byte-efficiency/uses-responsive-images.d.ts +0 -44
- package/core/audits/byte-efficiency/uses-responsive-images.js +0 -202
- package/core/audits/byte-efficiency/uses-text-compression.d.ts +0 -14
- package/core/audits/byte-efficiency/uses-text-compression.js +0 -108
- package/core/audits/critical-request-chains.d.ts +0 -44
- package/core/audits/critical-request-chains.js +0 -221
- package/core/audits/dobetterweb/dom-size.d.ts +0 -32
- package/core/audits/dobetterweb/dom-size.js +0 -182
- package/core/audits/dobetterweb/no-document-write.d.ts +0 -16
- package/core/audits/dobetterweb/no-document-write.js +0 -86
- package/core/audits/dobetterweb/uses-http2.d.ts +0 -72
- package/core/audits/dobetterweb/uses-http2.js +0 -276
- package/core/audits/dobetterweb/uses-passive-event-listeners.d.ts +0 -16
- package/core/audits/dobetterweb/uses-passive-event-listeners.js +0 -69
- package/core/audits/font-display.d.ts +0 -32
- package/core/audits/font-display.js +0 -195
- package/core/audits/largest-contentful-paint-element.d.ts +0 -34
- package/core/audits/largest-contentful-paint-element.js +0 -181
- package/core/audits/lcp-lazy-loaded.d.ts +0 -22
- package/core/audits/lcp-lazy-loaded.js +0 -115
- package/core/audits/metrics/first-meaningful-paint.d.ts +0 -12
- package/core/audits/metrics/first-meaningful-paint.js +0 -47
- package/core/audits/preload-fonts.d.ts +0 -25
- package/core/audits/preload-fonts.js +0 -97
- package/core/audits/prioritize-lcp-image.d.ts +0 -74
- package/core/audits/prioritize-lcp-image.js +0 -297
- package/core/audits/seo/font-size.d.ts +0 -24
- package/core/audits/seo/font-size.js +0 -344
- package/core/audits/third-party-facades.d.ts +0 -41
- package/core/audits/third-party-facades.js +0 -234
- package/core/audits/third-party-summary.d.ts +0 -78
- package/core/audits/third-party-summary.js +0 -236
- package/core/audits/uses-rel-preconnect.d.ts +0 -37
- package/core/audits/uses-rel-preconnect.js +0 -286
- package/core/audits/uses-rel-preload.d.ts +0 -57
- package/core/audits/uses-rel-preload.js +0 -263
- package/core/audits/viewport.d.ts +0 -17
- package/core/audits/viewport.js +0 -87
- package/core/audits/work-during-interaction.d.ts +0 -81
- package/core/audits/work-during-interaction.js +0 -287
- package/core/computed/critical-request-chains.d.ts +0 -42
- package/core/computed/critical-request-chains.js +0 -143
- package/core/computed/viewport-meta.d.ts +0 -37
- package/core/computed/viewport-meta.js +0 -71
- package/core/gather/gatherers/cache-contents.d.ts +0 -11
- package/core/gather/gatherers/cache-contents.js +0 -56
- package/core/gather/gatherers/devtools-log-compat.d.ts +0 -13
- package/core/gather/gatherers/devtools-log-compat.js +0 -35
- package/core/gather/gatherers/dobetterweb/domstats.d.ts +0 -10
- package/core/gather/gatherers/dobetterweb/domstats.js +0 -102
- package/core/gather/gatherers/dobetterweb/optimized-images.d.ts +0 -48
- package/core/gather/gatherers/dobetterweb/optimized-images.js +0 -169
- package/core/gather/gatherers/dobetterweb/response-compression.d.ts +0 -23
- package/core/gather/gatherers/dobetterweb/response-compression.js +0 -136
- package/core/gather/gatherers/seo/font-size.d.ts +0 -131
- package/core/gather/gatherers/seo/font-size.js +0 -347
- package/core/gather/gatherers/trace-compat.d.ts +0 -13
- package/core/gather/gatherers/trace-compat.js +0 -35
- package/types/internal/metaviewport-parser.d.ts +0 -13
- package/types/internal/parse-cache-control.d.ts +0 -20
package/core/audits/viewport.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {Audit} from './audit.js';
|
|
8
|
-
import {ViewportMeta} from '../computed/viewport-meta.js';
|
|
9
|
-
import * as i18n from '../lib/i18n/i18n.js';
|
|
10
|
-
|
|
11
|
-
const UIStrings = {
|
|
12
|
-
/** Title of a Lighthouse audit that provides detail on the viewport meta tag in a web page's html. This descriptive title is shown to users when a viewport tag is set and configured. */
|
|
13
|
-
title: 'Has a `<meta name="viewport">` tag with `width` or `initial-scale`',
|
|
14
|
-
/** Title of a Lighthouse audit that provides detail on the viewport meta tag in a web page's html. This descriptive title is shown to users when a viewport tag is not set or configured. */
|
|
15
|
-
failureTitle: 'Does not have a `<meta name="viewport">` tag with `width` ' +
|
|
16
|
-
'or `initial-scale`',
|
|
17
|
-
/** Description of a Lighthouse audit that tells the user why they should have a viewport meta tag in their html. 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. */
|
|
18
|
-
description: 'A `<meta name="viewport">` not only optimizes your app for mobile screen sizes, ' +
|
|
19
|
-
'but also prevents [a 300 millisecond delay to user input](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/). ' +
|
|
20
|
-
'[Learn more about using the viewport meta tag](https://developer.chrome.com/docs/lighthouse/pwa/viewport/).',
|
|
21
|
-
/** Explanatory message stating that no viewport meta tag exists on the page. */
|
|
22
|
-
explanationNoTag: 'No `<meta name="viewport">` tag found',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
26
|
-
|
|
27
|
-
class Viewport extends Audit {
|
|
28
|
-
/**
|
|
29
|
-
* @return {LH.Audit.Meta}
|
|
30
|
-
*/
|
|
31
|
-
static get meta() {
|
|
32
|
-
return {
|
|
33
|
-
id: 'viewport',
|
|
34
|
-
title: str_(UIStrings.title),
|
|
35
|
-
failureTitle: str_(UIStrings.failureTitle),
|
|
36
|
-
description: str_(UIStrings.description),
|
|
37
|
-
guidanceLevel: 3,
|
|
38
|
-
requiredArtifacts: ['MetaElements'],
|
|
39
|
-
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @param {LH.Artifacts} artifacts
|
|
45
|
-
* @param {LH.Audit.Context} context
|
|
46
|
-
* @return {Promise<LH.Audit.Product>}
|
|
47
|
-
*/
|
|
48
|
-
static async audit(artifacts, context) {
|
|
49
|
-
const viewportMeta = await ViewportMeta.request(artifacts.MetaElements, context);
|
|
50
|
-
|
|
51
|
-
let inpSavings = 300;
|
|
52
|
-
if (!viewportMeta.hasViewportTag) {
|
|
53
|
-
return {
|
|
54
|
-
score: 0,
|
|
55
|
-
explanation: str_(UIStrings.explanationNoTag),
|
|
56
|
-
metricSavings: {
|
|
57
|
-
INP: inpSavings,
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (viewportMeta.isMobileOptimized) {
|
|
63
|
-
inpSavings = 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** @type {LH.Audit.Details.DebugData|undefined} */
|
|
67
|
-
let details;
|
|
68
|
-
if (viewportMeta.rawContentString !== undefined) {
|
|
69
|
-
details = {
|
|
70
|
-
type: 'debugdata',
|
|
71
|
-
viewportContent: viewportMeta.rawContentString,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
score: Number(viewportMeta.isMobileOptimized),
|
|
77
|
-
metricSavings: {
|
|
78
|
-
INP: inpSavings,
|
|
79
|
-
},
|
|
80
|
-
warnings: viewportMeta.parserWarnings,
|
|
81
|
-
details,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export default Viewport;
|
|
87
|
-
export {UIStrings};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
export default WorkDuringInteraction;
|
|
2
|
-
export type EventTimingEvent = import("../computed/metrics/responsiveness.js").EventTimingEvent;
|
|
3
|
-
export type TaskNode = import("../lib/tracehouse/main-thread-tasks.js").TaskNode;
|
|
4
|
-
/**
|
|
5
|
-
* @fileoverview This metric gives a high-percentile measure of responsiveness to input.
|
|
6
|
-
*/
|
|
7
|
-
declare class WorkDuringInteraction extends Audit {
|
|
8
|
-
/**
|
|
9
|
-
* @param {TaskNode} task
|
|
10
|
-
* @param {TaskNode|undefined} parent
|
|
11
|
-
* @param {number} startTs
|
|
12
|
-
* @param {number} endTs
|
|
13
|
-
* @return {number}
|
|
14
|
-
*/
|
|
15
|
-
static recursivelyClipTasks(task: TaskNode, parent: TaskNode | undefined, startTs: number, endTs: number): number;
|
|
16
|
-
/**
|
|
17
|
-
* Clip the tasks by the start and end points. Take the easy route and drop
|
|
18
|
-
* to duration 0 if out of bounds, since only durations are needed in the
|
|
19
|
-
* end (for now).
|
|
20
|
-
* Assumes owned tasks, so modifies in place. Can be called multiple times on
|
|
21
|
-
* the same `tasks` because always computed from original event timing.
|
|
22
|
-
* @param {Array<TaskNode>} tasks
|
|
23
|
-
* @param {number} startTs
|
|
24
|
-
* @param {number} endTs
|
|
25
|
-
*/
|
|
26
|
-
static clipTasksByTs(tasks: Array<TaskNode>, startTs: number, endTs: number): void;
|
|
27
|
-
/**
|
|
28
|
-
* @param {EventTimingEvent} interactionEvent
|
|
29
|
-
*/
|
|
30
|
-
static getPhaseTimes(interactionEvent: EventTimingEvent): {
|
|
31
|
-
inputDelay: {
|
|
32
|
-
startTs: number;
|
|
33
|
-
endTs: number;
|
|
34
|
-
};
|
|
35
|
-
processingDuration: {
|
|
36
|
-
startTs: number;
|
|
37
|
-
endTs: number;
|
|
38
|
-
};
|
|
39
|
-
presentationDelay: {
|
|
40
|
-
startTs: number;
|
|
41
|
-
endTs: number;
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* @param {EventTimingEvent} interactionEvent
|
|
46
|
-
* @param {LH.Trace} trace
|
|
47
|
-
* @param {LH.Artifacts.ProcessedTrace} processedTrace
|
|
48
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
49
|
-
* @return {{table: LH.Audit.Details.Table, phases: Record<string, {startTs: number, endTs: number}>}}
|
|
50
|
-
*/
|
|
51
|
-
static getThreadBreakdownTable(interactionEvent: EventTimingEvent, trace: LH.Trace, processedTrace: LH.Artifacts.ProcessedTrace, networkRecords: Array<LH.Artifacts.NetworkRequest>): {
|
|
52
|
-
table: LH.Audit.Details.Table;
|
|
53
|
-
phases: Record<string, {
|
|
54
|
-
startTs: number;
|
|
55
|
-
endTs: number;
|
|
56
|
-
}>;
|
|
57
|
-
};
|
|
58
|
-
/**
|
|
59
|
-
* @param {LH.Artifacts['TraceElements']} traceElements
|
|
60
|
-
* @return {LH.Audit.Details.Table | undefined}
|
|
61
|
-
*/
|
|
62
|
-
static getTraceElementTable(traceElements: LH.Artifacts["TraceElements"]): LH.Audit.Details.Table | undefined;
|
|
63
|
-
/**
|
|
64
|
-
* @param {LH.Artifacts} artifacts
|
|
65
|
-
* @param {LH.Audit.Context} context
|
|
66
|
-
* @return {Promise<LH.Audit.Product>}
|
|
67
|
-
*/
|
|
68
|
-
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
69
|
-
}
|
|
70
|
-
export namespace UIStrings {
|
|
71
|
-
let title: string;
|
|
72
|
-
let failureTitle: string;
|
|
73
|
-
let description: string;
|
|
74
|
-
let inputDelay: string;
|
|
75
|
-
let processingDuration: string;
|
|
76
|
-
let presentationDelay: string;
|
|
77
|
-
let displayValue: string;
|
|
78
|
-
let eventTarget: string;
|
|
79
|
-
}
|
|
80
|
-
import { Audit } from './audit.js';
|
|
81
|
-
//# sourceMappingURL=work-during-interaction.d.ts.map
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2022 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {Audit} from './audit.js';
|
|
8
|
-
import {Responsiveness} from '../computed/metrics/responsiveness.js';
|
|
9
|
-
import {ProcessedTrace} from '../computed/processed-trace.js';
|
|
10
|
-
import * as i18n from '../lib/i18n/i18n.js';
|
|
11
|
-
import {NetworkRecords} from '../computed/network-records.js';
|
|
12
|
-
import {MainThreadTasks} from '../lib/tracehouse/main-thread-tasks.js';
|
|
13
|
-
import {taskGroups} from '../lib/tracehouse/task-groups.js';
|
|
14
|
-
import {TraceProcessor} from '../lib/tracehouse/trace-processor.js';
|
|
15
|
-
import {getExecutionTimingsByURL} from '../lib/tracehouse/task-summary.js';
|
|
16
|
-
import InteractionToNextPaint from './metrics/interaction-to-next-paint.js';
|
|
17
|
-
|
|
18
|
-
/** @typedef {import('../computed/metrics/responsiveness.js').EventTimingEvent} EventTimingEvent */
|
|
19
|
-
/** @typedef {import('../lib/tracehouse/main-thread-tasks.js').TaskNode} TaskNode */
|
|
20
|
-
|
|
21
|
-
const TASK_THRESHOLD = 1;
|
|
22
|
-
|
|
23
|
-
const UIStrings = {
|
|
24
|
-
/** Title of a diagnostic audit that provides detail on the main thread work the browser did during a key user interaction. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
25
|
-
title: 'Minimizes work during key interaction',
|
|
26
|
-
/** Title of a diagnostic audit that provides detail on the main thread work the browser did during a key user interaction. This imperative title is shown to users when there is a significant amount of execution time that could be reduced. */
|
|
27
|
-
failureTitle: 'Minimize work during key interaction',
|
|
28
|
-
/** Description of the work-during-interaction metric. This description is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
29
|
-
description: 'This is the thread-blocking work occurring during the Interaction to Next Paint measurement. [Learn more about the Interaction to Next Paint metric](https://web.dev/articles/inp).',
|
|
30
|
-
/** Label for a column in a data table; entries will be information on the time that the browser is delayed before responding to user input. Ideally fits within a ~40 character limit. */
|
|
31
|
-
inputDelay: 'Input delay',
|
|
32
|
-
/** Label for a column in a data table; entries will be information on the time taken by code processing user input that delays a response to the user. Ideally fits within a ~40 character limit. */
|
|
33
|
-
processingDuration: 'Processing duration',
|
|
34
|
-
/** Label for a column in a data table; entries will be information on the time that the browser is delayed before presenting a response to user input on screen. Ideally fits within a ~40 character limit. */
|
|
35
|
-
presentationDelay: 'Presentation delay',
|
|
36
|
-
/**
|
|
37
|
-
* @description Summary text that identifies the time the browser took to process a user interaction.
|
|
38
|
-
* @example {mousedown} interactionType
|
|
39
|
-
*/
|
|
40
|
-
displayValue: `{timeInMs, number, milliseconds}\xa0ms spent on event '{interactionType}'`,
|
|
41
|
-
/** Label for a column in a data table; entries will the UI element that was the target of a user interaction (for example, a button that was clicked on). Ideally fits within a ~40 character limit. */
|
|
42
|
-
eventTarget: 'Event target',
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @fileoverview This metric gives a high-percentile measure of responsiveness to input.
|
|
49
|
-
*/
|
|
50
|
-
class WorkDuringInteraction extends Audit {
|
|
51
|
-
/**
|
|
52
|
-
* @return {LH.Audit.Meta}
|
|
53
|
-
*/
|
|
54
|
-
static get meta() {
|
|
55
|
-
return {
|
|
56
|
-
id: 'work-during-interaction',
|
|
57
|
-
title: str_(UIStrings.title),
|
|
58
|
-
failureTitle: str_(UIStrings.failureTitle),
|
|
59
|
-
description: str_(UIStrings.description),
|
|
60
|
-
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
61
|
-
supportedModes: ['timespan'],
|
|
62
|
-
guidanceLevel: 1,
|
|
63
|
-
requiredArtifacts: ['Trace', 'DevtoolsLog', 'TraceElements'],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @param {TaskNode} task
|
|
69
|
-
* @param {TaskNode|undefined} parent
|
|
70
|
-
* @param {number} startTs
|
|
71
|
-
* @param {number} endTs
|
|
72
|
-
* @return {number}
|
|
73
|
-
*/
|
|
74
|
-
static recursivelyClipTasks(task, parent, startTs, endTs) {
|
|
75
|
-
const taskEventStart = task.event.ts;
|
|
76
|
-
const taskEventEnd = task.endEvent?.ts ?? task.event.ts + Number(task.event.dur || 0);
|
|
77
|
-
|
|
78
|
-
task.startTime = Math.max(startTs, Math.min(endTs, taskEventStart)) / 1000;
|
|
79
|
-
task.endTime = Math.max(startTs, Math.min(endTs, taskEventEnd)) / 1000;
|
|
80
|
-
task.duration = task.endTime - task.startTime;
|
|
81
|
-
|
|
82
|
-
const childTime = task.children
|
|
83
|
-
.map(child => WorkDuringInteraction.recursivelyClipTasks(child, task, startTs, endTs))
|
|
84
|
-
.reduce((sum, child) => sum + child, 0);
|
|
85
|
-
task.selfTime = task.duration - childTime;
|
|
86
|
-
return task.duration;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Clip the tasks by the start and end points. Take the easy route and drop
|
|
91
|
-
* to duration 0 if out of bounds, since only durations are needed in the
|
|
92
|
-
* end (for now).
|
|
93
|
-
* Assumes owned tasks, so modifies in place. Can be called multiple times on
|
|
94
|
-
* the same `tasks` because always computed from original event timing.
|
|
95
|
-
* @param {Array<TaskNode>} tasks
|
|
96
|
-
* @param {number} startTs
|
|
97
|
-
* @param {number} endTs
|
|
98
|
-
*/
|
|
99
|
-
static clipTasksByTs(tasks, startTs, endTs) {
|
|
100
|
-
for (const task of tasks) {
|
|
101
|
-
if (task.parent) continue;
|
|
102
|
-
WorkDuringInteraction.recursivelyClipTasks(task, undefined, startTs, endTs);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* @param {EventTimingEvent} interactionEvent
|
|
108
|
-
*/
|
|
109
|
-
static getPhaseTimes(interactionEvent) {
|
|
110
|
-
const interactionData = interactionEvent.args.data;
|
|
111
|
-
const startTs = interactionEvent.ts;
|
|
112
|
-
const navStart = startTs - interactionData.timeStamp * 1000;
|
|
113
|
-
const processingStartTs = navStart + interactionData.processingStart * 1000;
|
|
114
|
-
const processingEndTs = navStart + interactionData.processingEnd * 1000;
|
|
115
|
-
const endTs = startTs + interactionData.duration * 1000;
|
|
116
|
-
return {
|
|
117
|
-
inputDelay: {startTs, endTs: processingStartTs},
|
|
118
|
-
processingDuration: {startTs: processingStartTs, endTs: processingEndTs},
|
|
119
|
-
presentationDelay: {startTs: processingEndTs, endTs},
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* @param {EventTimingEvent} interactionEvent
|
|
125
|
-
* @param {LH.Trace} trace
|
|
126
|
-
* @param {LH.Artifacts.ProcessedTrace} processedTrace
|
|
127
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
128
|
-
* @return {{table: LH.Audit.Details.Table, phases: Record<string, {startTs: number, endTs: number}>}}
|
|
129
|
-
*/
|
|
130
|
-
static getThreadBreakdownTable(interactionEvent, trace, processedTrace, networkRecords) {
|
|
131
|
-
// Limit to interactionEvent's thread.
|
|
132
|
-
// TODO(bckenny): limit to interactionEvent's navigation.
|
|
133
|
-
const threadEvents = TraceProcessor.filteredTraceSort(trace.traceEvents, evt => {
|
|
134
|
-
return evt.pid === interactionEvent.pid && evt.tid === interactionEvent.tid;
|
|
135
|
-
});
|
|
136
|
-
const traceEndTs = threadEvents.reduce((endTs, evt) => {
|
|
137
|
-
return Math.max(evt.ts + (evt.dur || 0), endTs);
|
|
138
|
-
}, 0);
|
|
139
|
-
// frames is only used for URL attribution, so can include all frames, even if OOPIF.
|
|
140
|
-
const {frames} = processedTrace;
|
|
141
|
-
const threadTasks = MainThreadTasks.getMainThreadTasks(threadEvents, frames, traceEndTs);
|
|
142
|
-
|
|
143
|
-
const phases = WorkDuringInteraction.getPhaseTimes(interactionEvent);
|
|
144
|
-
|
|
145
|
-
/** @type {LH.Audit.Details.TableItem[]} */
|
|
146
|
-
const items = [];
|
|
147
|
-
for (const [phaseName, phaseTimes] of Object.entries(phases)) {
|
|
148
|
-
// Clip tasks to start and end time.
|
|
149
|
-
WorkDuringInteraction.clipTasksByTs(threadTasks, phaseTimes.startTs, phaseTimes.endTs);
|
|
150
|
-
const executionTimings = getExecutionTimingsByURL(threadTasks, networkRecords);
|
|
151
|
-
|
|
152
|
-
const results = [];
|
|
153
|
-
for (const [url, timingByGroupId] of executionTimings) {
|
|
154
|
-
const totalExecutionTimeForURL = Object.values(timingByGroupId)
|
|
155
|
-
.reduce((total, timespanMs) => total + timespanMs);
|
|
156
|
-
|
|
157
|
-
const scriptingTotal = timingByGroupId[taskGroups.scriptEvaluation.id] || 0;
|
|
158
|
-
const layoutTotal = timingByGroupId[taskGroups.styleLayout.id] || 0;
|
|
159
|
-
const renderTotal = timingByGroupId[taskGroups.paintCompositeRender.id] || 0;
|
|
160
|
-
|
|
161
|
-
results.push({
|
|
162
|
-
url: url,
|
|
163
|
-
total: totalExecutionTimeForURL,
|
|
164
|
-
scripting: scriptingTotal,
|
|
165
|
-
layout: layoutTotal,
|
|
166
|
-
render: renderTotal,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const filteredResults = results
|
|
171
|
-
.filter(result => result.total > TASK_THRESHOLD)
|
|
172
|
-
.sort((a, b) => b.total - a.total);
|
|
173
|
-
|
|
174
|
-
items.push({
|
|
175
|
-
phase: str_(UIStrings[/** @type {keyof UIStrings} */ (phaseName)]),
|
|
176
|
-
total: (phaseTimes.endTs - phaseTimes.startTs) / 1000,
|
|
177
|
-
subItems: {
|
|
178
|
-
type: 'subitems',
|
|
179
|
-
items: filteredResults,
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/** @type {LH.Audit.Details.Table['headings']} */
|
|
185
|
-
const headings = [
|
|
186
|
-
/* eslint-disable max-len */
|
|
187
|
-
{key: 'phase', valueType: 'text', subItemsHeading: {key: 'url', valueType: 'url'}, label: 'Phase'},
|
|
188
|
-
{key: 'total', valueType: 'ms', subItemsHeading: {key: 'total', granularity: 1, valueType: 'ms'}, granularity: 1, label: 'Total time'},
|
|
189
|
-
{key: null, valueType: 'ms', subItemsHeading: {key: 'scripting', granularity: 1, valueType: 'ms'}, label: 'Script evaluation'},
|
|
190
|
-
{key: null, valueType: 'ms', subItemsHeading: {key: 'layout', granularity: 1, valueType: 'ms'}, label: taskGroups.styleLayout.label},
|
|
191
|
-
{key: null, valueType: 'ms', subItemsHeading: {key: 'render', granularity: 1, valueType: 'ms'}, label: taskGroups.paintCompositeRender.label},
|
|
192
|
-
/* eslint-enable max-len */
|
|
193
|
-
];
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
table: Audit.makeTableDetails(headings, items, {sortedBy: ['total']}),
|
|
197
|
-
phases,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* @param {LH.Artifacts['TraceElements']} traceElements
|
|
203
|
-
* @return {LH.Audit.Details.Table | undefined}
|
|
204
|
-
*/
|
|
205
|
-
static getTraceElementTable(traceElements) {
|
|
206
|
-
const responsivenessElement = traceElements.find(el => el.traceEventType === 'responsiveness');
|
|
207
|
-
if (!responsivenessElement) return;
|
|
208
|
-
|
|
209
|
-
/** @type {LH.Audit.Details.Table['headings']} */
|
|
210
|
-
const headings = [
|
|
211
|
-
{key: 'node', valueType: 'node', label: str_(UIStrings.eventTarget)},
|
|
212
|
-
];
|
|
213
|
-
const elementItems = [{node: Audit.makeNodeItem(responsivenessElement.node)}];
|
|
214
|
-
|
|
215
|
-
return Audit.makeTableDetails(headings, elementItems);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* @param {LH.Artifacts} artifacts
|
|
220
|
-
* @param {LH.Audit.Context} context
|
|
221
|
-
* @return {Promise<LH.Audit.Product>}
|
|
222
|
-
*/
|
|
223
|
-
static async audit(artifacts, context) {
|
|
224
|
-
const {settings} = context;
|
|
225
|
-
// TODO: responsiveness isn't yet supported by lantern.
|
|
226
|
-
if (settings.throttlingMethod === 'simulate') {
|
|
227
|
-
return {
|
|
228
|
-
score: null,
|
|
229
|
-
notApplicable: true,
|
|
230
|
-
metricSavings: {INP: 0},
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const trace = artifacts.Trace;
|
|
235
|
-
const metricData = {trace, settings};
|
|
236
|
-
const interactionEvent = await Responsiveness.request(metricData, context);
|
|
237
|
-
// If no interaction, diagnostic audit is n/a.
|
|
238
|
-
if (interactionEvent === null) {
|
|
239
|
-
return {
|
|
240
|
-
score: null,
|
|
241
|
-
notApplicable: true,
|
|
242
|
-
metricSavings: {INP: 0},
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const auditDetailsItems = [];
|
|
247
|
-
|
|
248
|
-
const traceElementItem = WorkDuringInteraction.getTraceElementTable(artifacts.TraceElements);
|
|
249
|
-
if (traceElementItem) auditDetailsItems.push(traceElementItem);
|
|
250
|
-
|
|
251
|
-
const devtoolsLog = artifacts.DevtoolsLog;
|
|
252
|
-
// Network records will usually be empty for timespans.
|
|
253
|
-
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
254
|
-
const processedTrace = await ProcessedTrace.request(trace, context);
|
|
255
|
-
const {table: breakdownTable, phases} = WorkDuringInteraction.getThreadBreakdownTable(
|
|
256
|
-
interactionEvent, trace, processedTrace, networkRecords);
|
|
257
|
-
auditDetailsItems.push(breakdownTable);
|
|
258
|
-
|
|
259
|
-
const interactionType = interactionEvent.args.data.type;
|
|
260
|
-
auditDetailsItems.push({
|
|
261
|
-
type: /** @type {const} */ ('debugdata'),
|
|
262
|
-
interactionType,
|
|
263
|
-
phases,
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const duration = interactionEvent.args.data.duration;
|
|
267
|
-
const displayValue = str_(UIStrings.displayValue, {timeInMs: duration, interactionType});
|
|
268
|
-
|
|
269
|
-
const passed = duration < InteractionToNextPaint.defaultOptions.p10;
|
|
270
|
-
|
|
271
|
-
return {
|
|
272
|
-
score: passed ? 1 : 0,
|
|
273
|
-
scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined,
|
|
274
|
-
displayValue,
|
|
275
|
-
details: {
|
|
276
|
-
type: 'list',
|
|
277
|
-
items: auditDetailsItems,
|
|
278
|
-
},
|
|
279
|
-
metricSavings: {
|
|
280
|
-
INP: duration,
|
|
281
|
-
},
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export default WorkDuringInteraction;
|
|
287
|
-
export {UIStrings};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
export { CriticalRequestChainsComputed as CriticalRequestChains };
|
|
2
|
-
declare const CriticalRequestChainsComputed: typeof CriticalRequestChains & {
|
|
3
|
-
request: (dependencies: {
|
|
4
|
-
URL: LH.Artifacts["URL"];
|
|
5
|
-
SourceMaps: LH.Artifacts["SourceMaps"];
|
|
6
|
-
devtoolsLog: LH.DevtoolsLog;
|
|
7
|
-
trace: LH.Trace;
|
|
8
|
-
settings: LH.Audit.Context["settings"];
|
|
9
|
-
}, context: LH.Artifacts.ComputedContext) => Promise<import("../index.js").Artifacts.CriticalRequestNode>;
|
|
10
|
-
};
|
|
11
|
-
declare class CriticalRequestChains {
|
|
12
|
-
/**
|
|
13
|
-
* For now, we use network priorities as a proxy for "render-blocking"/critical-ness.
|
|
14
|
-
* It's imperfect, but there is not a higher-fidelity signal available yet.
|
|
15
|
-
* @see https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc
|
|
16
|
-
* @param {Lantern.Types.NetworkRequest} request
|
|
17
|
-
* @param {Lantern.Types.NetworkRequest} mainResource
|
|
18
|
-
* @return {boolean}
|
|
19
|
-
*/
|
|
20
|
-
static isCritical(request: Lantern.Types.NetworkRequest, mainResource: Lantern.Types.NetworkRequest): boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Create a tree of critical requests.
|
|
23
|
-
* @param {LH.Artifacts.NetworkRequest} mainResource
|
|
24
|
-
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
25
|
-
* @return {LH.Artifacts.CriticalRequestNode}
|
|
26
|
-
*/
|
|
27
|
-
static extractChainsFromGraph(mainResource: LH.Artifacts.NetworkRequest, graph: LH.Gatherer.Simulation.GraphNode): LH.Artifacts.CriticalRequestNode;
|
|
28
|
-
/**
|
|
29
|
-
* @param {{URL: LH.Artifacts['URL'], SourceMaps: LH.Artifacts['SourceMaps'], devtoolsLog: LH.DevtoolsLog, trace: LH.Trace, settings: LH.Audit.Context['settings']}} data
|
|
30
|
-
* @param {LH.Artifacts.ComputedContext} context
|
|
31
|
-
* @return {Promise<LH.Artifacts.CriticalRequestNode>}
|
|
32
|
-
*/
|
|
33
|
-
static compute_(data: {
|
|
34
|
-
URL: LH.Artifacts["URL"];
|
|
35
|
-
SourceMaps: LH.Artifacts["SourceMaps"];
|
|
36
|
-
devtoolsLog: LH.DevtoolsLog;
|
|
37
|
-
trace: LH.Trace;
|
|
38
|
-
settings: LH.Audit.Context["settings"];
|
|
39
|
-
}, context: LH.Artifacts.ComputedContext): Promise<LH.Artifacts.CriticalRequestNode>;
|
|
40
|
-
}
|
|
41
|
-
import * as Lantern from '../lib/lantern/lantern.js';
|
|
42
|
-
//# sourceMappingURL=critical-request-chains.d.ts.map
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as Lantern from '../lib/lantern/lantern.js';
|
|
8
|
-
import {makeComputedArtifact} from './computed-artifact.js';
|
|
9
|
-
import {NetworkRequest} from '../lib/network-request.js';
|
|
10
|
-
import {MainResource} from './main-resource.js';
|
|
11
|
-
import {PageDependencyGraph} from './page-dependency-graph.js';
|
|
12
|
-
|
|
13
|
-
class CriticalRequestChains {
|
|
14
|
-
/**
|
|
15
|
-
* For now, we use network priorities as a proxy for "render-blocking"/critical-ness.
|
|
16
|
-
* It's imperfect, but there is not a higher-fidelity signal available yet.
|
|
17
|
-
* @see https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc
|
|
18
|
-
* @param {Lantern.Types.NetworkRequest} request
|
|
19
|
-
* @param {Lantern.Types.NetworkRequest} mainResource
|
|
20
|
-
* @return {boolean}
|
|
21
|
-
*/
|
|
22
|
-
static isCritical(request, mainResource) {
|
|
23
|
-
if (!mainResource) {
|
|
24
|
-
throw new Error('mainResource not provided');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// The main resource is always critical.
|
|
28
|
-
if (request.requestId === mainResource.requestId) return true;
|
|
29
|
-
|
|
30
|
-
// Treat any preloaded resource as non-critical
|
|
31
|
-
if (request.isLinkPreload) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Whenever a request is a redirect, we don't know if it's critical until we resolve the final
|
|
36
|
-
// destination. At that point we can assign all the properties (priority, resourceType) of the
|
|
37
|
-
// final request back to the redirect(s) that led to it.
|
|
38
|
-
// See https://github.com/GoogleChrome/lighthouse/pull/6704
|
|
39
|
-
while (request.redirectDestination) {
|
|
40
|
-
request = request.redirectDestination;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Iframes are considered High Priority but they are not render blocking
|
|
44
|
-
const isIframe = request.resourceType === NetworkRequest.TYPES.Document &&
|
|
45
|
-
request.frameId !== mainResource.frameId;
|
|
46
|
-
// XHRs are fetched at High priority, but we exclude them, as they are unlikely to be critical
|
|
47
|
-
// Images are also non-critical.
|
|
48
|
-
// Treat any missed images, primarily favicons, as non-critical resources
|
|
49
|
-
/** @type {Array<LH.Crdp.Network.ResourceType>} */
|
|
50
|
-
const nonCriticalResourceTypes = [
|
|
51
|
-
NetworkRequest.TYPES.Image,
|
|
52
|
-
NetworkRequest.TYPES.XHR,
|
|
53
|
-
NetworkRequest.TYPES.Fetch,
|
|
54
|
-
NetworkRequest.TYPES.EventSource,
|
|
55
|
-
];
|
|
56
|
-
if (nonCriticalResourceTypes.includes(request.resourceType || 'Other') ||
|
|
57
|
-
isIframe ||
|
|
58
|
-
request.mimeType && request.mimeType.startsWith('image/')) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Requests that have no initiatorRequest are typically ambiguous late-load assets.
|
|
63
|
-
// Even on the off chance they were important, we don't have any parent to display for them.
|
|
64
|
-
if (!request.initiatorRequest) return false;
|
|
65
|
-
|
|
66
|
-
return ['VeryHigh', 'High', 'Medium'].includes(request.priority);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Create a tree of critical requests.
|
|
71
|
-
* @param {LH.Artifacts.NetworkRequest} mainResource
|
|
72
|
-
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
73
|
-
* @return {LH.Artifacts.CriticalRequestNode}
|
|
74
|
-
*/
|
|
75
|
-
static extractChainsFromGraph(mainResource, graph) {
|
|
76
|
-
/** @type {LH.Artifacts.CriticalRequestNode} */
|
|
77
|
-
const rootNode = {};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @param {LH.Artifacts.NetworkRequest[]} path
|
|
81
|
-
*/
|
|
82
|
-
function addChain(path) {
|
|
83
|
-
let currentNode = rootNode;
|
|
84
|
-
|
|
85
|
-
for (const record of path) {
|
|
86
|
-
if (!currentNode[record.requestId]) {
|
|
87
|
-
currentNode[record.requestId] = {
|
|
88
|
-
request: record,
|
|
89
|
-
children: {},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
currentNode = currentNode[record.requestId].children;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// By default `traverse` will discover nodes in BFS-order regardless of dependencies, but
|
|
98
|
-
// here we need traversal in a topological sort order. We'll visit a node only when its
|
|
99
|
-
// dependencies have been met.
|
|
100
|
-
const seenNodes = new Set();
|
|
101
|
-
/** @param {LH.Gatherer.Simulation.GraphNode} node */
|
|
102
|
-
function getNextNodes(node) {
|
|
103
|
-
return node.getDependents().filter(n => n.getDependencies().every(d => seenNodes.has(d)));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
graph.traverse((node, traversalPath) => {
|
|
107
|
-
seenNodes.add(node);
|
|
108
|
-
if (node.type !== 'network') return;
|
|
109
|
-
if (!CriticalRequestChains.isCritical(node.request, mainResource)) return;
|
|
110
|
-
|
|
111
|
-
const networkPath = traversalPath
|
|
112
|
-
.filter(n => n.type === 'network')
|
|
113
|
-
.reverse()
|
|
114
|
-
.map(node => node.rawRequest);
|
|
115
|
-
|
|
116
|
-
// Ignore if some ancestor is not a critical request.
|
|
117
|
-
if (networkPath.some(r => !CriticalRequestChains.isCritical(r, mainResource))) return;
|
|
118
|
-
|
|
119
|
-
// Ignore non-network things (like data urls).
|
|
120
|
-
if (NetworkRequest.isNonNetworkRequest(node.request)) return;
|
|
121
|
-
|
|
122
|
-
addChain(networkPath);
|
|
123
|
-
}, getNextNodes);
|
|
124
|
-
|
|
125
|
-
return rootNode;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @param {{URL: LH.Artifacts['URL'], SourceMaps: LH.Artifacts['SourceMaps'], devtoolsLog: LH.DevtoolsLog, trace: LH.Trace, settings: LH.Audit.Context['settings']}} data
|
|
130
|
-
* @param {LH.Artifacts.ComputedContext} context
|
|
131
|
-
* @return {Promise<LH.Artifacts.CriticalRequestNode>}
|
|
132
|
-
*/
|
|
133
|
-
static async compute_(data, context) {
|
|
134
|
-
const mainResource = await MainResource.request(data, context);
|
|
135
|
-
const graph = await PageDependencyGraph.request({...data, fromTrace: false}, context);
|
|
136
|
-
|
|
137
|
-
return CriticalRequestChains.extractChainsFromGraph(mainResource, graph);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const CriticalRequestChainsComputed = makeComputedArtifact(CriticalRequestChains,
|
|
142
|
-
['URL', 'SourceMaps', 'devtoolsLog', 'trace', 'settings']);
|
|
143
|
-
export {CriticalRequestChainsComputed as CriticalRequestChains};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
export { ViewportMetaComputed as ViewportMeta };
|
|
2
|
-
export type ViewportMetaResult = {
|
|
3
|
-
/**
|
|
4
|
-
* Whether the page has any viewport tag.
|
|
5
|
-
*/
|
|
6
|
-
hasViewportTag: boolean;
|
|
7
|
-
/**
|
|
8
|
-
* Whether the viewport tag is optimized for mobile screens.
|
|
9
|
-
*/
|
|
10
|
-
isMobileOptimized: boolean;
|
|
11
|
-
/**
|
|
12
|
-
* Warnings if the parser encountered invalid content in the viewport tag.
|
|
13
|
-
*/
|
|
14
|
-
parserWarnings: Array<string>;
|
|
15
|
-
/**
|
|
16
|
-
* The `content` attribute value, if a viewport was defined.
|
|
17
|
-
*/
|
|
18
|
-
rawContentString: string | undefined;
|
|
19
|
-
};
|
|
20
|
-
declare const ViewportMetaComputed: typeof ViewportMeta & {
|
|
21
|
-
request: (dependencies: {
|
|
22
|
-
name?: string;
|
|
23
|
-
content?: string;
|
|
24
|
-
property?: string;
|
|
25
|
-
httpEquiv?: string;
|
|
26
|
-
charset?: string;
|
|
27
|
-
node: import("../index.js").Artifacts.NodeDetails;
|
|
28
|
-
}[], context: LH.Artifacts.ComputedContext) => Promise<ViewportMetaResult>;
|
|
29
|
-
};
|
|
30
|
-
declare class ViewportMeta {
|
|
31
|
-
/**
|
|
32
|
-
* @param {LH.GathererArtifacts['MetaElements']} MetaElements
|
|
33
|
-
* @return {Promise<ViewportMetaResult>}
|
|
34
|
-
*/
|
|
35
|
-
static compute_(MetaElements: LH.GathererArtifacts["MetaElements"]): Promise<ViewportMetaResult>;
|
|
36
|
-
}
|
|
37
|
-
//# sourceMappingURL=viewport-meta.d.ts.map
|