lighthouse 13.5.0 → 13.6.0-dev.20261027
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/.agents/skills/update-ard-port/SKILL.md +171 -0
- package/.agents/skills/update-trace-engine/SKILL.md +154 -0
- package/cli/bin.d.ts +2 -2
- package/cli/bin.js +5 -4
- package/cli/cli-flags.d.ts +16 -1
- package/cli/cli-flags.js +53 -2
- package/cli/printer.js +5 -8
- package/cli/run.d.ts +5 -5
- package/cli/run.js +97 -47
- package/cli/sentry-prompt.js +3 -4
- package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +1 -1
- package/cli/test/smokehouse/config/exclusions.js +3 -1
- package/cli/test/smokehouse/frontends/smokehouse-bin.js +5 -3
- package/cli/test/smokehouse/report-assert-test.js +65 -0
- package/cli/test/smokehouse/report-assert.js +24 -39
- package/cli/test/smokehouse/smokehouse-test.d.ts +2 -0
- package/cli/test/smokehouse/smokehouse-test.js +120 -0
- package/cli/test/smokehouse/smokehouse.js +37 -15
- package/cli/test/smokehouse/version-check-test.js +5 -0
- package/cli/test/smokehouse/version-check.d.ts +1 -1
- package/cli/test/smokehouse/version-check.js +1 -1
- package/commitlint.config.js +1 -1
- package/core/audits/accessibility/accesskeys.js +1 -1
- package/core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/core/audits/accessibility/aria-allowed-role.js +1 -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 +1 -1
- package/core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/core/audits/accessibility/aria-input-field-name.js +1 -1
- 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 +1 -1
- package/core/audits/accessibility/aria-required-children.js +1 -1
- package/core/audits/accessibility/aria-required-parent.js +1 -1
- package/core/audits/accessibility/aria-roles.js +1 -1
- package/core/audits/accessibility/aria-text.js +1 -1
- package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
- 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 +1 -1
- package/core/audits/accessibility/aria-valid-attr.js +1 -1
- package/core/audits/accessibility/autocomplete-valid.js +1 -1
- package/core/audits/accessibility/button-name.js +1 -1
- package/core/audits/accessibility/bypass.js +1 -1
- package/core/audits/accessibility/color-contrast.js +1 -1
- package/core/audits/accessibility/definition-list.js +1 -1
- package/core/audits/accessibility/dlitem.js +1 -1
- package/core/audits/accessibility/document-title.js +1 -1
- package/core/audits/accessibility/duplicate-id-aria.js +1 -1
- package/core/audits/accessibility/empty-heading.js +1 -1
- package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/core/audits/accessibility/frame-title.js +1 -1
- package/core/audits/accessibility/heading-order.js +1 -1
- package/core/audits/accessibility/html-has-lang.js +1 -1
- package/core/audits/accessibility/html-lang-valid.js +1 -1
- package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
- package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
- package/core/audits/accessibility/image-alt.js +1 -1
- package/core/audits/accessibility/image-redundant-alt.js +1 -1
- package/core/audits/accessibility/input-button-name.js +1 -1
- package/core/audits/accessibility/input-image-alt.js +1 -1
- package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
- package/core/audits/accessibility/label.js +1 -1
- package/core/audits/accessibility/landmark-one-main.js +1 -1
- package/core/audits/accessibility/link-in-text-block.js +1 -1
- package/core/audits/accessibility/link-name.js +1 -1
- package/core/audits/accessibility/list.js +1 -1
- package/core/audits/accessibility/listitem.js +1 -1
- package/core/audits/accessibility/meta-refresh.js +1 -1
- package/core/audits/accessibility/meta-viewport.js +1 -1
- package/core/audits/accessibility/object-alt.js +1 -1
- package/core/audits/accessibility/presentation-role-conflict.js +1 -1
- package/core/audits/accessibility/select-name.js +1 -1
- package/core/audits/accessibility/skip-link.js +1 -1
- package/core/audits/accessibility/svg-img-alt.js +1 -1
- package/core/audits/accessibility/tabindex.js +1 -1
- package/core/audits/accessibility/table-duplicate-name.js +1 -1
- package/core/audits/accessibility/table-fake-caption.js +1 -1
- package/core/audits/accessibility/target-size.js +1 -1
- package/core/audits/accessibility/td-has-header.js +1 -1
- package/core/audits/accessibility/td-headers-attr.js +1 -1
- package/core/audits/accessibility/th-has-data-cells.js +1 -1
- package/core/audits/accessibility/valid-lang.js +1 -1
- package/core/audits/accessibility/video-caption.js +1 -1
- package/core/audits/agentic/ard-schema.d.ts +13 -0
- package/core/audits/agentic/ard-schema.js +81 -33
- package/core/audits/agentic/llms-txt.js +5 -2
- package/core/audits/baseline.d.ts +29 -7
- package/core/audits/baseline.js +19 -6
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +2 -2
- package/core/audits/insights/insight-audit.d.ts +9 -0
- package/core/audits/insights/insight-audit.js +35 -28
- package/core/audits/long-tasks.js +2 -1
- package/core/audits/predictive-perf.js +2 -2
- package/core/audits/screenshot-thumbnails.d.ts +0 -13
- package/core/audits/screenshot-thumbnails.js +0 -45
- package/core/audits/script-treemap-data.js +26 -6
- package/core/audits/seo/robots-txt.js +3 -8
- package/core/audits/unsized-images.d.ts +0 -7
- package/core/audits/unsized-images.js +7 -26
- package/core/computed/lcp-image-record.d.ts +2 -23
- package/core/computed/lcp-image-record.js +3 -67
- package/core/computed/lcp-network-record.d.ts +24 -0
- package/core/computed/lcp-network-record.js +74 -0
- package/core/computed/metrics/lantern-metric.js +1 -1
- package/core/computed/metrics/lcp-breakdown.js +6 -6
- package/core/computed/metrics/time-to-first-byte.js +5 -3
- package/core/computed/navigation-insights.js +3 -2
- package/core/computed/page-dependency-graph.js +55 -5
- package/core/computed/speedline.js +20 -5
- package/core/computed/trace-engine-result.js +25 -5
- package/core/computed/unused-css.js +15 -2
- package/core/config/config-helpers.d.ts +5 -0
- package/core/config/config-helpers.js +8 -28
- package/core/config/config.js +1 -2
- package/core/config/constants.d.ts +2 -28
- package/core/config/lr-desktop-config.js +13 -0
- package/core/config/lr-mobile-config.js +13 -0
- package/core/gather/base-artifacts.js +7 -1
- package/core/gather/driver/storage.js +3 -2
- package/core/gather/gatherers/agentic/ard.d.ts +59 -4
- package/core/gather/gatherers/agentic/ard.js +218 -56
- package/core/gather/gatherers/full-page-screenshot.js +2 -2
- package/core/gather/gatherers/image-elements.d.ts +7 -0
- package/core/gather/gatherers/image-elements.js +75 -24
- package/core/gather/gatherers/inspector-issues.js +2 -0
- package/core/gather/gatherers/js-usage.js +2 -0
- package/core/gather/gatherers/scripts.d.ts +16 -3
- package/core/gather/gatherers/scripts.js +157 -14
- package/core/gather/gatherers/stylesheets.js +2 -0
- package/core/gather/gatherers/trace-elements.js +2 -1
- package/core/gather/gatherers/trace.d.ts +6 -0
- package/core/gather/gatherers/trace.js +56 -16
- package/core/lib/arbitrary-equality-map.d.ts +14 -3
- package/core/lib/arbitrary-equality-map.js +113 -6
- package/core/lib/baseline/web-features-data.json +32 -11
- package/core/lib/baseline/web-features-metadata.json +1 -1
- package/core/lib/bf-cache-strings.js +237 -254
- package/core/lib/cdt/Common.d.ts +1 -1
- package/core/lib/cdt/Common.js +2 -2
- package/core/lib/cdt/Platform.d.ts +7 -7
- package/core/lib/cdt/Platform.js +11 -8
- package/core/lib/cdt/SDK.d.ts +5 -1
- package/core/lib/cdt/SDK.js +4 -2
- package/core/lib/cdt/generated/ParsedURL.d.ts +0 -1
- package/core/lib/cdt/generated/ParsedURL.js +3 -9
- package/core/lib/cdt/generated/SourceMap.d.ts +24 -25
- package/core/lib/cdt/generated/SourceMap.js +7 -15
- package/core/lib/deprecations-strings.d.ts +93 -88
- package/core/lib/deprecations-strings.js +20 -14
- package/core/lib/i18n/i18n.d.ts +0 -1
- package/core/lib/i18n/i18n.js +17 -11
- package/core/lib/legacy-javascript/legacy-javascript.d.ts +3 -2
- package/core/lib/legacy-javascript/legacy-javascript.js +235 -111
- package/core/lib/legacy-javascript/polyfill-graph-data.json +94 -89
- package/core/lib/legacy-javascript/polyfill-module-data.json +48 -7
- package/core/lib/minification-estimator.js +10 -10
- package/core/lib/network-request.d.ts +2 -0
- package/core/lib/network-request.js +21 -0
- package/core/lib/page-functions.js +2 -5
- package/core/lib/sentry.d.ts +1 -0
- package/core/lib/sentry.js +3 -0
- package/core/lib/trace-engine.d.ts +1 -0
- package/core/lib/trace-engine.js +2 -0
- package/core/runner.js +9 -3
- package/dist/report/bundle.esm.js +10 -10
- package/dist/report/flow.js +13 -13
- package/dist/report/standalone.js +5 -5
- package/flow-report/src/common.tsx +5 -2
- package/flow-report/src/i18n/i18n.d.ts +2 -0
- package/flow-report/src/summary/summary.tsx +9 -1
- package/flow-report/src/util.d.ts +1 -1
- package/package.json +62 -98
- package/readme.md +13 -18
- package/report/generator/file-namer.d.ts +1 -1
- package/report/generator/file-namer.js +5 -4
- package/report/generator/flow-report-assets.js +1 -3
- package/report/generator/report-assets.js +1 -2
- package/report/renderer/category-renderer.js +2 -2
- package/report/renderer/details-renderer.js +3 -2
- package/report/renderer/dom.d.ts +8 -2
- package/report/renderer/dom.js +29 -1
- package/report/renderer/element-screenshot-renderer.js +3 -0
- package/report/renderer/performance-category-renderer.js +3 -1
- package/report/renderer/report-renderer.js +4 -0
- package/report/renderer/report-utils.d.ts +1 -0
- package/report/renderer/report-utils.js +2 -0
- package/report/renderer/text-encoding.js +2 -2
- package/report/types/augment-dom.d.ts +4 -0
- package/shared/esm-utils.d.ts +0 -8
- package/shared/esm-utils.js +0 -18
- package/shared/localization/format.js +1 -2
- package/shared/localization/locales/ar-XB.json +157 -142
- package/shared/localization/locales/ar.json +157 -142
- package/shared/localization/locales/bg.json +154 -139
- package/shared/localization/locales/ca.json +157 -142
- package/shared/localization/locales/cs.json +148 -133
- package/shared/localization/locales/da.json +153 -138
- package/shared/localization/locales/de.json +152 -137
- package/shared/localization/locales/el.json +153 -138
- package/shared/localization/locales/en-GB.json +160 -145
- package/shared/localization/locales/en-US.json +239 -218
- package/shared/localization/locales/en-XL.json +239 -218
- package/shared/localization/locales/es-419.json +150 -135
- package/shared/localization/locales/es.json +149 -134
- package/shared/localization/locales/fi.json +151 -136
- package/shared/localization/locales/fil.json +154 -139
- package/shared/localization/locales/fr.json +150 -135
- package/shared/localization/locales/he.json +160 -145
- package/shared/localization/locales/hi.json +156 -141
- package/shared/localization/locales/hr.json +154 -139
- package/shared/localization/locales/hu.json +111 -96
- package/shared/localization/locales/id.json +153 -138
- package/shared/localization/locales/it.json +153 -138
- package/shared/localization/locales/ja.json +150 -135
- package/shared/localization/locales/ko.json +42 -27
- package/shared/localization/locales/lt.json +152 -137
- package/shared/localization/locales/lv.json +93 -78
- package/shared/localization/locales/nl.json +149 -134
- package/shared/localization/locales/no.json +152 -137
- package/shared/localization/locales/pl.json +153 -138
- package/shared/localization/locales/pt-PT.json +152 -137
- package/shared/localization/locales/pt.json +157 -142
- package/shared/localization/locales/ro.json +149 -134
- package/shared/localization/locales/ru.json +76 -61
- package/shared/localization/locales/sk.json +153 -138
- package/shared/localization/locales/sl.json +149 -134
- package/shared/localization/locales/sr-Latn.json +151 -136
- package/shared/localization/locales/sr.json +151 -136
- package/shared/localization/locales/sv.json +152 -137
- package/shared/localization/locales/ta.json +156 -141
- package/shared/localization/locales/te.json +157 -142
- package/shared/localization/locales/th.json +111 -96
- package/shared/localization/locales/tr.json +153 -138
- package/shared/localization/locales/uk.json +149 -134
- package/shared/localization/locales/vi.json +162 -147
- package/shared/localization/locales/zh-HK.json +150 -135
- package/shared/localization/locales/zh-TW.json +154 -139
- package/shared/localization/locales/zh.json +152 -137
- package/shared/localization/swap-locale.js +10 -5
- package/shared/root.js +1 -3
- package/third-party/ard/README.md +19 -10
- package/third-party/ard/ard-test.js +189 -266
- package/third-party/ard/ard.d.ts +18 -1
- package/third-party/ard/ard.js +282 -45
- package/third-party/ard/schema-validator.d.ts +14 -5
- package/third-party/ard/schema-validator.js +921 -1177
- package/third-party/ard/spec/schemas/ard-entry.schema.json +176 -0
- package/tsconfig.json +0 -2
- package/types/artifacts.d.ts +43 -7
- package/types/externs.d.ts +4 -0
- package/types/internal/test.d.ts +1 -0
- package/build-tracker.config.js +0 -16
- package/core/lib/cdt/package.json +0 -4
- package/eslint-local-rules.d.cts +0 -9
- package/eslint.config.mjs +0 -242
- package/third-party/ard/spec/schemas/ai-catalog.schema.json +0 -292
- package/third-party/esbuild-plugins-polyfills/LICENSE +0 -3
- package/third-party/esbuild-plugins-polyfills/README.md +0 -7
- package/third-party/esbuild-plugins-polyfills/esbuild-polyfills.d.ts +0 -9
- package/third-party/esbuild-plugins-polyfills/esbuild-polyfills.js +0 -129
- package/types/internal/lookup-closest-locale.d.ts +0 -13
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {makeComputedArtifact} from './computed-artifact.js';
|
|
8
|
+
import {NetworkRecords} from './network-records.js';
|
|
9
|
+
import {ProcessedNavigation} from './processed-navigation.js';
|
|
10
|
+
import {LighthouseError} from '../lib/lh-error.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Match the LCP event with the paint event to get the request of the image actually painted.
|
|
14
|
+
* This could differ from the `ImageElement` associated with the nodeId if e.g. the LCP
|
|
15
|
+
* was a pseudo-element associated with a node containing a smaller background-image.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
class LCPNetworkRecord {
|
|
19
|
+
/**
|
|
20
|
+
* @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog}} data
|
|
21
|
+
* @param {LH.Artifacts.ComputedContext} context
|
|
22
|
+
* @return {Promise<LH.Artifacts.NetworkRequest|undefined>}
|
|
23
|
+
*/
|
|
24
|
+
static async compute_(data, context) {
|
|
25
|
+
const {trace, devtoolsLog} = data;
|
|
26
|
+
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
27
|
+
const processedNavigation = await ProcessedNavigation.request(trace, context);
|
|
28
|
+
if (processedNavigation.timings.largestContentfulPaint === undefined) {
|
|
29
|
+
throw new LighthouseError(LighthouseError.errors.NO_LCP);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Use main-frame-only LCP to match the metric value.
|
|
33
|
+
const lcpEvent = processedNavigation.largestContentfulPaintEvt;
|
|
34
|
+
if (!lcpEvent) return;
|
|
35
|
+
|
|
36
|
+
const lcpImagePaintEvent = trace.traceEvents.filter(e => {
|
|
37
|
+
return e.name === 'LargestImagePaint::Candidate' &&
|
|
38
|
+
e.args.frame === lcpEvent.args.frame &&
|
|
39
|
+
e.args.data?.DOMNodeId === lcpEvent.args.data?.nodeId &&
|
|
40
|
+
e.args.data?.size === lcpEvent.args.data?.size;
|
|
41
|
+
// Get last candidate, in case there was more than one.
|
|
42
|
+
}).sort((a, b) => b.ts - a.ts)[0];
|
|
43
|
+
|
|
44
|
+
const lcpUrl = lcpImagePaintEvent?.args.data?.imageUrl;
|
|
45
|
+
if (!lcpUrl) return;
|
|
46
|
+
|
|
47
|
+
const candidates = networkRecords.filter(record => {
|
|
48
|
+
return record.url === lcpUrl &&
|
|
49
|
+
record.finished &&
|
|
50
|
+
// Same frame as LCP trace event.
|
|
51
|
+
record.frameId === lcpImagePaintEvent.args.frame &&
|
|
52
|
+
record.networkRequestTime < (processedNavigation.timestamps.largestContentfulPaint || 0);
|
|
53
|
+
}).map(record => {
|
|
54
|
+
// Follow any redirects to find the real image request.
|
|
55
|
+
while (record.redirectDestination) {
|
|
56
|
+
record = record.redirectDestination;
|
|
57
|
+
}
|
|
58
|
+
return record;
|
|
59
|
+
}).filter(record => {
|
|
60
|
+
// Don't select if also loaded by some other means (xhr, etc). `resourceType`
|
|
61
|
+
// isn't set on redirect _sources_, so have to check after following redirects.
|
|
62
|
+
return record.resourceType === 'Image' || record.resourceType === 'Media';
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// If there are still multiple candidates, at this point it appears the page
|
|
66
|
+
// simply made multiple requests for the image. The first loaded is the best
|
|
67
|
+
// guess of the request that made the image available for use.
|
|
68
|
+
return candidates.sort((a, b) => a.networkEndTime - b.networkEndTime)[0];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const LCPNetworkRecordComputed = makeComputedArtifact(LCPNetworkRecord, ['devtoolsLog', 'trace']);
|
|
73
|
+
export {LCPNetworkRecordComputed as LCPNetworkRecord};
|
|
74
|
+
|
|
@@ -40,7 +40,7 @@ async function getComputationDataParamsFromTrace(data, context) {
|
|
|
40
40
|
const traceEngineResult = await TraceEngineResult.request(data, context);
|
|
41
41
|
const frameId = traceEngineResult.data.Meta.mainFrameId;
|
|
42
42
|
const navigation =
|
|
43
|
-
traceEngineResult.data.Meta.mainFrameNavigations
|
|
43
|
+
traceEngineResult.data.Meta.mainFrameNavigations.at(-1);
|
|
44
44
|
if (!navigation) {
|
|
45
45
|
throw new Error(`Lantern metrics could not be calculated due to missing navigation id`);
|
|
46
46
|
}
|
|
@@ -9,7 +9,7 @@ import {LighthouseError} from '../../lib/lh-error.js';
|
|
|
9
9
|
import {LargestContentfulPaint} from './largest-contentful-paint.js';
|
|
10
10
|
import {ProcessedNavigation} from '../processed-navigation.js';
|
|
11
11
|
import {TimeToFirstByte} from './time-to-first-byte.js';
|
|
12
|
-
import {
|
|
12
|
+
import {LCPNetworkRecord} from '../lcp-network-record.js';
|
|
13
13
|
import {NavigationInsights} from '../navigation-insights.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -32,7 +32,7 @@ class LCPBreakdown {
|
|
|
32
32
|
|
|
33
33
|
const {timing: ttfb} = await TimeToFirstByte.request(data, context);
|
|
34
34
|
|
|
35
|
-
const lcpRecord = await
|
|
35
|
+
const lcpRecord = await LCPNetworkRecord.request(data, context);
|
|
36
36
|
if (!lcpRecord) {
|
|
37
37
|
return {ttfb};
|
|
38
38
|
}
|
|
@@ -42,15 +42,15 @@ class LCPBreakdown {
|
|
|
42
42
|
const throttleRatio = metricLcp / observedLcp;
|
|
43
43
|
|
|
44
44
|
const unclampedLoadStart = (lcpRecord.networkRequestTime - timeOrigin) * throttleRatio;
|
|
45
|
-
const
|
|
45
|
+
const loadStart = Math.max(ttfb, Math.min(unclampedLoadStart, metricLcp));
|
|
46
46
|
|
|
47
47
|
const unclampedLoadEnd = (lcpRecord.networkEndTime - timeOrigin) * throttleRatio;
|
|
48
|
-
const
|
|
48
|
+
const loadEnd = Math.max(loadStart, Math.min(unclampedLoadEnd, metricLcp));
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
ttfb,
|
|
52
|
-
loadDelay,
|
|
53
|
-
loadDuration,
|
|
52
|
+
loadDelay: loadStart - ttfb,
|
|
53
|
+
loadDuration: loadEnd - loadStart,
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {calculateDocFirstByteTs} from '@paulirish/trace_engine/models/trace/insights/Common.js';
|
|
8
8
|
|
|
9
|
+
import {Helpers} from '../../lib/trace-engine.js';
|
|
9
10
|
import {makeComputedArtifact} from '../computed-artifact.js';
|
|
10
11
|
import {NavigationMetric} from './navigation-metric.js';
|
|
11
12
|
import {MainResource} from '../main-resource.js';
|
|
@@ -52,15 +53,16 @@ class TimeToFirstByte extends NavigationMetric {
|
|
|
52
53
|
await NavigationInsights.request({trace, settings, SourceMaps, HostDPR}, context);
|
|
53
54
|
const lcpBreakdown = navInsights.model.LCPBreakdown;
|
|
54
55
|
|
|
56
|
+
const navigationId = Helpers.Trace.navigationIDForEvent(navInsights.navigation);
|
|
57
|
+
|
|
55
58
|
// Defer to LCP breakdown, but if there's no LCP fallback to manual calculation.
|
|
56
59
|
if (lcpBreakdown && !(lcpBreakdown instanceof Error) && lcpBreakdown.subparts) {
|
|
57
60
|
return {
|
|
58
61
|
timing: lcpBreakdown.subparts.ttfb.range / 1000,
|
|
59
62
|
timestamp: lcpBreakdown.subparts.ttfb.max,
|
|
60
63
|
};
|
|
61
|
-
} else if (navInsights.navigation
|
|
62
|
-
const request = traceEngineResult.data.NetworkRequests.byId.get(
|
|
63
|
-
navInsights.navigation.args.data.navigationId);
|
|
64
|
+
} else if (navInsights.navigation && navigationId) {
|
|
65
|
+
const request = traceEngineResult.data.NetworkRequests.byId.get(navigationId);
|
|
64
66
|
if (!request) {
|
|
65
67
|
throw new Error();
|
|
66
68
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {Helpers} from '../lib/trace-engine.js';
|
|
7
8
|
import {makeComputedArtifact} from './computed-artifact.js';
|
|
8
9
|
import {ProcessedTrace} from './processed-trace.js';
|
|
9
10
|
import {TraceEngineResult} from './trace-engine-result.js';
|
|
@@ -26,8 +27,8 @@ class NavigationInsights {
|
|
|
26
27
|
const navigationId = processedTrace.timeOriginEvt.args.data?.navigationId;
|
|
27
28
|
if (!navigationId) throw new Error('No navigationId found');
|
|
28
29
|
|
|
29
|
-
const navInsights = [...traceEngineResult.insights.values()]
|
|
30
|
-
|
|
30
|
+
const navInsights = [...traceEngineResult.insights.values()].find(insightSet =>
|
|
31
|
+
Helpers.Trace.navigationIDForEvent(insightSet.navigation) === navigationId);
|
|
31
32
|
if (!navInsights) throw new Error('No navigations insights found');
|
|
32
33
|
|
|
33
34
|
return navInsights;
|
|
@@ -11,6 +11,26 @@ import {ProcessedTrace} from './processed-trace.js';
|
|
|
11
11
|
import {NetworkRecords} from './network-records.js';
|
|
12
12
|
import {TraceEngineResult} from './trace-engine-result.js';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* ResourceSendRequest URLs in the trace retain #hash fragments, whereas CDP
|
|
16
|
+
* Network.requestWillBeSent, Page.frameNavigated, initiator.url, documentURL,
|
|
17
|
+
* and V8 script events strip #hash fragments, and Lantern's NetworkAnalyzer
|
|
18
|
+
* requires request.url not to have a fragment.
|
|
19
|
+
* TODO: Remove once @paulirish/trace_engine includes the fix in LanternComputationData.
|
|
20
|
+
* @param {string} urlStr
|
|
21
|
+
* @return {string}
|
|
22
|
+
*/
|
|
23
|
+
function stripUrlHash(urlStr) {
|
|
24
|
+
if (!urlStr.includes('#')) return urlStr;
|
|
25
|
+
try {
|
|
26
|
+
const url = new URL(urlStr);
|
|
27
|
+
url.hash = '';
|
|
28
|
+
return url.href;
|
|
29
|
+
} catch {
|
|
30
|
+
return urlStr;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
class PageDependencyGraph {
|
|
15
35
|
/**
|
|
16
36
|
* @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, settings: LH.Audit.Context['settings'], URL: LH.Artifacts['URL'], SourceMaps: LH.Artifacts['SourceMaps'], HostDPR: LH.Artifacts['HostDPR'], fromTrace: boolean}} data
|
|
@@ -27,7 +47,41 @@ class PageDependencyGraph {
|
|
|
27
47
|
if (data.fromTrace) {
|
|
28
48
|
const traceEngineResult =
|
|
29
49
|
await TraceEngineResult.request({trace, settings, SourceMaps, HostDPR}, context);
|
|
30
|
-
|
|
50
|
+
let parsedTrace = traceEngineResult.data;
|
|
51
|
+
if (parsedTrace.NetworkRequests.byTime.some(
|
|
52
|
+
r => r.args.data.url.includes('#') ||
|
|
53
|
+
r.args.data.redirects.some(red => red.url.includes('#'))
|
|
54
|
+
)) {
|
|
55
|
+
const byTime = parsedTrace.NetworkRequests.byTime.map(request => {
|
|
56
|
+
const url = stripUrlHash(request.args.data.url);
|
|
57
|
+
const redirects = request.args.data.redirects.map(redirect => ({
|
|
58
|
+
...redirect,
|
|
59
|
+
url: stripUrlHash(redirect.url),
|
|
60
|
+
}));
|
|
61
|
+
if (url === request.args.data.url &&
|
|
62
|
+
redirects.every((red, i) => red.url === request.args.data.redirects[i].url)) {
|
|
63
|
+
return request;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
...request,
|
|
67
|
+
args: {
|
|
68
|
+
...request.args,
|
|
69
|
+
data: {
|
|
70
|
+
...request.args.data,
|
|
71
|
+
url,
|
|
72
|
+
redirects,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
parsedTrace = {
|
|
78
|
+
...parsedTrace,
|
|
79
|
+
NetworkRequests: {
|
|
80
|
+
...parsedTrace.NetworkRequests,
|
|
81
|
+
byTime,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
31
85
|
const requests =
|
|
32
86
|
Lantern.TraceEngineComputationData.createNetworkRequests(trace, parsedTrace);
|
|
33
87
|
const graph =
|
|
@@ -36,10 +90,6 @@ class PageDependencyGraph {
|
|
|
36
90
|
return graph;
|
|
37
91
|
}
|
|
38
92
|
|
|
39
|
-
// TODO: currently the trace version has no requests that failed, or requests that have "Preflight".
|
|
40
|
-
// so the following gets the devtools log version _closer_ to the exact same results as the trace.
|
|
41
|
-
// const lanternRequests = networkRecords.map(NetworkRequest.asLanternNetworkRequest).filter(r => !r.failed && r.resourceType !== 'Preflight');
|
|
42
|
-
|
|
43
93
|
const lanternRequests = networkRecords.map(NetworkRequest.asLanternNetworkRequest);
|
|
44
94
|
return Lantern.Graph.PageDependencyGraph.createGraph(mainThreadEvents, lanternRequests, URL);
|
|
45
95
|
}
|
|
@@ -20,13 +20,10 @@ class Speedline {
|
|
|
20
20
|
// speedline() may throw without a promise, so we resolve immediately
|
|
21
21
|
// to get in a promise chain.
|
|
22
22
|
return ProcessedTrace.request(trace, context).then(processedTrace => {
|
|
23
|
-
// Use a shallow copy of traceEvents so speedline can sort as it pleases.
|
|
24
|
-
// See https://github.com/GoogleChrome/lighthouse/issues/2333
|
|
25
|
-
const traceEvents = trace.traceEvents.slice();
|
|
26
23
|
// Force use of timeOrigin as reference point for speedline
|
|
27
24
|
// See https://github.com/GoogleChrome/lighthouse/issues/2095
|
|
28
25
|
const timeOrigin = processedTrace.timestamps.timeOrigin;
|
|
29
|
-
return speedline(traceEvents, {
|
|
26
|
+
return speedline(trace.traceEvents, {
|
|
30
27
|
timeOrigin,
|
|
31
28
|
fastMode: true,
|
|
32
29
|
include: 'speedIndex',
|
|
@@ -46,7 +43,25 @@ class Speedline {
|
|
|
46
43
|
throw new LighthouseError(LighthouseError.errors.SPEEDINDEX_OF_ZERO);
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
|
|
46
|
+
// Evict the ~1 MB per-frame decoded RGBA buffers and histograms cached inside
|
|
47
|
+
// speedline-core's frame closures, keeping only the getters Lighthouse uses.
|
|
48
|
+
// TODO(https://github.com/paulirish/speedline/issues/122): Upstream to speedline-core
|
|
49
|
+
// so `frame()` in `lib/frame.js` does not cache `_parsedImage` on frame closures.
|
|
50
|
+
const frames = speedline.frames.map(frame => {
|
|
51
|
+
const imgBuff = frame.getImage();
|
|
52
|
+
const timeStamp = frame.getTimeStamp();
|
|
53
|
+
const isProgressInterpolated = frame.isProgressInterpolated();
|
|
54
|
+
return {
|
|
55
|
+
getImage: () => imgBuff,
|
|
56
|
+
getTimeStamp: () => timeStamp,
|
|
57
|
+
isProgressInterpolated: () => isProgressInterpolated,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
...speedline,
|
|
63
|
+
frames,
|
|
64
|
+
};
|
|
50
65
|
});
|
|
51
66
|
}
|
|
52
67
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import log from 'lighthouse-logger';
|
|
8
8
|
|
|
9
9
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
10
|
+
import * as Lantern from '../lib/lantern/lantern.js';
|
|
10
11
|
import * as TraceEngine from '../lib/trace-engine.js';
|
|
11
12
|
import {makeComputedArtifact} from './computed-artifact.js';
|
|
12
13
|
import {CumulativeLayoutShift} from './metrics/cumulative-layout-shift.js';
|
|
@@ -26,7 +27,9 @@ class TraceEngineResult {
|
|
|
26
27
|
* @return {Promise<LH.Artifacts.TraceEngineResult>}
|
|
27
28
|
*/
|
|
28
29
|
static async runTraceEngine(_traceEvents, settings, SourceMaps, HostDPR) {
|
|
29
|
-
const
|
|
30
|
+
const config = TraceEngine.Types.Configuration.defaults();
|
|
31
|
+
config.enableSoftNavigation = false;
|
|
32
|
+
const processor = new TraceEngine.TraceProcessor(TraceEngine.TraceHandlers, config);
|
|
30
33
|
const traceEvents =
|
|
31
34
|
/** @type {import('@paulirish/trace_engine').Types.Events.Event[]} */ (_traceEvents);
|
|
32
35
|
|
|
@@ -66,8 +69,20 @@ class TraceEngineResult {
|
|
|
66
69
|
});
|
|
67
70
|
if (!processor.data) throw new Error('No data');
|
|
68
71
|
if (!processor.insights) throw new Error('No insights');
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
const data = processor.data;
|
|
73
|
+
const insights = processor.insights;
|
|
74
|
+
this.localizeInsights(insights);
|
|
75
|
+
|
|
76
|
+
// Release intermediate handler state retained in module-level TraceHandlers singletons
|
|
77
|
+
// and static Lantern simulation node timings after trace processing completes.
|
|
78
|
+
for (const handler of Object.values(TraceEngine.TraceHandlers)) {
|
|
79
|
+
handler.reset();
|
|
80
|
+
}
|
|
81
|
+
if (!process.env.LANTERN_DEBUG) {
|
|
82
|
+
Lantern.Simulation.Simulator.allNodeTimings.clear();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {data, insights};
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
/**
|
|
@@ -236,8 +251,9 @@ class TraceEngineResult {
|
|
|
236
251
|
const layoutShiftEvents = new Set(
|
|
237
252
|
CumulativeLayoutShift.getLayoutShiftEvents(processedTrace).map(e => e.event));
|
|
238
253
|
|
|
239
|
-
// Avoid modifying the input array.
|
|
240
|
-
|
|
254
|
+
// Avoid modifying the input array, copying only if an event is modified.
|
|
255
|
+
let traceEvents = data.trace.traceEvents;
|
|
256
|
+
let copied = false;
|
|
241
257
|
for (let i = 0; i < traceEvents.length; i++) {
|
|
242
258
|
let event = traceEvents[i];
|
|
243
259
|
if (event.name !== 'LayoutShift') continue;
|
|
@@ -245,6 +261,10 @@ class TraceEngineResult {
|
|
|
245
261
|
|
|
246
262
|
const isConsidered = layoutShiftEvents.has(event);
|
|
247
263
|
if (event.args.data.had_recent_input && isConsidered) {
|
|
264
|
+
if (!copied) {
|
|
265
|
+
traceEvents = [...traceEvents];
|
|
266
|
+
copied = true;
|
|
267
|
+
}
|
|
248
268
|
event = JSON.parse(JSON.stringify(event));
|
|
249
269
|
// @ts-expect-error impossible for data to be missing.
|
|
250
270
|
event.args.data.had_recent_input = false;
|
|
@@ -66,8 +66,21 @@ class UnusedCSS {
|
|
|
66
66
|
let usedUncompressedBytes = 0;
|
|
67
67
|
const totalUncompressedBytes = stylesheetInfo.content.length;
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// CSS rule usage tracking can return overlapping ranges (e.g. a nested rule inside its
|
|
70
|
+
// parent rule's range) or duplicate ranges (parent rules are emitted once per used child rule).
|
|
71
|
+
// Sort and merge overlapping ranges so used bytes are only counted once.
|
|
72
|
+
const usedRules = stylesheetInfo.usedRules
|
|
73
|
+
.slice()
|
|
74
|
+
.sort((a, b) => a.startOffset - b.startOffset);
|
|
75
|
+
|
|
76
|
+
let lastEndOffset = -1;
|
|
77
|
+
for (const usedRule of usedRules) {
|
|
78
|
+
if (usedRule.endOffset <= lastEndOffset) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const startOffset = Math.max(usedRule.startOffset, lastEndOffset);
|
|
82
|
+
usedUncompressedBytes += usedRule.endOffset - startOffset;
|
|
83
|
+
lastEndOffset = usedRule.endOffset;
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
const compressedSize = estimateCompressedContentSize(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type GathererConstructor = typeof import("../gather/base-gatherer.js").default;
|
|
2
2
|
export type Audit = typeof import("../audits/audit.js")["Audit"];
|
|
3
3
|
export type Gatherer = InstanceType<GathererConstructor>;
|
|
4
|
+
/** @type {Map<string, Promise<any>>} */
|
|
5
|
+
export const bundledModules: Map<string, Promise<any>>;
|
|
4
6
|
/**
|
|
5
7
|
* // TODO(bckenny): could adopt "jsonified" type to ensure T will survive JSON
|
|
6
8
|
* round trip: https://github.com/Microsoft/TypeScript/issues/21838
|
|
@@ -33,6 +35,9 @@ export const mergeConfigFragment: LH.Config.Merge;
|
|
|
33
35
|
* @return {Array<T>}
|
|
34
36
|
*/
|
|
35
37
|
export function mergeConfigFragmentArrayByKey<T extends Record<string, any>>(baseArray: Array<T> | null | undefined, extensionArray: Array<T> | null | undefined, keyFn: (item: T) => string): Array<T>;
|
|
38
|
+
/** @typedef {typeof import('../gather/base-gatherer.js').default} GathererConstructor */
|
|
39
|
+
/** @typedef {typeof import('../audits/audit.js')['Audit']} Audit */
|
|
40
|
+
/** @typedef {InstanceType<GathererConstructor>} Gatherer */
|
|
36
41
|
/**
|
|
37
42
|
* If any items with identical `path` properties are found in the input array,
|
|
38
43
|
* merge their `options` properties into the first instance and then discard any
|
|
@@ -7,15 +7,13 @@ import path from 'path';
|
|
|
7
7
|
import {createRequire} from 'module';
|
|
8
8
|
import url from 'url';
|
|
9
9
|
|
|
10
|
-
import {isEqual} from 'lodash-es';
|
|
11
|
-
|
|
12
10
|
import * as LH from '../../types/lh.js';
|
|
13
11
|
import * as constants from './constants.js';
|
|
14
12
|
import ConfigPlugin from './config-plugin.js';
|
|
15
13
|
import {Runner} from '../runner.js';
|
|
16
14
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
17
15
|
import * as validation from './validation.js';
|
|
18
|
-
import {
|
|
16
|
+
import {isEqual} from '../lib/arbitrary-equality-map.js';
|
|
19
17
|
|
|
20
18
|
const require = createRequire(import.meta.url);
|
|
21
19
|
|
|
@@ -23,21 +21,6 @@ const require = createRequire(import.meta.url);
|
|
|
23
21
|
/** @typedef {typeof import('../audits/audit.js')['Audit']} Audit */
|
|
24
22
|
/** @typedef {InstanceType<GathererConstructor>} Gatherer */
|
|
25
23
|
|
|
26
|
-
function isBundledEnvironment() {
|
|
27
|
-
// If we're in DevTools or LightRider, we are definitely bundled.
|
|
28
|
-
// TODO: refactor and delete `global.isDevtools`.
|
|
29
|
-
if (global.isDevtools || global.isLightrider) return true;
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
// Not foolproof, but `lighthouse-logger` is a dependency of lighthouse that should always be resolvable.
|
|
33
|
-
// `require.resolve` will only throw in atypical/bundled environments.
|
|
34
|
-
require.resolve('lighthouse-logger');
|
|
35
|
-
return false;
|
|
36
|
-
} catch (err) {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
24
|
/**
|
|
42
25
|
* If any items with identical `path` properties are found in the input array,
|
|
43
26
|
* merge their `options` properties into the first instance and then discard any
|
|
@@ -226,6 +209,8 @@ async function requireWrapper(requirePath) {
|
|
|
226
209
|
let module;
|
|
227
210
|
if (bundledModules.has(requirePath)) {
|
|
228
211
|
module = await bundledModules.get(requirePath);
|
|
212
|
+
} else if (bundledModules.size > 0) {
|
|
213
|
+
throw new Error(`Unable to locate module '${requirePath}' in bundled Lighthouse`);
|
|
229
214
|
} else if (requirePath.match(/\.(js|mjs|cjs)$/)) {
|
|
230
215
|
module = await import(requirePath);
|
|
231
216
|
} else {
|
|
@@ -285,18 +270,12 @@ function requireAudit(auditPath, coreAuditList, configDir) {
|
|
|
285
270
|
const coreAudit = coreAuditList.find(a => a === auditPathJs);
|
|
286
271
|
let requirePath = `../audits/${auditPath}`;
|
|
287
272
|
if (!coreAudit) {
|
|
288
|
-
if (
|
|
273
|
+
if (bundledModules.has(auditPath)) {
|
|
289
274
|
// This is for plugin bundling.
|
|
290
275
|
requirePath = auditPath;
|
|
291
276
|
} else {
|
|
292
277
|
// Otherwise, attempt to find it elsewhere. This throws if not found.
|
|
293
|
-
|
|
294
|
-
if (isBundledEnvironment()) {
|
|
295
|
-
// Use a relative path so bundler can easily expose it.
|
|
296
|
-
requirePath = path.relative(getModuleDirectory(import.meta), absolutePath);
|
|
297
|
-
} else {
|
|
298
|
-
requirePath = absolutePath;
|
|
299
|
-
}
|
|
278
|
+
requirePath = resolveModulePath(auditPath, configDir, 'audit');
|
|
300
279
|
}
|
|
301
280
|
}
|
|
302
281
|
|
|
@@ -374,7 +353,7 @@ async function mergePlugins(config, configDir, flags) {
|
|
|
374
353
|
validation.assertValidPluginName(config, pluginName);
|
|
375
354
|
|
|
376
355
|
// In bundled contexts, `resolveModulePath` will fail, so use the raw pluginName directly.
|
|
377
|
-
const pluginPath =
|
|
356
|
+
const pluginPath = bundledModules.has(pluginName) ?
|
|
378
357
|
pluginName :
|
|
379
358
|
resolveModulePath(pluginName, configDir, 'plugin');
|
|
380
359
|
const rawPluginJson = await requireWrapper(pluginPath);
|
|
@@ -513,7 +492,7 @@ function resolveModulePath(moduleIdentifier, configDir, category) {
|
|
|
513
492
|
const errorString = 'Unable to locate ' + (category ? `${category}: ` : '') +
|
|
514
493
|
`\`${moduleIdentifier}\`.
|
|
515
494
|
Tried to resolve the module from these locations:
|
|
516
|
-
${
|
|
495
|
+
${import.meta.dirname}
|
|
517
496
|
${cwdPath}`;
|
|
518
497
|
|
|
519
498
|
if (!configDir) {
|
|
@@ -606,6 +585,7 @@ function deepCloneConfigJson(json) {
|
|
|
606
585
|
}
|
|
607
586
|
|
|
608
587
|
export {
|
|
588
|
+
bundledModules,
|
|
609
589
|
deepClone,
|
|
610
590
|
deepCloneConfigJson,
|
|
611
591
|
mergeConfigFragment,
|
package/core/config/config.js
CHANGED
|
@@ -28,11 +28,10 @@ import {
|
|
|
28
28
|
mergeConfigFragment,
|
|
29
29
|
mergeConfigFragmentArrayByKey,
|
|
30
30
|
} from './config-helpers.js';
|
|
31
|
-
import {getModuleDirectory} from '../../shared/esm-utils.js';
|
|
32
31
|
import * as format from '../../shared/localization/format.js';
|
|
33
32
|
|
|
34
33
|
const defaultConfigPath = path.join(
|
|
35
|
-
|
|
34
|
+
import.meta.dirname,
|
|
36
35
|
'../../config/default-config.js'
|
|
37
36
|
);
|
|
38
37
|
|
|
@@ -1,31 +1,4 @@
|
|
|
1
|
-
export const throttling:
|
|
2
|
-
DEVTOOLS_RTT_ADJUSTMENT_FACTOR: number;
|
|
3
|
-
DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR: number;
|
|
4
|
-
mobileSlow4G: {
|
|
5
|
-
rttMs: number;
|
|
6
|
-
throughputKbps: number;
|
|
7
|
-
requestLatencyMs: number;
|
|
8
|
-
downloadThroughputKbps: number;
|
|
9
|
-
uploadThroughputKbps: number;
|
|
10
|
-
cpuSlowdownMultiplier: number;
|
|
11
|
-
};
|
|
12
|
-
mobileRegular3G: {
|
|
13
|
-
rttMs: number;
|
|
14
|
-
throughputKbps: number;
|
|
15
|
-
requestLatencyMs: number;
|
|
16
|
-
downloadThroughputKbps: number;
|
|
17
|
-
uploadThroughputKbps: number;
|
|
18
|
-
cpuSlowdownMultiplier: number;
|
|
19
|
-
};
|
|
20
|
-
desktopDense4G: {
|
|
21
|
-
rttMs: number;
|
|
22
|
-
throughputKbps: number;
|
|
23
|
-
cpuSlowdownMultiplier: number;
|
|
24
|
-
requestLatencyMs: number;
|
|
25
|
-
downloadThroughputKbps: number;
|
|
26
|
-
uploadThroughputKbps: number;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
1
|
+
export const throttling: Lantern.Simulation.ThrottlingSettings;
|
|
29
2
|
export namespace screenEmulationMetrics {
|
|
30
3
|
export { MOTOGPOWER_EMULATION_METRICS as mobile };
|
|
31
4
|
export { DESKTOP_EMULATION_METRICS as desktop };
|
|
@@ -42,6 +15,7 @@ export namespace nonSimulatedSettingsOverrides {
|
|
|
42
15
|
let networkQuietThresholdMs: number;
|
|
43
16
|
let cpuQuietThresholdMs: number;
|
|
44
17
|
}
|
|
18
|
+
import * as Lantern from '../lib/lantern/lantern.js';
|
|
45
19
|
/**
|
|
46
20
|
* @type {Required<LH.SharedFlagsSettings['screenEmulation']>}
|
|
47
21
|
*/
|
|
@@ -24,6 +24,19 @@ const config = {
|
|
|
24
24
|
'bf-cache',
|
|
25
25
|
],
|
|
26
26
|
},
|
|
27
|
+
audits: [
|
|
28
|
+
// Lightrider does not support WebSocket connections.
|
|
29
|
+
// See https://github.com/GoogleChrome/lighthouse/issues/12459
|
|
30
|
+
{
|
|
31
|
+
path: 'errors-in-console',
|
|
32
|
+
options: {
|
|
33
|
+
ignoredPatterns: [
|
|
34
|
+
'ERR_BLOCKED_BY_CLIENT.Inspector',
|
|
35
|
+
/^WebSocket connection to 'wss?:\/\/.*' failed:/,
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
27
40
|
};
|
|
28
41
|
|
|
29
42
|
export default config;
|
|
@@ -24,6 +24,19 @@ const config = {
|
|
|
24
24
|
'bf-cache',
|
|
25
25
|
],
|
|
26
26
|
},
|
|
27
|
+
audits: [
|
|
28
|
+
// Lightrider does not support WebSocket connections.
|
|
29
|
+
// See https://github.com/GoogleChrome/lighthouse/issues/12459
|
|
30
|
+
{
|
|
31
|
+
path: 'errors-in-console',
|
|
32
|
+
options: {
|
|
33
|
+
ignoredPatterns: [
|
|
34
|
+
'ERR_BLOCKED_BY_CLIENT.Inspector',
|
|
35
|
+
/^WebSocket connection to 'wss?:\/\/.*' failed:/,
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
27
40
|
};
|
|
28
41
|
|
|
29
42
|
export default config;
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import log from 'lighthouse-logger';
|
|
8
|
-
import {isEqual} from 'lodash-es';
|
|
9
8
|
|
|
10
9
|
import {
|
|
11
10
|
getBrowserVersion, getBenchmarkIndex, getEnvironmentWarnings,
|
|
12
11
|
getDevicePixelRatio,
|
|
13
12
|
} from './driver/environment.js';
|
|
13
|
+
import {isEqual} from '../lib/arbitrary-equality-map.js';
|
|
14
|
+
import {Sentry} from '../lib/sentry.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* @param {LH.Config.ResolvedConfig} resolvedConfig
|
|
@@ -23,6 +24,11 @@ async function getBaseArtifacts(resolvedConfig, driver, context) {
|
|
|
23
24
|
const {userAgent, product} = await getBrowserVersion(driver.defaultSession);
|
|
24
25
|
const HostDPR = await getDevicePixelRatio(driver.executionContext);
|
|
25
26
|
|
|
27
|
+
const [name, version] = product.split('/');
|
|
28
|
+
if (name && version) {
|
|
29
|
+
Sentry.setContext('browser', {name, version});
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
return {
|
|
27
33
|
// Meta artifacts.
|
|
28
34
|
fetchTime: new Date().toJSON(),
|
|
@@ -41,13 +41,14 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
|
41
41
|
* @return {Promise<LH.IcuMessage[]>}
|
|
42
42
|
*/
|
|
43
43
|
async function clearDataForOrigin(session, url, clearStorageTypes) {
|
|
44
|
+
const origin = new URL(url).origin;
|
|
45
|
+
if (origin === 'null') return [];
|
|
46
|
+
|
|
44
47
|
const status = {msg: 'Cleaning origin data', id: 'lh:storage:clearDataForOrigin'};
|
|
45
48
|
log.time(status);
|
|
46
49
|
|
|
47
50
|
const warnings = [];
|
|
48
51
|
|
|
49
|
-
const origin = new URL(url).origin;
|
|
50
|
-
|
|
51
52
|
const typesToClear = clearStorageTypes.join(',');
|
|
52
53
|
|
|
53
54
|
// `Storage.clearDataForOrigin` is one of our PROTOCOL_TIMEOUT culprits and this command is also
|