lighthouse 12.6.1-dev.20250610 → 12.6.1-dev.20250611
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/core/audits/audit.js +2 -2
- package/core/audits/insights/insight-audit.js +2 -1
- package/core/audits/seo/crawlable-anchors.js +27 -0
- package/core/config/filters.js +6 -0
- package/core/gather/gatherers/anchor-elements.js +20 -0
- package/package.json +3 -3
- package/readme.md +3 -2
- package/types/artifacts.d.ts +4 -0
package/core/audits/audit.js
CHANGED
|
@@ -164,7 +164,7 @@ class Audit {
|
|
|
164
164
|
if (results.length === 0) {
|
|
165
165
|
return {
|
|
166
166
|
type: 'table',
|
|
167
|
-
headings
|
|
167
|
+
headings,
|
|
168
168
|
items: [],
|
|
169
169
|
summary,
|
|
170
170
|
};
|
|
@@ -174,7 +174,7 @@ class Audit {
|
|
|
174
174
|
|
|
175
175
|
return {
|
|
176
176
|
type: 'table',
|
|
177
|
-
headings
|
|
177
|
+
headings,
|
|
178
178
|
items: results,
|
|
179
179
|
summary,
|
|
180
180
|
sortedBy,
|
|
@@ -68,10 +68,11 @@ async function adaptInsightToAuditProduct(artifacts, context, insightName, creat
|
|
|
68
68
|
parsedTrace,
|
|
69
69
|
insights,
|
|
70
70
|
});
|
|
71
|
-
if (!details || (details.type === 'table' && details.
|
|
71
|
+
if (!details || (details.type === 'table' && details.items.length === 0)) {
|
|
72
72
|
return {
|
|
73
73
|
scoreDisplayMode: Audit.SCORING_MODES.NOT_APPLICABLE,
|
|
74
74
|
score: null,
|
|
75
|
+
details,
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -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
|
|
package/core/config/filters.js
CHANGED
|
@@ -263,6 +263,12 @@ function filterConfigByGatherMode(resolvedConfig, mode) {
|
|
|
263
263
|
*/
|
|
264
264
|
function filterConfigByExplicitFilters(resolvedConfig, filters) {
|
|
265
265
|
const {onlyAudits, onlyCategories, skipAudits} = filters;
|
|
266
|
+
if (onlyAudits && !onlyAudits.length) {
|
|
267
|
+
throw new Error(`onlyAudits cannot be an empty array.`);
|
|
268
|
+
}
|
|
269
|
+
if (onlyCategories && !onlyCategories.length) {
|
|
270
|
+
throw new Error(`onlyCategories cannot be an empty array.`);
|
|
271
|
+
}
|
|
266
272
|
|
|
267
273
|
warnOnUnknownOnlyCategories(resolvedConfig.categories, onlyCategories);
|
|
268
274
|
|
|
@@ -94,6 +94,7 @@ function collectAnchorElements() {
|
|
|
94
94
|
rel: node.rel,
|
|
95
95
|
target: node.target,
|
|
96
96
|
id: node.getAttribute('id') || '',
|
|
97
|
+
attributeNames: node.getAttributeNames(),
|
|
97
98
|
// @ts-expect-error - getNodeDetails put into scope via stringification
|
|
98
99
|
node: getNodeDetails(node),
|
|
99
100
|
};
|
|
@@ -109,6 +110,7 @@ function collectAnchorElements() {
|
|
|
109
110
|
rel: '',
|
|
110
111
|
target: node.target.baseVal || '',
|
|
111
112
|
id: node.getAttribute('id') || '',
|
|
113
|
+
attributeNames: node.getAttributeNames(),
|
|
112
114
|
// @ts-expect-error - getNodeDetails put into scope via stringification
|
|
113
115
|
node: getNodeDetails(node),
|
|
114
116
|
};
|
|
@@ -160,9 +162,27 @@ class AnchorElements extends BaseGatherer {
|
|
|
160
162
|
const anchorsWithEventListeners = anchors.map(async anchor => {
|
|
161
163
|
const listeners = await getEventListeners(session, anchor.node.devtoolsNodePath);
|
|
162
164
|
|
|
165
|
+
/** @type {Set<{type: string}>} */
|
|
166
|
+
const ancestorListeners = new Set();
|
|
167
|
+
const splitPath = anchor.node.devtoolsNodePath.split(',');
|
|
168
|
+
const ancestorListenerPromises = [];
|
|
169
|
+
while (splitPath.length >= 2) {
|
|
170
|
+
splitPath.length -= 2;
|
|
171
|
+
const path = splitPath.join(',');
|
|
172
|
+
const promise = getEventListeners(session, path).then(listeners => {
|
|
173
|
+
for (const listener of listeners) {
|
|
174
|
+
ancestorListeners.add(listener);
|
|
175
|
+
}
|
|
176
|
+
}).catch(() => {});
|
|
177
|
+
ancestorListenerPromises.push(promise);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
await Promise.all(ancestorListenerPromises);
|
|
181
|
+
|
|
163
182
|
return {
|
|
164
183
|
...anchor,
|
|
165
184
|
listeners,
|
|
185
|
+
ancestorListeners: Array.from(ancestorListeners),
|
|
166
186
|
};
|
|
167
187
|
});
|
|
168
188
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "12.6.1-dev.
|
|
4
|
+
"version": "12.6.1-dev.20250611",
|
|
5
5
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
6
6
|
"main": "./core/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"@testing-library/preact-hooks": "^1.1.0",
|
|
112
112
|
"@types/archiver": "^2.1.2",
|
|
113
113
|
"@types/chrome": "^0.0.154",
|
|
114
|
-
"@types/configstore": "^
|
|
114
|
+
"@types/configstore": "^6.0.2",
|
|
115
115
|
"@types/cpy": "^5.1.0",
|
|
116
116
|
"@types/debug": "^4.1.7",
|
|
117
117
|
"@types/eslint": "^8.2.1",
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
"@sentry/node": "^7.0.0",
|
|
190
190
|
"axe-core": "^4.10.3",
|
|
191
191
|
"chrome-launcher": "^1.2.0",
|
|
192
|
-
"configstore": "^
|
|
192
|
+
"configstore": "^7.0.0",
|
|
193
193
|
"csp_evaluator": "1.1.5",
|
|
194
194
|
"devtools-protocol": "0.0.1467305",
|
|
195
195
|
"enquirer": "^2.3.6",
|
package/readme.md
CHANGED
|
@@ -52,7 +52,7 @@ The Chrome extension was available prior to Lighthouse being available in Chrome
|
|
|
52
52
|
|
|
53
53
|
The Node CLI provides the most flexibility in how Lighthouse runs can be configured and reported. Users who want more advanced usage, or want to run Lighthouse in an automated fashion should use the Node CLI.
|
|
54
54
|
|
|
55
|
-
>
|
|
55
|
+
> [!NOTE]
|
|
56
56
|
> Lighthouse requires Node 18.20 or later.
|
|
57
57
|
|
|
58
58
|
**Installation**:
|
|
@@ -227,7 +227,8 @@ top of any Lighthouse HTML report and open the report in the
|
|
|
227
227
|
In the Viewer, reports can be shared by clicking the share icon in the top
|
|
228
228
|
right corner and signing in to GitHub.
|
|
229
229
|
|
|
230
|
-
>
|
|
230
|
+
> [!NOTE]
|
|
231
|
+
> shared reports are stashed as a secret Gist in GitHub, under your account.
|
|
231
232
|
|
|
232
233
|
## Docs & Recipes
|
|
233
234
|
|
package/types/artifacts.d.ts
CHANGED
|
@@ -363,9 +363,13 @@ declare module Artifacts {
|
|
|
363
363
|
node: NodeDetails
|
|
364
364
|
onclick: string
|
|
365
365
|
id: string
|
|
366
|
+
attributeNames: Array<string>
|
|
366
367
|
listeners?: Array<{
|
|
367
368
|
type: Crdp.DOMDebugger.EventListener['type']
|
|
368
369
|
}>
|
|
370
|
+
ancestorListeners?: Array<{
|
|
371
|
+
type: Crdp.DOMDebugger.EventListener['type']
|
|
372
|
+
}>
|
|
369
373
|
}
|
|
370
374
|
|
|
371
375
|
type BFCacheReasonMap = {
|