lighthouse 13.4.1 → 13.5.0
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/build-tracker.config.js +1 -1
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +3 -3
- package/cli/test/smokehouse/lighthouse-runners/devtools-mcp.js +2 -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 +19 -0
- package/core/audits/agentic/ard-schema.js +135 -0
- package/core/audits/baseline.d.ts +23 -10
- package/core/audits/deprecations.js +1 -2
- package/core/audits/webmcp-form-coverage.js +3 -1
- package/core/audits/webmcp-registered-tools.d.ts +2 -0
- package/core/audits/webmcp-registered-tools.js +16 -1
- package/core/audits/webmcp-schema-validity.js +1 -1
- package/core/computed/metrics/interactive.js +1 -1
- package/core/computed/metrics/lantern-metric.js +1 -1
- package/core/computed/trace-engine-result.d.ts +3 -3
- package/core/computed/trace-engine-result.js +4 -4
- package/core/computed/unused-javascript-summary.d.ts +0 -1
- package/core/computed/unused-javascript-summary.js +0 -1
- package/core/config/agentic-browsing-config.d.ts +2 -0
- package/core/config/agentic-browsing-config.js +13 -1
- package/core/config/default-config.js +13 -1
- package/core/config/validation.js +6 -0
- package/core/gather/driver/execution-context.js +0 -2
- package/core/gather/driver/navigation.d.ts +1 -0
- package/core/gather/driver/navigation.js +35 -7
- package/core/gather/driver/wait-for-condition.d.ts +5 -7
- package/core/gather/driver/wait-for-condition.js +15 -9
- package/core/gather/fetcher.d.ts +9 -2
- package/core/gather/fetcher.js +30 -4
- package/core/gather/gatherers/accessibility.js +1 -3
- 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 +1 -13
- 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.d.ts +1 -1
- package/core/gather/gatherers/stacks.js +1 -3
- package/core/gather/gatherers/trace-elements.js +1 -3
- package/core/gather/gatherers/viewport-dimensions.js +0 -2
- package/core/gather/navigation-runner.d.ts +3 -1
- package/core/gather/navigation-runner.js +6 -3
- package/core/gather/session.js +1 -0
- package/core/index.cjs +5 -5
- package/core/index.d.cts +5 -5
- package/core/lib/arbitrary-equality-map.d.ts +2 -2
- package/core/lib/arbitrary-equality-map.js +1 -1
- package/core/lib/baseline/web-features-data.json +52 -28
- package/core/lib/baseline/web-features-metadata.json +1 -1
- package/core/lib/deprecation-description.js +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/core/runner.js +21 -0
- package/core/user-flow.d.ts +3 -0
- package/core/user-flow.js +12 -0
- package/dist/report/bundle.esm.js +10 -3
- package/dist/report/flow.js +20 -13
- package/dist/report/standalone.js +10 -3
- package/package.json +19 -14
- package/report/assets/styles.css +7 -0
- package/report/renderer/components.js +1 -1
- package/report/renderer/details-renderer.js +7 -5
- package/report/renderer/drop-down-menu.d.ts +2 -2
- package/report/renderer/drop-down-menu.js +1 -1
- package/report/renderer/report-renderer.js +24 -3
- package/report/renderer/report-utils.js +1 -1
- package/report/renderer/snippet-renderer.d.ts +4 -4
- package/report/renderer/snippet-renderer.js +3 -3
- package/report/renderer/text-encoding.js +2 -2
- package/shared/localization/locales/ar-XB.json +111 -72
- package/shared/localization/locales/ar.json +113 -74
- package/shared/localization/locales/bg.json +112 -73
- package/shared/localization/locales/ca.json +112 -73
- package/shared/localization/locales/cs.json +112 -73
- package/shared/localization/locales/da.json +112 -73
- package/shared/localization/locales/de.json +112 -73
- package/shared/localization/locales/el.json +112 -73
- package/shared/localization/locales/en-GB.json +111 -72
- package/shared/localization/locales/en-US.json +102 -66
- package/shared/localization/locales/en-XA.json +111 -72
- package/shared/localization/locales/en-XL.json +102 -66
- package/shared/localization/locales/es-419.json +112 -73
- package/shared/localization/locales/es.json +112 -73
- package/shared/localization/locales/fi.json +112 -73
- package/shared/localization/locales/fil.json +112 -73
- package/shared/localization/locales/fr.json +112 -73
- package/shared/localization/locales/he.json +112 -73
- package/shared/localization/locales/hi.json +112 -73
- package/shared/localization/locales/hr.json +112 -73
- package/shared/localization/locales/hu.json +112 -73
- package/shared/localization/locales/id.json +112 -73
- package/shared/localization/locales/it.json +113 -74
- package/shared/localization/locales/ja.json +112 -73
- package/shared/localization/locales/ko.json +112 -73
- package/shared/localization/locales/lt.json +112 -73
- package/shared/localization/locales/lv.json +112 -73
- package/shared/localization/locales/nl.json +112 -73
- package/shared/localization/locales/no.json +112 -73
- package/shared/localization/locales/pl.json +112 -73
- package/shared/localization/locales/pt-PT.json +112 -73
- package/shared/localization/locales/pt.json +112 -73
- package/shared/localization/locales/ro.json +112 -73
- package/shared/localization/locales/ru.json +112 -73
- package/shared/localization/locales/sk.json +112 -73
- package/shared/localization/locales/sl.json +112 -73
- package/shared/localization/locales/sr-Latn.json +112 -73
- package/shared/localization/locales/sr.json +112 -73
- package/shared/localization/locales/sv.json +124 -85
- package/shared/localization/locales/ta.json +112 -73
- package/shared/localization/locales/te.json +112 -73
- package/shared/localization/locales/th.json +112 -73
- package/shared/localization/locales/tr.json +113 -74
- package/shared/localization/locales/uk.json +112 -73
- package/shared/localization/locales/vi.json +112 -73
- package/shared/localization/locales/zh-HK.json +112 -73
- package/shared/localization/locales/zh-TW.json +113 -74
- package/shared/localization/locales/zh.json +113 -74
- package/shared/util.d.ts +6 -0
- package/shared/util.js +9 -0
- 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
|
@@ -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
|
|
@@ -189,7 +177,7 @@ function findSizeDeclaration(rule, property) {
|
|
|
189
177
|
* Finds the most specific directly matched CSS font-size rule from the list.
|
|
190
178
|
*
|
|
191
179
|
* @param {Array<LH.Crdp.CSS.RuleMatch>} matchedCSSRules
|
|
192
|
-
* @param {
|
|
180
|
+
* @param {(style: LH.Crdp.CSS.CSSStyle) => boolean|string|undefined} isDeclarationOfInterest
|
|
193
181
|
*/
|
|
194
182
|
function findMostSpecificMatchedCSSRule(matchedCSSRules = [], isDeclarationOfInterest) {
|
|
195
183
|
let mostSpecificRule;
|
|
@@ -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
|
|
|
@@ -13,7 +13,7 @@ export type JSLibraryDetectorTest = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Returns false if library is not present, otherwise returns an object that contains the library version (set to null if the version is not detected).
|
|
15
15
|
*/
|
|
16
|
-
test: (
|
|
16
|
+
test: (w: Window) => JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>;
|
|
17
17
|
};
|
|
18
18
|
export type JSLibrary = {
|
|
19
19
|
id: string;
|
|
@@ -32,7 +32,7 @@ const libDetectorSource = fs.readFileSync(
|
|
|
32
32
|
* @property {string} icon
|
|
33
33
|
* @property {string} url
|
|
34
34
|
* @property {string|null} npm npm module name, if applicable to library.
|
|
35
|
-
* @property {
|
|
35
|
+
* @property {(w: Window) => JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>} test Returns false if library is not present, otherwise returns an object that contains the library version (set to null if the version is not detected).
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -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'>} */
|
|
@@ -98,7 +96,7 @@ class TraceElements extends BaseGatherer {
|
|
|
98
96
|
* @param {Set<object>} seen
|
|
99
97
|
*/
|
|
100
98
|
function recursiveObjectEnumerate(obj, cb, seen) {
|
|
101
|
-
if (seen.has(
|
|
99
|
+
if (seen.has(obj)) {
|
|
102
100
|
return;
|
|
103
101
|
}
|
|
104
102
|
|
|
@@ -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} */
|
|
@@ -5,17 +5,19 @@ export type NavigationContext = {
|
|
|
5
5
|
requestor: LH.NavigationRequestor;
|
|
6
6
|
baseArtifacts: LH.BaseArtifacts;
|
|
7
7
|
computedCache: Map<string, LH.ArbitraryEqualityMap>;
|
|
8
|
+
signal?: AbortSignal | undefined;
|
|
8
9
|
};
|
|
9
10
|
export type PhaseState = Omit<Parameters<typeof collectPhaseArtifacts>[0], "phase">;
|
|
10
11
|
/**
|
|
11
12
|
* @param {LH.Puppeteer.Page|undefined} page
|
|
12
13
|
* @param {LH.NavigationRequestor|undefined} requestor
|
|
13
|
-
* @param {{config?: LH.Config, flags?: LH.Flags}} [options]
|
|
14
|
+
* @param {{config?: LH.Config, flags?: LH.Flags, signal?: AbortSignal }} [options]
|
|
14
15
|
* @return {Promise<LH.Gatherer.GatherResult>}
|
|
15
16
|
*/
|
|
16
17
|
export function navigationGather(page: LH.Puppeteer.Page | undefined, requestor: LH.NavigationRequestor | undefined, options?: {
|
|
17
18
|
config?: LH.Config;
|
|
18
19
|
flags?: LH.Flags;
|
|
20
|
+
signal?: AbortSignal;
|
|
19
21
|
}): Promise<LH.Gatherer.GatherResult>;
|
|
20
22
|
/**
|
|
21
23
|
* @param {{driver: Driver, resolvedConfig: LH.Config.ResolvedConfig, requestor: LH.NavigationRequestor}} args
|
|
@@ -32,6 +32,7 @@ import {NetworkRecords} from '../computed/network-records.js';
|
|
|
32
32
|
* @property {LH.NavigationRequestor} requestor
|
|
33
33
|
* @property {LH.BaseArtifacts} baseArtifacts
|
|
34
34
|
* @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
|
|
35
|
+
* @property {AbortSignal} [signal]
|
|
35
36
|
*/
|
|
36
37
|
|
|
37
38
|
/** @typedef {Omit<Parameters<typeof collectPhaseArtifacts>[0], 'phase'>} PhaseState */
|
|
@@ -79,12 +80,13 @@ async function _cleanupNavigation({driver}) {
|
|
|
79
80
|
* @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined}>}
|
|
80
81
|
*/
|
|
81
82
|
async function _navigate(navigationContext) {
|
|
82
|
-
const {driver, resolvedConfig, requestor} = navigationContext;
|
|
83
|
+
const {driver, resolvedConfig, requestor, signal} = navigationContext;
|
|
83
84
|
|
|
84
85
|
try {
|
|
85
86
|
const {requestedUrl, mainDocumentUrl, warnings} = await gotoURL(driver, requestor, {
|
|
86
87
|
...resolvedConfig.settings,
|
|
87
88
|
waitUntil: resolvedConfig.settings.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
|
|
89
|
+
signal,
|
|
88
90
|
});
|
|
89
91
|
|
|
90
92
|
navigationContext.baseArtifacts.LighthouseRunWarnings.push(...warnings);
|
|
@@ -251,11 +253,11 @@ async function _cleanup({requestedUrl, driver, resolvedConfig, lhBrowser, lhPage
|
|
|
251
253
|
/**
|
|
252
254
|
* @param {LH.Puppeteer.Page|undefined} page
|
|
253
255
|
* @param {LH.NavigationRequestor|undefined} requestor
|
|
254
|
-
* @param {{config?: LH.Config, flags?: LH.Flags}} [options]
|
|
256
|
+
* @param {{config?: LH.Config, flags?: LH.Flags, signal?: AbortSignal }} [options]
|
|
255
257
|
* @return {Promise<LH.Gatherer.GatherResult>}
|
|
256
258
|
*/
|
|
257
259
|
async function navigationGather(page, requestor, options = {}) {
|
|
258
|
-
const {flags = {}, config} = options;
|
|
260
|
+
const {flags = {}, config, signal} = options;
|
|
259
261
|
log.setLevel(flags.logLevel || 'error');
|
|
260
262
|
|
|
261
263
|
const {resolvedConfig} = await initializeConfig('navigation', config, flags);
|
|
@@ -291,6 +293,7 @@ async function navigationGather(page, requestor, options = {}) {
|
|
|
291
293
|
resolvedConfig,
|
|
292
294
|
requestor: normalizedRequestor,
|
|
293
295
|
computedCache,
|
|
296
|
+
signal,
|
|
294
297
|
};
|
|
295
298
|
const {baseArtifacts} = await _setup(context);
|
|
296
299
|
|
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,
|
package/core/index.cjs
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @typedef ExportType
|
|
9
|
-
* @property {import('./index.js')['startFlow']} startFlow
|
|
10
|
-
* @property {import('./index.js')['navigation']} navigation
|
|
11
|
-
* @property {import('./index.js')['startTimespan']} startTimespan
|
|
12
|
-
* @property {import('./index.js')['snapshot']} snapshot
|
|
9
|
+
* @property {typeof import('./index.js')['startFlow']} startFlow
|
|
10
|
+
* @property {typeof import('./index.js')['navigation']} navigation
|
|
11
|
+
* @property {typeof import('./index.js')['startTimespan']} startTimespan
|
|
12
|
+
* @property {typeof import('./index.js')['snapshot']} snapshot
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
/** @type {import('./index.js')['default'] & ExportType} */
|
|
15
|
+
/** @type {typeof import('./index.js')['default'] & ExportType} */
|
|
16
16
|
const lighthouse = async function lighthouse(...args) {
|
|
17
17
|
const {default: lighthouse} = await import('./index.js');
|
|
18
18
|
return lighthouse(...args);
|
package/core/index.d.cts
CHANGED
|
@@ -6,11 +6,11 @@ export = lighthouse;
|
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
8
|
* @typedef ExportType
|
|
9
|
-
* @property {import('./index.js')['startFlow']} startFlow
|
|
10
|
-
* @property {import('./index.js')['navigation']} navigation
|
|
11
|
-
* @property {import('./index.js')['startTimespan']} startTimespan
|
|
12
|
-
* @property {import('./index.js')['snapshot']} snapshot
|
|
9
|
+
* @property {typeof import('./index.js')['startFlow']} startFlow
|
|
10
|
+
* @property {typeof import('./index.js')['navigation']} navigation
|
|
11
|
+
* @property {typeof import('./index.js')['startTimespan']} startTimespan
|
|
12
|
+
* @property {typeof import('./index.js')['snapshot']} snapshot
|
|
13
13
|
*/
|
|
14
|
-
/** @type {import('./index.js')['default'] & ExportType} */
|
|
14
|
+
/** @type {typeof import('./index.js')['default'] & ExportType} */
|
|
15
15
|
declare const lighthouse: typeof import("./index.js")["default"] & ExportType;
|
|
16
16
|
//# sourceMappingURL=index.d.cts.map
|
|
@@ -20,9 +20,9 @@ export class ArbitraryEqualityMap {
|
|
|
20
20
|
value: any;
|
|
21
21
|
}>;
|
|
22
22
|
/**
|
|
23
|
-
* @param {
|
|
23
|
+
* @param {(a: any, b: any) => boolean} equalsFn
|
|
24
24
|
*/
|
|
25
|
-
setEqualityFn(equalsFn: (
|
|
25
|
+
setEqualityFn(equalsFn: (a: any, b: any) => boolean): void;
|
|
26
26
|
/**
|
|
27
27
|
* @param {*} key
|
|
28
28
|
* @return {boolean}
|