lighthouse 12.6.1 → 12.7.0-dev.20250628
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
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/NetworkDependencyTree.js';
|
|
7
|
+
import {UIStrings, TOO_MANY_PRECONNECTS_THRESHOLD} from '@paulirish/trace_engine/models/trace/insights/NetworkDependencyTree.js';
|
|
8
8
|
|
|
9
9
|
import {Audit} from '../audit.js';
|
|
10
10
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
11
|
-
import {adaptInsightToAuditProduct} from './insight-audit.js';
|
|
11
|
+
import {adaptInsightToAuditProduct, makeNodeItemForNodeId} from './insight-audit.js';
|
|
12
12
|
|
|
13
13
|
// eslint-disable-next-line max-len
|
|
14
14
|
const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/NetworkDependencyTree.js', UIStrings);
|
|
@@ -24,8 +24,8 @@ class NetworkDependencyTreeInsight extends Audit {
|
|
|
24
24
|
failureTitle: str_(UIStrings.title),
|
|
25
25
|
description: str_(UIStrings.description),
|
|
26
26
|
guidanceLevel: 1,
|
|
27
|
-
requiredArtifacts: ['Trace', 'SourceMaps'],
|
|
28
|
-
replacesAudits: ['critical-request-chains'],
|
|
27
|
+
requiredArtifacts: ['Trace', 'SourceMaps', 'TraceElements'],
|
|
28
|
+
replacesAudits: ['critical-request-chains', 'uses-rel-preconnect'],
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -59,15 +59,92 @@ class NetworkDependencyTreeInsight extends Audit {
|
|
|
59
59
|
*/
|
|
60
60
|
static async audit(artifacts, context) {
|
|
61
61
|
return adaptInsightToAuditProduct(artifacts, context, 'NetworkDependencyTree', (insight) => {
|
|
62
|
-
const
|
|
62
|
+
const list = [];
|
|
63
|
+
let sectionDetails;
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
sectionDetails = /** @type {LH.Audit.Details.NetworkTree} */({
|
|
65
66
|
type: 'network-tree',
|
|
66
|
-
chains,
|
|
67
|
+
chains: this.traceEngineNodesToDetailsNodes(insight.rootNodes),
|
|
67
68
|
longestChain: {
|
|
68
69
|
duration: Math.round(insight.maxTime / 1000),
|
|
69
70
|
},
|
|
70
|
-
};
|
|
71
|
+
});
|
|
72
|
+
list.push(Audit.makeListDetailSectionItem(sectionDetails));
|
|
73
|
+
|
|
74
|
+
// Preconnected origins table.
|
|
75
|
+
if (insight.preconnectedOrigins.length) {
|
|
76
|
+
/** @type {LH.Audit.Details.Table['headings']} */
|
|
77
|
+
const headings = [
|
|
78
|
+
/* eslint-disable max-len */
|
|
79
|
+
{key: 'origin', valueType: 'text', subItemsHeading: {key: 'warning'}, label: str_(UIStrings.columnOrigin)},
|
|
80
|
+
{key: 'source', valueType: 'node', label: str_(UIStrings.columnSource)},
|
|
81
|
+
/* eslint-enable max-len */
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
/** @type {LH.Audit.Details.Table['items']} */
|
|
85
|
+
const items = insight.preconnectedOrigins.map(c => {
|
|
86
|
+
const warnings = [];
|
|
87
|
+
if (c.unused) {
|
|
88
|
+
warnings.push(str_(UIStrings.unusedWarning));
|
|
89
|
+
}
|
|
90
|
+
if (c.crossorigin) {
|
|
91
|
+
warnings.push(str_(UIStrings.crossoriginWarning));
|
|
92
|
+
}
|
|
93
|
+
/** @type {LH.Audit.Details.TableSubItems} */
|
|
94
|
+
const subItems = {
|
|
95
|
+
type: 'subitems',
|
|
96
|
+
items: warnings.map(warning => ({warning})),
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
origin: c.url,
|
|
100
|
+
source: c.source === 'DOM' ?
|
|
101
|
+
makeNodeItemForNodeId(artifacts.TraceElements, c.node_id) :
|
|
102
|
+
{type: 'text', value: c.headerText},
|
|
103
|
+
subItems,
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
sectionDetails = Audit.makeTableDetails(headings, items);
|
|
108
|
+
} else {
|
|
109
|
+
sectionDetails = /** @type {LH.Audit.Details.TextValue} */ (
|
|
110
|
+
{type: 'text', value: str_(UIStrings.noPreconnectOrigins)});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
list.push(Audit.makeListDetailSectionItem(
|
|
114
|
+
sectionDetails,
|
|
115
|
+
str_(UIStrings.preconnectOriginsTableTitle),
|
|
116
|
+
str_(UIStrings.preconnectOriginsTableDescription)));
|
|
117
|
+
|
|
118
|
+
// Estimated savings table.
|
|
119
|
+
if (insight.preconnectCandidates.length) {
|
|
120
|
+
/** @type {LH.Audit.Details.Table['headings']} */
|
|
121
|
+
const headings = [
|
|
122
|
+
{key: 'origin', valueType: 'text', label: str_(UIStrings.columnOrigin)},
|
|
123
|
+
{key: 'wastedMs', valueType: 'ms', label: str_(UIStrings.columnWastedMs)},
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
/** @type {LH.Audit.Details.Table['items']} */
|
|
127
|
+
const items = insight.preconnectCandidates.map(c => {
|
|
128
|
+
return {origin: c.origin, wastedMs: c.wastedMs};
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
sectionDetails = Audit.makeTableDetails(headings, items);
|
|
132
|
+
} else {
|
|
133
|
+
sectionDetails = /** @type {LH.Audit.Details.TextValue} */ (
|
|
134
|
+
{type: 'text', value: str_(UIStrings.noPreconnectCandidates)});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
list.push(Audit.makeListDetailSectionItem(
|
|
138
|
+
sectionDetails,
|
|
139
|
+
str_(UIStrings.estSavingTableTitle),
|
|
140
|
+
str_(UIStrings.estSavingTableDescription)));
|
|
141
|
+
|
|
142
|
+
const warnings = [];
|
|
143
|
+
if (insight.preconnectedOrigins.length > TOO_MANY_PRECONNECTS_THRESHOLD) {
|
|
144
|
+
warnings.push(str_(UIStrings.tooManyPreconnectLinksWarning));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {details: Audit.makeListDetails(list), warnings};
|
|
71
148
|
});
|
|
72
149
|
}
|
|
73
150
|
}
|
|
@@ -41,7 +41,7 @@ class RenderBlockingInsight extends Audit {
|
|
|
41
41
|
const headings = [
|
|
42
42
|
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
43
43
|
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize)},
|
|
44
|
-
{key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.
|
|
44
|
+
{key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnDuration)},
|
|
45
45
|
];
|
|
46
46
|
/** @type {LH.Audit.Details.Table['items']} */
|
|
47
47
|
const items = insight.renderBlockingRequests.map(request => ({
|
|
@@ -62,7 +62,7 @@ class LayoutShifts extends Audit {
|
|
|
62
62
|
const SourceMaps = artifacts.SourceMaps;
|
|
63
63
|
const traceEngineResult =
|
|
64
64
|
await TraceEngineResult.request({trace, settings, SourceMaps}, context);
|
|
65
|
-
const clusters = traceEngineResult.
|
|
65
|
+
const clusters = traceEngineResult.parsedTrace.LayoutShifts.clusters ?? [];
|
|
66
66
|
const {cumulativeLayoutShift: clsSavings, impactByNodeId} =
|
|
67
67
|
await CumulativeLayoutShiftComputed.request(trace, context);
|
|
68
68
|
const traceElements = artifacts.TraceElements
|
|
@@ -89,7 +89,7 @@ class LayoutShifts extends Audit {
|
|
|
89
89
|
.slice(0, MAX_LAYOUT_SHIFTS);
|
|
90
90
|
for (const event of topLayoutShiftEvents) {
|
|
91
91
|
const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent(
|
|
92
|
-
event.args.data.impacted_nodes || [], impactByNodeId
|
|
92
|
+
event.args.data.impacted_nodes || [], impactByNodeId);
|
|
93
93
|
const biggestImpactElement = traceElements.find(t => t.nodeId === biggestImpactNodeId);
|
|
94
94
|
|
|
95
95
|
// Turn root causes into sub-items.
|
|
@@ -105,15 +105,16 @@ class LayoutShifts extends Audit {
|
|
|
105
105
|
cause: str_(UIStrings.rootCauseUnsizedMedia),
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
|
-
for (const request of rootCauses.
|
|
108
|
+
for (const request of rootCauses.webFonts) {
|
|
109
109
|
const url = request.args.data.url;
|
|
110
110
|
subItems.push({
|
|
111
111
|
extra: {type: 'url', value: url},
|
|
112
112
|
cause: str_(UIStrings.rootCauseFontChanges),
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
-
|
|
115
|
+
for (const iframe of rootCauses.iframes) {
|
|
116
116
|
subItems.push({
|
|
117
|
+
extra: iframe.url ? {type: 'url', value: iframe.url} : undefined,
|
|
117
118
|
cause: str_(UIStrings.rootCauseInjectedIframe),
|
|
118
119
|
});
|
|
119
120
|
}
|
|
@@ -18,6 +18,16 @@ const UIStrings = {
|
|
|
18
18
|
columnFailingLink: 'Uncrawlable Link',
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
const hrefAssociatedAttributes = [
|
|
22
|
+
'target',
|
|
23
|
+
'download',
|
|
24
|
+
'ping',
|
|
25
|
+
'rel',
|
|
26
|
+
'hreflang',
|
|
27
|
+
'type',
|
|
28
|
+
'referrerpolicy',
|
|
29
|
+
];
|
|
30
|
+
|
|
21
31
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
22
32
|
|
|
23
33
|
class CrawlableAnchors extends Audit {
|
|
@@ -45,10 +55,14 @@ class CrawlableAnchors extends Audit {
|
|
|
45
55
|
role = '',
|
|
46
56
|
id,
|
|
47
57
|
href,
|
|
58
|
+
attributeNames = [],
|
|
59
|
+
listeners = [],
|
|
60
|
+
ancestorListeners = [],
|
|
48
61
|
}) => {
|
|
49
62
|
rawHref = rawHref.replace( /\s/g, '');
|
|
50
63
|
name = name.trim();
|
|
51
64
|
role = role.trim();
|
|
65
|
+
const hasListener = Boolean(listeners.length || ancestorListeners.length);
|
|
52
66
|
|
|
53
67
|
if (role.length > 0) return;
|
|
54
68
|
// Ignore mailto links even if they use one of the failing patterns. See https://github.com/GoogleChrome/lighthouse/issues/11443#issuecomment-694898412
|
|
@@ -62,6 +76,19 @@ class CrawlableAnchors extends Audit {
|
|
|
62
76
|
if (rawHref.startsWith('file:')) return true;
|
|
63
77
|
if (name.length > 0) return;
|
|
64
78
|
|
|
79
|
+
// If the a element has no href attribute, then the element represents a
|
|
80
|
+
// placeholder for where a link might otherwise have been placed, if it had
|
|
81
|
+
// been relevant, consisting of just the element's contents. The target,
|
|
82
|
+
// download, ping, rel, hreflang, type, and referrerpolicy attributes must be
|
|
83
|
+
// omitted if the href attribute is not present.
|
|
84
|
+
// See https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element
|
|
85
|
+
if (
|
|
86
|
+
!attributeNames.includes('href') &&
|
|
87
|
+
hrefAssociatedAttributes.every(attribute => !attributeNames.includes(attribute))
|
|
88
|
+
) {
|
|
89
|
+
return hasListener;
|
|
90
|
+
}
|
|
91
|
+
|
|
65
92
|
if (href === '') return true;
|
|
66
93
|
if (javaScriptVoidRegExp.test(rawHref)) return true;
|
|
67
94
|
|
|
@@ -8,95 +8,123 @@ import {Audit} from '../audit.js';
|
|
|
8
8
|
import UrlUtils from '../../lib/url-utils.js';
|
|
9
9
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/** @type {Record<string, Set<string>>} */
|
|
12
|
+
const nonDescriptiveLinkTexts = {
|
|
12
13
|
// English
|
|
13
|
-
'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
'en': new Set([
|
|
15
|
+
'click here',
|
|
16
|
+
'click this',
|
|
17
|
+
'go',
|
|
18
|
+
'here',
|
|
19
|
+
'information',
|
|
20
|
+
'learn more',
|
|
21
|
+
'more',
|
|
22
|
+
'more info',
|
|
23
|
+
'more information',
|
|
24
|
+
'right here',
|
|
25
|
+
'read more',
|
|
26
|
+
'see more',
|
|
27
|
+
'start',
|
|
28
|
+
'this',
|
|
29
|
+
]),
|
|
27
30
|
// Japanese
|
|
28
|
-
'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
'ja': new Set([
|
|
32
|
+
'ここをクリック',
|
|
33
|
+
'こちらをクリック',
|
|
34
|
+
'リンク',
|
|
35
|
+
'続きを読む',
|
|
36
|
+
'続く',
|
|
37
|
+
'全文表示',
|
|
38
|
+
]),
|
|
34
39
|
// Spanish
|
|
35
|
-
'
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
'es': new Set([
|
|
41
|
+
'click aquí',
|
|
42
|
+
'click aqui',
|
|
43
|
+
'clicka aquí',
|
|
44
|
+
'clicka aqui',
|
|
45
|
+
'pincha aquí',
|
|
46
|
+
'pincha aqui',
|
|
47
|
+
'aquí',
|
|
48
|
+
'aqui',
|
|
49
|
+
'más',
|
|
50
|
+
'mas',
|
|
51
|
+
'más información',
|
|
52
|
+
'más informacion',
|
|
53
|
+
'mas información',
|
|
54
|
+
'mas informacion',
|
|
55
|
+
'este',
|
|
56
|
+
'enlace',
|
|
57
|
+
'este enlace',
|
|
58
|
+
'empezar',
|
|
59
|
+
]),
|
|
53
60
|
// Portuguese
|
|
54
|
-
'
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
'pt': new Set([
|
|
62
|
+
'clique aqui',
|
|
63
|
+
'ir',
|
|
64
|
+
'mais informação',
|
|
65
|
+
'mais informações',
|
|
66
|
+
'mais',
|
|
67
|
+
'veja mais',
|
|
68
|
+
]),
|
|
60
69
|
// Korean
|
|
61
|
-
'
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
'ko': new Set([
|
|
71
|
+
'여기',
|
|
72
|
+
'여기를 클릭',
|
|
73
|
+
'클릭',
|
|
74
|
+
'링크',
|
|
75
|
+
'자세히',
|
|
76
|
+
'자세히 보기',
|
|
77
|
+
'계속',
|
|
78
|
+
'이동',
|
|
79
|
+
'전체 보기',
|
|
80
|
+
]),
|
|
70
81
|
// Swedish
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
'sv': new Set([
|
|
83
|
+
'här',
|
|
84
|
+
'klicka här',
|
|
85
|
+
'läs mer',
|
|
86
|
+
'mer',
|
|
87
|
+
'mer info',
|
|
88
|
+
'mer information',
|
|
89
|
+
]),
|
|
90
|
+
// German
|
|
91
|
+
'de': new Set([
|
|
92
|
+
'klicke hier',
|
|
93
|
+
'hier klicken',
|
|
94
|
+
'hier',
|
|
95
|
+
'mehr',
|
|
96
|
+
'siehe',
|
|
97
|
+
'dies',
|
|
98
|
+
'das',
|
|
99
|
+
'weiterlesen',
|
|
100
|
+
]),
|
|
77
101
|
// Tamil
|
|
78
|
-
'
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
'ta': new Set([
|
|
103
|
+
'அடுத்த பக்கம்',
|
|
104
|
+
'மறுபக்கம்',
|
|
105
|
+
'முந்தைய பக்கம்',
|
|
106
|
+
'முன்பக்கம்',
|
|
107
|
+
'மேலும் அறிக',
|
|
108
|
+
'மேலும் தகவலுக்கு',
|
|
109
|
+
'மேலும் தரவுகளுக்கு',
|
|
110
|
+
'தயவுசெய்து இங்கே அழுத்தவும்',
|
|
111
|
+
'இங்கே கிளிக் செய்யவும்',
|
|
112
|
+
]),
|
|
87
113
|
// Persian
|
|
88
|
-
'
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
114
|
+
'fa': new Set([
|
|
115
|
+
'اطلاعات بیشتر',
|
|
116
|
+
'اطلاعات',
|
|
117
|
+
'این',
|
|
118
|
+
'اینجا بزنید',
|
|
119
|
+
'اینجا کلیک کنید',
|
|
120
|
+
'اینجا',
|
|
121
|
+
'برو',
|
|
122
|
+
'بیشتر بخوانید',
|
|
123
|
+
'بیشتر بدانید',
|
|
124
|
+
'بیشتر',
|
|
125
|
+
'شروع',
|
|
126
|
+
]),
|
|
127
|
+
};
|
|
100
128
|
|
|
101
129
|
const UIStrings = {
|
|
102
130
|
/** Title of a Lighthouse audit that tests if each link on a page contains a sufficient description of what a user will find when they click it. Generic, non-descriptive text like "click here" doesn't give an indication of what the link leads to. This descriptive title is shown when all links on the page have sufficient textual descriptions. */
|
|
@@ -135,8 +163,9 @@ class LinkText extends Audit {
|
|
|
135
163
|
*/
|
|
136
164
|
static audit(artifacts) {
|
|
137
165
|
const failingLinks = artifacts.AnchorElements
|
|
138
|
-
.filter(link => link.href && !link.rel.includes('nofollow'))
|
|
139
166
|
.filter(link => {
|
|
167
|
+
if (!link.href || link.rel.includes('nofollow')) return false;
|
|
168
|
+
|
|
140
169
|
const href = link.href.toLowerCase();
|
|
141
170
|
if (
|
|
142
171
|
href.startsWith('javascript:') ||
|
|
@@ -148,12 +177,30 @@ class LinkText extends Audit {
|
|
|
148
177
|
return false;
|
|
149
178
|
}
|
|
150
179
|
|
|
151
|
-
|
|
180
|
+
const searchTerm = link.text.trim().toLowerCase();
|
|
181
|
+
if (searchTerm) {
|
|
182
|
+
// Use language if detected, otherwise look at everything.
|
|
183
|
+
if (link.textLang) {
|
|
184
|
+
const lang = link.textLang.split('-')[0];
|
|
185
|
+
if (nonDescriptiveLinkTexts[lang] && nonDescriptiveLinkTexts[lang].has(searchTerm)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
for (const texts of Object.values(nonDescriptiveLinkTexts)) {
|
|
190
|
+
if (texts.has(searchTerm)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return false;
|
|
152
198
|
})
|
|
153
199
|
.map(link => {
|
|
154
200
|
return {
|
|
155
201
|
href: link.href,
|
|
156
202
|
text: link.text.trim(),
|
|
203
|
+
textLang: link.textLang,
|
|
157
204
|
};
|
|
158
205
|
});
|
|
159
206
|
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import {Audit} from './audit.js';
|
|
12
12
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
13
13
|
|
|
14
|
-
/* eslint-disable max-len */
|
|
15
14
|
const UIStrings = {
|
|
16
15
|
/** Title of a Lighthouse audit that provides detail on the use of third party cookies. This descriptive title is shown to users when the page does not use third party cookies. */
|
|
17
16
|
title: 'Avoids third-party cookies',
|
|
@@ -25,7 +24,6 @@ const UIStrings = {
|
|
|
25
24
|
other {# cookies found}
|
|
26
25
|
}`,
|
|
27
26
|
};
|
|
28
|
-
/* eslint-enable max-len */
|
|
29
27
|
|
|
30
28
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
31
29
|
|
|
@@ -121,7 +121,6 @@ class ValidSourceMaps extends Audit {
|
|
|
121
121
|
|
|
122
122
|
/** @type {LH.Audit.Details.TableColumnHeading[]} */
|
|
123
123
|
const headings = [
|
|
124
|
-
/* eslint-disable max-len */
|
|
125
124
|
{
|
|
126
125
|
key: 'scriptUrl',
|
|
127
126
|
valueType: 'url',
|
|
@@ -129,7 +128,6 @@ class ValidSourceMaps extends Audit {
|
|
|
129
128
|
label: str_(i18n.UIStrings.columnURL),
|
|
130
129
|
},
|
|
131
130
|
{key: 'sourceMapUrl', valueType: 'url', label: str_(UIStrings.columnMapURL)},
|
|
132
|
-
/* eslint-enable max-len */
|
|
133
131
|
];
|
|
134
132
|
|
|
135
133
|
results.sort((a, b) => {
|
|
@@ -55,7 +55,6 @@ function computeGeneratedFileSizes(map, contentLength, content) {
|
|
|
55
55
|
let mappingLength = 0;
|
|
56
56
|
if (lastColNum !== undefined) {
|
|
57
57
|
if (lastColNum > line.length) {
|
|
58
|
-
// eslint-disable-next-line max-len
|
|
59
58
|
const errorMessage =
|
|
60
59
|
`${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`;
|
|
61
60
|
log.error('JSBundles', errorMessage);
|
|
@@ -159,7 +159,6 @@ class CumulativeLayoutShift {
|
|
|
159
159
|
LayoutShifts: TraceEngine.TraceHandlers.LayoutShifts,
|
|
160
160
|
Screenshots: TraceEngine.TraceHandlers.Screenshots,
|
|
161
161
|
});
|
|
162
|
-
// eslint-disable-next-line max-len
|
|
163
162
|
await processor.parse(/** @type {import('@paulirish/trace_engine').Types.Events.Event[]} */ (
|
|
164
163
|
events
|
|
165
164
|
), {});
|
|
@@ -212,6 +211,7 @@ class CumulativeLayoutShift {
|
|
|
212
211
|
throw new Error(`new trace engine differed. expected: ${expected}, got: ${got}`);
|
|
213
212
|
}
|
|
214
213
|
} catch (err) {
|
|
214
|
+
// eslint-disable-next-line no-console
|
|
215
215
|
console.error(err);
|
|
216
216
|
newEngineResultDiffered = true;
|
|
217
217
|
|
|
@@ -38,14 +38,15 @@ async function getComputationDataParamsFromTrace(data, context) {
|
|
|
38
38
|
|
|
39
39
|
const graph = await PageDependencyGraph.request({...data, fromTrace: true}, context);
|
|
40
40
|
const traceEngineResult = await TraceEngineResult.request(data, context);
|
|
41
|
-
const frameId = traceEngineResult.
|
|
42
|
-
const navigationId =
|
|
41
|
+
const frameId = traceEngineResult.parsedTrace.Meta.mainFrameId;
|
|
42
|
+
const navigationId =
|
|
43
|
+
traceEngineResult.parsedTrace.Meta.mainFrameNavigations[0].args.data?.navigationId;
|
|
43
44
|
if (!navigationId) {
|
|
44
45
|
throw new Error(`Lantern metrics could not be calculated due to missing navigation id`);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
const processedNavigation = Lantern.TraceEngineComputationData.createProcessedNavigation(
|
|
48
|
-
traceEngineResult.
|
|
49
|
+
traceEngineResult.parsedTrace, frameId, navigationId);
|
|
49
50
|
const simulator = data.simulator || (await LoadSimulator.request(data, context));
|
|
50
51
|
|
|
51
52
|
return {simulator, graph, processedNavigation};
|
|
@@ -21,6 +21,7 @@ import {TotalBlockingTime} from './total-blocking-time.js';
|
|
|
21
21
|
import {makeComputedArtifact} from '../computed-artifact.js';
|
|
22
22
|
import {TimeToFirstByte} from './time-to-first-byte.js';
|
|
23
23
|
import {LCPBreakdown} from './lcp-breakdown.js';
|
|
24
|
+
import {isUnderTest} from '../../lib/lh-env.js';
|
|
24
25
|
|
|
25
26
|
class TimingSummary {
|
|
26
27
|
/**
|
|
@@ -46,7 +47,9 @@ class TimingSummary {
|
|
|
46
47
|
*/
|
|
47
48
|
const requestOrUndefined = (Artifact, artifact) => {
|
|
48
49
|
return Artifact.request(artifact, context).catch(err => {
|
|
49
|
-
|
|
50
|
+
if (isUnderTest) {
|
|
51
|
+
log.error('lh:computed:TimingSummary', err);
|
|
52
|
+
}
|
|
50
53
|
return undefined;
|
|
51
54
|
});
|
|
52
55
|
};
|
|
@@ -27,11 +27,11 @@ class PageDependencyGraph {
|
|
|
27
27
|
if (data.fromTrace) {
|
|
28
28
|
const traceEngineResult =
|
|
29
29
|
await TraceEngineResult.request({trace, settings, SourceMaps}, context);
|
|
30
|
-
const
|
|
30
|
+
const parsedTrace = traceEngineResult.parsedTrace;
|
|
31
31
|
const requests =
|
|
32
|
-
Lantern.TraceEngineComputationData.createNetworkRequests(trace,
|
|
32
|
+
Lantern.TraceEngineComputationData.createNetworkRequests(trace, parsedTrace);
|
|
33
33
|
const graph =
|
|
34
|
-
Lantern.TraceEngineComputationData.createGraph(requests, trace,
|
|
34
|
+
Lantern.TraceEngineComputationData.createGraph(requests, trace, parsedTrace, URL);
|
|
35
35
|
// @ts-expect-error for now, ignore that this is a SyntheticNetworkEvent instead of LH's NetworkEvent.
|
|
36
36
|
return graph;
|
|
37
37
|
}
|
|
@@ -34,7 +34,6 @@ class TraceEngineResult {
|
|
|
34
34
|
lanternSettings.precomputedLanternData = settings.precomputedLanternData;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
// eslint-disable-next-line max-len
|
|
38
37
|
await processor.parse(/** @type {import('@paulirish/trace_engine').Types.Events.Event[]} */ (
|
|
39
38
|
traceEvents
|
|
40
39
|
), {
|
|
@@ -63,7 +62,7 @@ class TraceEngineResult {
|
|
|
63
62
|
if (!processor.parsedTrace) throw new Error('No data');
|
|
64
63
|
if (!processor.insights) throw new Error('No insights');
|
|
65
64
|
this.localizeInsights(processor.insights);
|
|
66
|
-
return {
|
|
65
|
+
return {parsedTrace: processor.parsedTrace, insights: processor.insights};
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
/**
|
package/core/config/config.js
CHANGED
|
@@ -10,7 +10,7 @@ import log from 'lighthouse-logger';
|
|
|
10
10
|
|
|
11
11
|
import {Runner} from '../runner.js';
|
|
12
12
|
import defaultConfig from './default-config.js';
|
|
13
|
-
import {nonSimulatedSettingsOverrides} from './constants.js';
|
|
13
|
+
import {nonSimulatedSettingsOverrides} from './constants.js';
|
|
14
14
|
import {
|
|
15
15
|
throwInvalidDependencyOrder,
|
|
16
16
|
isValidArtifactDependency,
|
|
@@ -319,9 +319,9 @@ const defaultConfig = {
|
|
|
319
319
|
'insights/font-display-insight',
|
|
320
320
|
'insights/forced-reflow-insight',
|
|
321
321
|
'insights/image-delivery-insight',
|
|
322
|
-
'insights/
|
|
322
|
+
'insights/inp-breakdown-insight',
|
|
323
|
+
'insights/lcp-breakdown-insight',
|
|
323
324
|
'insights/lcp-discovery-insight',
|
|
324
|
-
'insights/lcp-phases-insight',
|
|
325
325
|
'insights/legacy-javascript-insight',
|
|
326
326
|
'insights/modern-http-insight',
|
|
327
327
|
'insights/network-dependency-tree-insight',
|
|
@@ -421,9 +421,9 @@ const defaultConfig = {
|
|
|
421
421
|
{id: 'font-display-insight', weight: 0, group: 'hidden'},
|
|
422
422
|
{id: 'forced-reflow-insight', weight: 0, group: 'hidden'},
|
|
423
423
|
{id: 'image-delivery-insight', weight: 0, group: 'hidden'},
|
|
424
|
-
{id: '
|
|
424
|
+
{id: 'inp-breakdown-insight', weight: 0, group: 'hidden'},
|
|
425
|
+
{id: 'lcp-breakdown-insight', weight: 0, group: 'hidden'},
|
|
425
426
|
{id: 'lcp-discovery-insight', weight: 0, group: 'hidden'},
|
|
426
|
-
{id: 'lcp-phases-insight', weight: 0, group: 'hidden'},
|
|
427
427
|
{id: 'legacy-javascript-insight', weight: 0, group: 'hidden'},
|
|
428
428
|
{id: 'modern-http-insight', weight: 0, group: 'hidden'},
|
|
429
429
|
{id: 'network-dependency-tree-insight', weight: 0, group: 'hidden'},
|
|
@@ -35,9 +35,9 @@ const config = {
|
|
|
35
35
|
{id: 'font-display-insight', weight: 0, group: 'insights'},
|
|
36
36
|
{id: 'forced-reflow-insight', weight: 0, group: 'insights'},
|
|
37
37
|
{id: 'image-delivery-insight', weight: 0, group: 'insights'},
|
|
38
|
-
{id: '
|
|
38
|
+
{id: 'inp-breakdown-insight', weight: 0, group: 'insights'},
|
|
39
|
+
{id: 'lcp-breakdown-insight', weight: 0, group: 'insights'},
|
|
39
40
|
{id: 'lcp-discovery-insight', weight: 0, group: 'insights'},
|
|
40
|
-
{id: 'lcp-phases-insight', weight: 0, group: 'insights'},
|
|
41
41
|
{id: 'legacy-javascript-insight', weight: 0, group: 'insights'},
|
|
42
42
|
{id: 'modern-http-insight', weight: 0, group: 'insights'},
|
|
43
43
|
{id: 'network-dependency-tree-insight', weight: 0, group: 'insights'},
|