lighthouse 13.4.1-dev.20260824 → 13.4.1-dev.20260831
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/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 +19 -0
- package/core/audits/agentic/ard-schema.js +135 -0
- package/core/audits/baseline.d.ts +10 -5
- package/core/config/agentic-browsing-config.d.ts +2 -0
- package/core/config/agentic-browsing-config.js +12 -0
- package/core/config/default-config.js +12 -0
- package/core/gather/driver/execution-context.js +0 -2
- package/core/gather/driver/wait-for-condition.js +0 -6
- package/core/gather/fetcher.d.ts +9 -2
- package/core/gather/fetcher.js +30 -4
- package/core/gather/gatherers/accessibility.js +0 -2
- package/core/gather/gatherers/agentic/ard.d.ts +30 -0
- package/core/gather/gatherers/agentic/ard.js +125 -0
- package/core/gather/gatherers/anchor-elements.js +0 -2
- package/core/gather/gatherers/full-page-screenshot.js +0 -4
- package/core/gather/gatherers/iframe-elements.js +0 -2
- package/core/gather/gatherers/image-elements.js +0 -12
- package/core/gather/gatherers/inputs.js +0 -2
- package/core/gather/gatherers/link-elements.js +0 -2
- package/core/gather/gatherers/meta-elements.js +0 -3
- package/core/gather/gatherers/seo/robots-txt.d.ts +1 -0
- package/core/gather/gatherers/seo/robots-txt.js +3 -0
- package/core/gather/gatherers/stacks.js +0 -2
- package/core/gather/gatherers/trace-elements.js +0 -2
- package/core/gather/gatherers/viewport-dimensions.js +0 -2
- package/core/gather/session.js +1 -0
- package/core/lib/baseline/web-features-data.json +26 -13
- package/core/lib/baseline/web-features-metadata.json +1 -1
- package/core/lib/page-functions.d.ts +0 -3
- package/core/lib/page-functions.js +0 -3
- package/core/lib/rect-helpers.js +0 -8
- package/package.json +16 -11
- package/shared/localization/locales/en-US.json +99 -66
- package/shared/localization/locales/en-XL.json +99 -66
- package/third-party/ard/LICENSE +201 -0
- package/third-party/ard/README.md +27 -0
- package/third-party/ard/ard-test.d.ts +2 -0
- package/third-party/ard/ard-test.js +358 -0
- package/third-party/ard/ard.d.ts +34 -0
- package/third-party/ard/ard.js +204 -0
- package/third-party/ard/schema-validator.d.ts +20 -0
- package/third-party/ard/schema-validator.js +1700 -0
- package/third-party/ard/spec/schemas/ai-catalog.schema.json +292 -0
- package/tsconfig.json +1 -0
- package/types/artifacts.d.ts +14 -0
package/core/gather/fetcher.js
CHANGED
|
@@ -10,7 +10,7 @@ import * as LH from '../../types/lh.js';
|
|
|
10
10
|
* ignoring normal browser constraints such as CORS.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
/** @typedef {{content: string|null, status: number|null}} FetchResponse */
|
|
13
|
+
/** @typedef {{content: string|null, status: number|null, headers?: Record<string, string>|null}} FetchResponse */
|
|
14
14
|
|
|
15
15
|
class Fetcher {
|
|
16
16
|
/**
|
|
@@ -37,6 +37,26 @@ class Fetcher {
|
|
|
37
37
|
return this._fetchResourceOverProtocol(url, options);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @param {Headers|Record<string, string|number|boolean>|null|undefined} rawHeaders
|
|
42
|
+
* @return {Record<string, string>|null}
|
|
43
|
+
*/
|
|
44
|
+
_normalizeHeaders(rawHeaders) {
|
|
45
|
+
if (!rawHeaders) return null;
|
|
46
|
+
|
|
47
|
+
/** @type {Record<string, string>} */
|
|
48
|
+
const headers = {};
|
|
49
|
+
const entries = typeof /** @type {Headers} */ (rawHeaders).entries === 'function' ?
|
|
50
|
+
/** @type {Headers} */ (rawHeaders).entries() :
|
|
51
|
+
Object.entries(rawHeaders);
|
|
52
|
+
|
|
53
|
+
for (const [key, value] of entries) {
|
|
54
|
+
headers[key.toLowerCase()] = String(value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return headers;
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
/**
|
|
41
61
|
* @param {string} url
|
|
42
62
|
* @return {Promise<FetchResponse>}
|
|
@@ -49,9 +69,12 @@ class Fetcher {
|
|
|
49
69
|
content = await response.text();
|
|
50
70
|
} catch {}
|
|
51
71
|
|
|
72
|
+
const headers = this._normalizeHeaders(response.headers);
|
|
73
|
+
|
|
52
74
|
return {
|
|
53
75
|
content,
|
|
54
76
|
status: response.status,
|
|
77
|
+
headers,
|
|
55
78
|
};
|
|
56
79
|
}
|
|
57
80
|
|
|
@@ -82,7 +105,7 @@ class Fetcher {
|
|
|
82
105
|
|
|
83
106
|
/**
|
|
84
107
|
* @param {string} url
|
|
85
|
-
* @return {Promise<{stream: LH.Crdp.IO.StreamHandle|null, status: number|null}>}
|
|
108
|
+
* @return {Promise<{stream: LH.Crdp.IO.StreamHandle|null, status: number|null, headers?: Record<string, string>|null}>}
|
|
86
109
|
*/
|
|
87
110
|
async _loadNetworkResource(url) {
|
|
88
111
|
const frameTreeResponse = await this.session.sendCommand('Page.getFrameTree');
|
|
@@ -98,6 +121,7 @@ class Fetcher {
|
|
|
98
121
|
return {
|
|
99
122
|
stream: networkResponse.resource.success ? (networkResponse.resource.stream || null) : null,
|
|
100
123
|
status: networkResponse.resource.httpStatusCode || null,
|
|
124
|
+
headers: networkResponse.resource.headers || null,
|
|
101
125
|
};
|
|
102
126
|
}
|
|
103
127
|
|
|
@@ -110,12 +134,14 @@ class Fetcher {
|
|
|
110
134
|
const startTime = Date.now();
|
|
111
135
|
const response = await this._wrapWithTimeout(this._loadNetworkResource(url), options.timeout);
|
|
112
136
|
|
|
137
|
+
const headers = this._normalizeHeaders(response.headers);
|
|
138
|
+
|
|
113
139
|
const isOk = response.status && response.status >= 200 && response.status <= 299;
|
|
114
|
-
if (!response.stream || !isOk) return {status: response.status, content: null};
|
|
140
|
+
if (!response.stream || !isOk) return {status: response.status, content: null, headers};
|
|
115
141
|
|
|
116
142
|
const timeout = options.timeout - (Date.now() - startTime);
|
|
117
143
|
const content = await this._readIOStream(response.stream, {timeout});
|
|
118
|
-
return {status: response.status, content};
|
|
144
|
+
return {status: response.status, content, headers};
|
|
119
145
|
}
|
|
120
146
|
|
|
121
147
|
/**
|
|
@@ -13,7 +13,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
|
|
|
13
13
|
/**
|
|
14
14
|
* @return {Promise<LH.Artifacts.Accessibility>}
|
|
15
15
|
*/
|
|
16
|
-
/* c8 ignore start */
|
|
17
16
|
async function runA11yChecks() {
|
|
18
17
|
/** @type {typeof import('axe-core/axe')} */
|
|
19
18
|
// @ts-expect-error - axe defined by axeLibSource
|
|
@@ -183,7 +182,6 @@ function createAxeRuleResultArtifact(result) {
|
|
|
183
182
|
description: result.description,
|
|
184
183
|
};
|
|
185
184
|
}
|
|
186
|
-
/* c8 ignore stop */
|
|
187
185
|
|
|
188
186
|
class Accessibility extends BaseGatherer {
|
|
189
187
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export default AgentResourceDiscovery;
|
|
2
|
+
declare class AgentResourceDiscovery extends BaseGatherer {
|
|
3
|
+
/**
|
|
4
|
+
* @param {LH.Artifacts['RobotsTxt']|null|undefined} robotsTxt
|
|
5
|
+
* @param {string} finalDisplayedUrl
|
|
6
|
+
* @return {string|null}
|
|
7
|
+
*/
|
|
8
|
+
static getRobotsTxtAgentmap(robotsTxt: LH.Artifacts["RobotsTxt"] | null | undefined, finalDisplayedUrl: string): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* @param {LH.Gatherer.Context} context
|
|
11
|
+
* @param {string} finalDisplayedUrl
|
|
12
|
+
* @return {Promise<string|null>}
|
|
13
|
+
*/
|
|
14
|
+
static getHtmlLinkFromDom(context: LH.Gatherer.Context, finalDisplayedUrl: string): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* @param {LH.Gatherer.Context} context
|
|
17
|
+
* @param {string} finalDisplayedUrl
|
|
18
|
+
* @return {Promise<string|null>}
|
|
19
|
+
*/
|
|
20
|
+
static getHttpHeaderLink(context: LH.Gatherer.Context, finalDisplayedUrl: string): Promise<string | null>;
|
|
21
|
+
/** @type {LH.Gatherer.GathererMeta<'RobotsTxt'>} */
|
|
22
|
+
meta: LH.Gatherer.GathererMeta<"RobotsTxt">;
|
|
23
|
+
/**
|
|
24
|
+
* @param {LH.Gatherer.Context<'RobotsTxt'>} context
|
|
25
|
+
* @return {Promise<LH.Artifacts['AgentResourceDiscovery']>}
|
|
26
|
+
*/
|
|
27
|
+
getArtifact(context: LH.Gatherer.Context<"RobotsTxt">): Promise<LH.Artifacts["AgentResourceDiscovery"]>;
|
|
28
|
+
}
|
|
29
|
+
import BaseGatherer from '../../base-gatherer.js';
|
|
30
|
+
//# sourceMappingURL=ard.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import log from 'lighthouse-logger';
|
|
8
|
+
import LinkHeader from 'http-link-header';
|
|
9
|
+
|
|
10
|
+
import BaseGatherer from '../../base-gatherer.js';
|
|
11
|
+
import RobotsTxt from '../seo/robots-txt.js';
|
|
12
|
+
import {pageFunctions} from '../../../lib/page-functions.js';
|
|
13
|
+
function getAiCatalogLinkInDOM() {
|
|
14
|
+
const link = document.querySelector('link[rel~="ai-catalog" i]');
|
|
15
|
+
return link instanceof HTMLLinkElement ? link.href : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class AgentResourceDiscovery extends BaseGatherer {
|
|
19
|
+
/** @type {LH.Gatherer.GathererMeta<'RobotsTxt'>} */
|
|
20
|
+
meta = {
|
|
21
|
+
supportedModes: ['snapshot', 'navigation'],
|
|
22
|
+
dependencies: {RobotsTxt: RobotsTxt.symbol},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {LH.Artifacts['RobotsTxt']|null|undefined} robotsTxt
|
|
27
|
+
* @param {string} finalDisplayedUrl
|
|
28
|
+
* @return {string|null}
|
|
29
|
+
*/
|
|
30
|
+
static getRobotsTxtAgentmap(robotsTxt, finalDisplayedUrl) {
|
|
31
|
+
if (!robotsTxt?.content) return null;
|
|
32
|
+
const match = robotsTxt.content.match(/^\s*Agentmap:\s*(\S+)/im);
|
|
33
|
+
if (!match) return null;
|
|
34
|
+
try {
|
|
35
|
+
return new URL(match[1], finalDisplayedUrl).href;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {LH.Gatherer.Context} context
|
|
43
|
+
* @param {string} finalDisplayedUrl
|
|
44
|
+
* @return {Promise<string|null>}
|
|
45
|
+
*/
|
|
46
|
+
static async getHtmlLinkFromDom(context, finalDisplayedUrl) {
|
|
47
|
+
try {
|
|
48
|
+
const href = await context.driver.executionContext.evaluate(getAiCatalogLinkInDOM, {
|
|
49
|
+
args: [],
|
|
50
|
+
useIsolation: true,
|
|
51
|
+
deps: [pageFunctions.getNodeDetails],
|
|
52
|
+
});
|
|
53
|
+
if (!href) return null;
|
|
54
|
+
return new URL(href, finalDisplayedUrl).href;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {LH.Gatherer.Context} context
|
|
62
|
+
* @param {string} finalDisplayedUrl
|
|
63
|
+
* @return {Promise<string|null>}
|
|
64
|
+
*/
|
|
65
|
+
static async getHttpHeaderLink(context, finalDisplayedUrl) {
|
|
66
|
+
// Only check in navigation mode
|
|
67
|
+
if (context.gatherMode !== 'navigation') return null;
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const mainResourceResponse = await context.driver.fetcher.fetchResource(finalDisplayedUrl);
|
|
71
|
+
const linkHeader = mainResourceResponse.headers?.['link'];
|
|
72
|
+
if (!linkHeader) return null;
|
|
73
|
+
|
|
74
|
+
const parsed = LinkHeader.parse(linkHeader);
|
|
75
|
+
const aiCatalogRef = parsed.get('rel', 'ai-catalog')[0];
|
|
76
|
+
if (!aiCatalogRef?.uri) return null;
|
|
77
|
+
|
|
78
|
+
return new URL(aiCatalogRef.uri, finalDisplayedUrl).href;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {LH.Gatherer.Context<'RobotsTxt'>} context
|
|
86
|
+
* @return {Promise<LH.Artifacts['AgentResourceDiscovery']>}
|
|
87
|
+
*/
|
|
88
|
+
async getArtifact(context) {
|
|
89
|
+
const {finalDisplayedUrl} = context.baseArtifacts.URL;
|
|
90
|
+
const robotsTxt = context.dependencies.RobotsTxt;
|
|
91
|
+
const robotsTxtAgentmap = AgentResourceDiscovery.getRobotsTxtAgentmap(
|
|
92
|
+
robotsTxt, finalDisplayedUrl);
|
|
93
|
+
const [htmlLink, httpHeaderLink] = await Promise.all([
|
|
94
|
+
AgentResourceDiscovery.getHtmlLinkFromDom(context, finalDisplayedUrl),
|
|
95
|
+
AgentResourceDiscovery.getHttpHeaderLink(context, finalDisplayedUrl),
|
|
96
|
+
]);
|
|
97
|
+
const wellKnown = new URL('/.well-known/ai-catalog.json', finalDisplayedUrl).href;
|
|
98
|
+
const catalogUrl = robotsTxtAgentmap || htmlLink || httpHeaderLink || wellKnown;
|
|
99
|
+
|
|
100
|
+
/** @type {string|undefined} */
|
|
101
|
+
let errorMessage;
|
|
102
|
+
const fetchResult = await context.driver.fetcher.fetchResource(catalogUrl)
|
|
103
|
+
.catch(err => {
|
|
104
|
+
log.error('AgentResourceDiscovery', err);
|
|
105
|
+
errorMessage = err.message;
|
|
106
|
+
return {status: null, content: null, headers: null};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
status: fetchResult.status,
|
|
111
|
+
content: fetchResult.content,
|
|
112
|
+
headers: fetchResult.headers || null,
|
|
113
|
+
catalogUrl,
|
|
114
|
+
discoverySignals: {
|
|
115
|
+
robotsTxtAgentmap,
|
|
116
|
+
htmlLink,
|
|
117
|
+
httpHeaderLink,
|
|
118
|
+
wellKnown,
|
|
119
|
+
},
|
|
120
|
+
errorMessage,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default AgentResourceDiscovery;
|
|
@@ -19,7 +19,6 @@ import {resolveDevtoolsNodePathToObjectId} from '../driver/dom.js';
|
|
|
19
19
|
*
|
|
20
20
|
* @return {LH.Artifacts['AnchorElements']}
|
|
21
21
|
*/
|
|
22
|
-
/* c8 ignore start */
|
|
23
22
|
function collectAnchorElements() {
|
|
24
23
|
/** @param {string} url */
|
|
25
24
|
const resolveURLOrEmpty = url => {
|
|
@@ -114,7 +113,6 @@ function collectAnchorElements() {
|
|
|
114
113
|
};
|
|
115
114
|
});
|
|
116
115
|
}
|
|
117
|
-
/* c8 ignore stop */
|
|
118
116
|
|
|
119
117
|
/**
|
|
120
118
|
* @param {LH.Gatherer.ProtocolSession} session
|
|
@@ -31,8 +31,6 @@ function kebabCaseToCamelCase(str) {
|
|
|
31
31
|
);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/* c8 ignore start */
|
|
35
|
-
|
|
36
34
|
function getObservedDeviceMetrics() {
|
|
37
35
|
// Convert the Web API's kebab case (landscape-primary) to camel case (landscapePrimary).
|
|
38
36
|
const screenOrientationType = kebabCaseToCamelCase(window.screen.orientation.type);
|
|
@@ -53,8 +51,6 @@ function waitForDoubleRaf() {
|
|
|
53
51
|
});
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
/* c8 ignore stop */
|
|
57
|
-
|
|
58
54
|
class FullPageScreenshot extends BaseGatherer {
|
|
59
55
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
60
56
|
meta = {
|
|
@@ -14,7 +14,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
|
|
|
14
14
|
/**
|
|
15
15
|
* @return {LH.Artifacts['IFrameElements']}
|
|
16
16
|
*/
|
|
17
|
-
/* c8 ignore start */
|
|
18
17
|
function collectIFrameElements() {
|
|
19
18
|
const realBoundingClientRect = window.__HTMLElementBoundingClientRect ||
|
|
20
19
|
window.HTMLElement.prototype.getBoundingClientRect;
|
|
@@ -35,7 +34,6 @@ function collectIFrameElements() {
|
|
|
35
34
|
};
|
|
36
35
|
});
|
|
37
36
|
}
|
|
38
|
-
/* c8 ignore stop */
|
|
39
37
|
|
|
40
38
|
class IFrameElements extends BaseGatherer {
|
|
41
39
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
@@ -17,7 +17,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
|
|
|
17
17
|
/* global getElementsInDocument, getNodeDetails */
|
|
18
18
|
|
|
19
19
|
/** @param {Element} element */
|
|
20
|
-
/* c8 ignore start */
|
|
21
20
|
function getClientRect(element) {
|
|
22
21
|
const clientRect = element.getBoundingClientRect();
|
|
23
22
|
return {
|
|
@@ -28,7 +27,6 @@ function getClientRect(element) {
|
|
|
28
27
|
right: clientRect.right,
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
|
-
/* c8 ignore stop */
|
|
32
30
|
|
|
33
31
|
/**
|
|
34
32
|
* If an image is within `picture`, the `picture` element's css position
|
|
@@ -36,7 +34,6 @@ function getClientRect(element) {
|
|
|
36
34
|
* @param {Element} element
|
|
37
35
|
* @param {CSSStyleDeclaration} computedStyle
|
|
38
36
|
*/
|
|
39
|
-
/* c8 ignore start */
|
|
40
37
|
function getPosition(element, computedStyle) {
|
|
41
38
|
if (element.parentElement && element.parentElement.tagName === 'PICTURE') {
|
|
42
39
|
const parentStyle = window.getComputedStyle(element.parentElement);
|
|
@@ -44,13 +41,11 @@ function getPosition(element, computedStyle) {
|
|
|
44
41
|
}
|
|
45
42
|
return computedStyle.getPropertyValue('position');
|
|
46
43
|
}
|
|
47
|
-
/* c8 ignore stop */
|
|
48
44
|
|
|
49
45
|
/**
|
|
50
46
|
* @param {Array<Element>} allElements
|
|
51
47
|
* @return {Array<LH.Artifacts.ImageElement>}
|
|
52
48
|
*/
|
|
53
|
-
/* c8 ignore start */
|
|
54
49
|
function getHTMLImages(allElements) {
|
|
55
50
|
const allImageElements = /** @type {Array<HTMLImageElement>} */ (allElements.filter(element => {
|
|
56
51
|
return element.localName === 'img';
|
|
@@ -89,13 +84,11 @@ function getHTMLImages(allElements) {
|
|
|
89
84
|
};
|
|
90
85
|
});
|
|
91
86
|
}
|
|
92
|
-
/* c8 ignore stop */
|
|
93
87
|
|
|
94
88
|
/**
|
|
95
89
|
* @param {Array<Element>} allElements
|
|
96
90
|
* @return {Array<LH.Artifacts.ImageElement>}
|
|
97
91
|
*/
|
|
98
|
-
/* c8 ignore start */
|
|
99
92
|
function getCSSImages(allElements) {
|
|
100
93
|
// Chrome normalizes background image style from getComputedStyle to be an absolute URL in quotes.
|
|
101
94
|
// Only match basic background-image: url("http://host/image.jpeg") declarations
|
|
@@ -138,23 +131,19 @@ function getCSSImages(allElements) {
|
|
|
138
131
|
|
|
139
132
|
return images;
|
|
140
133
|
}
|
|
141
|
-
/* c8 ignore stop */
|
|
142
134
|
|
|
143
135
|
/** @return {Array<LH.Artifacts.ImageElement>} */
|
|
144
|
-
/* c8 ignore start */
|
|
145
136
|
function collectImageElementInfo() {
|
|
146
137
|
/** @type {Array<Element>} */
|
|
147
138
|
// @ts-expect-error - added by getElementsInDocumentFnString
|
|
148
139
|
const allElements = getElementsInDocument();
|
|
149
140
|
return getHTMLImages(allElements).concat(getCSSImages(allElements));
|
|
150
141
|
}
|
|
151
|
-
/* c8 ignore stop */
|
|
152
142
|
|
|
153
143
|
/**
|
|
154
144
|
* @param {string} url
|
|
155
145
|
* @return {Promise<{naturalWidth: number, naturalHeight: number}>}
|
|
156
146
|
*/
|
|
157
|
-
/* c8 ignore start */
|
|
158
147
|
function determineNaturalSize(url) {
|
|
159
148
|
return new Promise((resolve, reject) => {
|
|
160
149
|
const img = new Image();
|
|
@@ -169,7 +158,6 @@ function determineNaturalSize(url) {
|
|
|
169
158
|
img.src = url;
|
|
170
159
|
});
|
|
171
160
|
}
|
|
172
|
-
/* c8 ignore stop */
|
|
173
161
|
|
|
174
162
|
/**
|
|
175
163
|
* @param {Partial<Pick<LH.Crdp.CSS.CSSStyle, 'cssProperties'>>|undefined} rule
|
|
@@ -14,7 +14,6 @@ import {pageFunctions} from '../../lib/page-functions.js';
|
|
|
14
14
|
/**
|
|
15
15
|
* @return {LH.Artifacts['Inputs']}
|
|
16
16
|
*/
|
|
17
|
-
/* c8 ignore start */
|
|
18
17
|
function collectElements() {
|
|
19
18
|
/** @type {LH.Artifacts.InputElement[]} */
|
|
20
19
|
const inputArtifacts = [];
|
|
@@ -93,7 +92,6 @@ function collectElements() {
|
|
|
93
92
|
labels: [...labelElToArtifact.values()],
|
|
94
93
|
};
|
|
95
94
|
}
|
|
96
|
-
/* c8 ignore stop */
|
|
97
95
|
|
|
98
96
|
class Inputs extends BaseGatherer {
|
|
99
97
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
@@ -59,7 +59,6 @@ function getCrossoriginFromHeader(value) {
|
|
|
59
59
|
/**
|
|
60
60
|
* @return {LH.Artifacts['LinkElements']}
|
|
61
61
|
*/
|
|
62
|
-
/* c8 ignore start */
|
|
63
62
|
function getLinkElementsInDOM() {
|
|
64
63
|
/** @type {Array<HTMLOrSVGElement>} */
|
|
65
64
|
// @ts-expect-error - getElementsInDocument put into scope via stringification
|
|
@@ -91,7 +90,6 @@ function getLinkElementsInDOM() {
|
|
|
91
90
|
|
|
92
91
|
return linkElements;
|
|
93
92
|
}
|
|
94
|
-
/* c8 ignore stop */
|
|
95
93
|
|
|
96
94
|
class LinkElements extends BaseGatherer {
|
|
97
95
|
/** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
|
|
@@ -8,8 +8,6 @@ import BaseGatherer from '../base-gatherer.js';
|
|
|
8
8
|
import {pageFunctions} from '../../lib/page-functions.js';
|
|
9
9
|
|
|
10
10
|
/* globals getElementsInDocument getNodeDetails */
|
|
11
|
-
|
|
12
|
-
/* c8 ignore start */
|
|
13
11
|
function collectMetaElements() {
|
|
14
12
|
const functions = /** @type {typeof pageFunctions} */({
|
|
15
13
|
// @ts-expect-error - getElementsInDocument put into scope via stringification
|
|
@@ -36,7 +34,6 @@ function collectMetaElements() {
|
|
|
36
34
|
};
|
|
37
35
|
});
|
|
38
36
|
}
|
|
39
|
-
/* c8 ignore stop */
|
|
40
37
|
|
|
41
38
|
class MetaElements extends BaseGatherer {
|
|
42
39
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
@@ -9,8 +9,11 @@ import log from 'lighthouse-logger';
|
|
|
9
9
|
import BaseGatherer from '../../base-gatherer.js';
|
|
10
10
|
|
|
11
11
|
class RobotsTxt extends BaseGatherer {
|
|
12
|
+
static symbol = Symbol('RobotsTxt');
|
|
13
|
+
|
|
12
14
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
13
15
|
meta = {
|
|
16
|
+
symbol: RobotsTxt.symbol,
|
|
14
17
|
supportedModes: ['snapshot', 'navigation'],
|
|
15
18
|
};
|
|
16
19
|
|
|
@@ -46,7 +46,6 @@ const libDetectorSource = fs.readFileSync(
|
|
|
46
46
|
/**
|
|
47
47
|
* Obtains a list of detected JS libraries and their versions.
|
|
48
48
|
*/
|
|
49
|
-
/* c8 ignore start */
|
|
50
49
|
async function detectLibraries() {
|
|
51
50
|
/** @type {JSLibrary[]} */
|
|
52
51
|
const libraries = [];
|
|
@@ -81,7 +80,6 @@ async function detectLibraries() {
|
|
|
81
80
|
|
|
82
81
|
return libraries;
|
|
83
82
|
}
|
|
84
|
-
/* c8 ignore stop */
|
|
85
83
|
|
|
86
84
|
|
|
87
85
|
/** @implements {LH.Gatherer.GathererInstance} */
|
|
@@ -30,7 +30,6 @@ const MAX_LAYOUT_SHIFTS = 15;
|
|
|
30
30
|
/**
|
|
31
31
|
* @param {Element | ShadowRoot | Text} node
|
|
32
32
|
*/
|
|
33
|
-
/* c8 ignore start */
|
|
34
33
|
function getNodeDetailsData(node) {
|
|
35
34
|
/** @type {Element | ShadowRoot | Text | null} */
|
|
36
35
|
let elem = node.nodeType === document.ELEMENT_NODE ? node : node.parentElement;
|
|
@@ -45,7 +44,6 @@ function getNodeDetailsData(node) {
|
|
|
45
44
|
}
|
|
46
45
|
return traceElement;
|
|
47
46
|
}
|
|
48
|
-
/* c8 ignore stop */
|
|
49
47
|
|
|
50
48
|
class TraceElements extends BaseGatherer {
|
|
51
49
|
/** @type {LH.Gatherer.GathererMeta<'Trace'|'SourceMaps'>} */
|
|
@@ -9,7 +9,6 @@ import BaseGatherer from '../base-gatherer.js';
|
|
|
9
9
|
/**
|
|
10
10
|
* @return {LH.Artifacts.ViewportDimensions}
|
|
11
11
|
*/
|
|
12
|
-
/* c8 ignore start */
|
|
13
12
|
function getViewportDimensions() {
|
|
14
13
|
// window.innerWidth to get the scrollable size of the window (irrespective of zoom)
|
|
15
14
|
// window.outerWidth to get the size of the visible area
|
|
@@ -22,7 +21,6 @@ function getViewportDimensions() {
|
|
|
22
21
|
devicePixelRatio: window.devicePixelRatio,
|
|
23
22
|
};
|
|
24
23
|
}
|
|
25
|
-
/* c8 ignore stop */
|
|
26
24
|
|
|
27
25
|
class ViewportDimensions extends BaseGatherer {
|
|
28
26
|
/** @type {LH.Gatherer.GathererMeta} */
|
package/core/gather/session.js
CHANGED
|
@@ -123,6 +123,7 @@ class ProtocolSession extends CrdpEventEmitter {
|
|
|
123
123
|
}));
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
+
// @ts-ignore-error CdpSession.send expects a method key of ProtocolMapping.Commands, but we use LH.CrdpCommands
|
|
126
127
|
const resultPromise = this._cdpSession.send(method, ...params, {
|
|
127
128
|
// Add 50ms to the Puppeteer timeout to ensure the Lighthouse timeout finishes first.
|
|
128
129
|
timeout: timeoutMs + PPTR_BUFFER,
|