lighthouse 11.7.0-dev.20240410 → 11.7.0-dev.20240412
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/byte-efficiency/render-blocking-resources.js +25 -26
- package/core/audits/layout-shifts.js +1 -1
- package/core/computed/metrics/lantern-first-contentful-paint.js +3 -2
- package/core/computed/metrics/lantern-first-meaningful-paint.js +3 -2
- package/core/computed/metrics/lantern-interactive.js +3 -2
- package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -2
- package/core/computed/metrics/lantern-max-potential-fid.js +3 -2
- package/core/computed/metrics/lantern-metric.d.ts +5 -0
- package/core/computed/metrics/lantern-metric.js +23 -1
- package/core/computed/metrics/lantern-speed-index.js +3 -2
- package/core/computed/navigation-insights.d.ts +36 -0
- package/core/computed/navigation-insights.js +35 -0
- package/core/computed/trace-engine-result.d.ts +16 -3
- package/core/computed/trace-engine-result.js +19 -14
- package/core/config/default-config.js +0 -2
- package/core/config/filters.js +0 -1
- package/core/gather/gatherers/root-causes.d.ts +2 -2
- package/core/gather/gatherers/root-causes.js +6 -5
- package/core/gather/gatherers/trace-elements.d.ts +2 -2
- package/core/gather/gatherers/trace-elements.js +2 -2
- package/core/lib/lantern/cpu-node.d.ts +7 -1
- package/core/lib/lantern/cpu-node.js +12 -2
- package/core/lib/lantern/lantern-error.d.ts +8 -0
- package/core/lib/lantern/lantern-error.js +9 -0
- package/core/lib/lantern/metrics/first-meaningful-paint.js +2 -4
- package/core/lib/lantern/metrics/interactive.js +1 -1
- package/core/lib/lantern/metrics/largest-contentful-paint.js +3 -3
- package/core/lib/lantern/page-dependency-graph.js +14 -2
- package/core/lib/lantern/simulator/simulator.js +1 -1
- package/core/lib/lh-error.js +3 -3
- package/core/runner.js +0 -1
- package/package.json +1 -1
- package/report/renderer/report-renderer.js +1 -1
- package/tsconfig.json +0 -2
- package/types/artifacts.d.ts +15 -65
- package/types/lhr/lhr.d.ts +0 -2
- package/core/gather/driver/service-workers.d.ts +0 -16
- package/core/gather/driver/service-workers.js +0 -52
- package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
- package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
- package/core/gather/gatherers/service-worker.d.ts +0 -10
- package/core/gather/gatherers/service-worker.js +0 -32
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* @fileoverview
|
|
8
|
-
* Identifies stylesheets, HTML Imports, and scripts that potentially block
|
|
9
|
-
* the first paint of the page by running several scripts in the page context.
|
|
10
|
-
* Candidate blocking tags are collected by querying for all script tags in
|
|
11
|
-
* the head of the page and all link tags that are either matching media
|
|
12
|
-
* stylesheets or non-async HTML imports. These are then compared to the
|
|
13
|
-
* network requests to ensure they were initiated by the parser and not
|
|
14
|
-
* injected with script. To avoid false positives from strategies like
|
|
15
|
-
* (http://filamentgroup.github.io/loadCSS/test/preload.html), a separate
|
|
16
|
-
* script is run to flag all links that at one point were rel=preload.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import {NetworkRecords} from '../../../computed/network-records.js';
|
|
21
|
-
import DevtoolsLog from '../devtools-log.js';
|
|
22
|
-
import BaseGatherer from '../../base-gatherer.js';
|
|
23
|
-
|
|
24
|
-
/* global document, window, HTMLLinkElement, SVGScriptElement */
|
|
25
|
-
|
|
26
|
-
/** @typedef {{href: string, media: string, msSinceHTMLEnd: number, matches: boolean}} MediaChange */
|
|
27
|
-
/** @typedef {{tagName: 'LINK', url: string, href: string, rel: string, media: string, disabled: boolean, mediaChanges: Array<MediaChange>}} LinkTag */
|
|
28
|
-
/** @typedef {{tagName: 'SCRIPT', url: string, src: string}} ScriptTag */
|
|
29
|
-
|
|
30
|
-
/* c8 ignore start */
|
|
31
|
-
function installMediaListener() {
|
|
32
|
-
// @ts-expect-error - inserted in page to track media changes.
|
|
33
|
-
window.___linkMediaChanges = [];
|
|
34
|
-
Object.defineProperty(HTMLLinkElement.prototype, 'media', {
|
|
35
|
-
set: function(val) {
|
|
36
|
-
/** @type {MediaChange} */
|
|
37
|
-
const mediaChange = {
|
|
38
|
-
href: this.href,
|
|
39
|
-
media: val,
|
|
40
|
-
msSinceHTMLEnd: Date.now() - performance.timing.responseEnd,
|
|
41
|
-
matches: window.matchMedia(val).matches,
|
|
42
|
-
};
|
|
43
|
-
// @ts-expect-error - `___linkMediaChanges` created above.
|
|
44
|
-
window.___linkMediaChanges.push(mediaChange);
|
|
45
|
-
|
|
46
|
-
this.setAttribute('media', val);
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
/* c8 ignore stop */
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @return {Promise<Array<LinkTag | ScriptTag>>}
|
|
54
|
-
*/
|
|
55
|
-
/* c8 ignore start */
|
|
56
|
-
async function collectTagsThatBlockFirstPaint() {
|
|
57
|
-
/** @type {Array<MediaChange>} */
|
|
58
|
-
// @ts-expect-error - `___linkMediaChanges` created in `installMediaListener`.
|
|
59
|
-
const linkMediaChanges = window.___linkMediaChanges;
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
/** @type {Array<LinkTag>} */
|
|
63
|
-
const linkTags = [...document.querySelectorAll('link')]
|
|
64
|
-
.filter(linkTag => {
|
|
65
|
-
// Ignore malformed links with no href (e.g. `<link rel="stylesheet" href="">`)
|
|
66
|
-
// The resolved `linkTag.href` will be the main document, but the main document
|
|
67
|
-
// should never be render blocking.
|
|
68
|
-
if (!linkTag.getAttribute('href')) return false;
|
|
69
|
-
|
|
70
|
-
// Filter stylesheet/HTML imports that block rendering.
|
|
71
|
-
// https://www.igvita.com/2012/06/14/debunking-responsive-css-performance-myths/
|
|
72
|
-
// https://www.w3.org/TR/html-imports/#dfn-import-async-attribute
|
|
73
|
-
const blockingStylesheet = linkTag.rel === 'stylesheet' &&
|
|
74
|
-
window.matchMedia(linkTag.media).matches && !linkTag.disabled;
|
|
75
|
-
const blockingImport = linkTag.rel === 'import' && !linkTag.hasAttribute('async');
|
|
76
|
-
return blockingStylesheet || blockingImport;
|
|
77
|
-
})
|
|
78
|
-
.map(tag => {
|
|
79
|
-
return {
|
|
80
|
-
tagName: 'LINK',
|
|
81
|
-
url: tag.href,
|
|
82
|
-
href: tag.href,
|
|
83
|
-
rel: tag.rel,
|
|
84
|
-
media: tag.media,
|
|
85
|
-
disabled: tag.disabled,
|
|
86
|
-
mediaChanges: linkMediaChanges.filter(item => item.href === tag.href),
|
|
87
|
-
};
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
/** @type {Array<ScriptTag>} */
|
|
91
|
-
const scriptTags = [...document.querySelectorAll('head script[src]')]
|
|
92
|
-
.filter(/** @return {scriptTag is HTMLScriptElement} */ scriptTag => {
|
|
93
|
-
// SVGScriptElement can't appear in <head> (it'll be kicked to <body>), but keep tsc happy.
|
|
94
|
-
// https://html.spec.whatwg.org/multipage/semantics.html#the-head-element
|
|
95
|
-
if (scriptTag instanceof SVGScriptElement) return false;
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
!scriptTag.hasAttribute('async') &&
|
|
99
|
-
!scriptTag.hasAttribute('defer') &&
|
|
100
|
-
!/^data:/.test(scriptTag.src) &&
|
|
101
|
-
!/^blob:/.test(scriptTag.src) &&
|
|
102
|
-
scriptTag.getAttribute('type') !== 'module'
|
|
103
|
-
);
|
|
104
|
-
})
|
|
105
|
-
.map(tag => {
|
|
106
|
-
return {
|
|
107
|
-
tagName: 'SCRIPT',
|
|
108
|
-
url: tag.src,
|
|
109
|
-
src: tag.src,
|
|
110
|
-
};
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
return [...linkTags, ...scriptTags];
|
|
114
|
-
} catch (e) {
|
|
115
|
-
const friendly = 'Unable to gather Scripts/Stylesheets/HTML Imports on the page';
|
|
116
|
-
throw new Error(`${friendly}: ${e.message}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
/* c8 ignore stop */
|
|
120
|
-
|
|
121
|
-
class TagsBlockingFirstPaint extends BaseGatherer {
|
|
122
|
-
/** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
|
|
123
|
-
meta = {
|
|
124
|
-
supportedModes: ['navigation'],
|
|
125
|
-
dependencies: {DevtoolsLog: DevtoolsLog.symbol},
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
130
|
-
* @return {Map<string, LH.Artifacts.NetworkRequest>}
|
|
131
|
-
*/
|
|
132
|
-
static _filteredAndIndexedByUrl(networkRecords) {
|
|
133
|
-
/** @type {Map<string, LH.Artifacts.NetworkRequest>} */
|
|
134
|
-
const result = new Map();
|
|
135
|
-
|
|
136
|
-
for (const record of networkRecords) {
|
|
137
|
-
if (!record.finished) continue;
|
|
138
|
-
|
|
139
|
-
const isParserGenerated = record.initiator.type === 'parser';
|
|
140
|
-
// A stylesheet only blocks script if it was initiated by the parser
|
|
141
|
-
// https://html.spec.whatwg.org/multipage/semantics.html#interactions-of-styling-and-scripting
|
|
142
|
-
const isParserScriptOrStyle = /(css|script)/.test(record.mimeType) && isParserGenerated;
|
|
143
|
-
const isFailedRequest = record.failed;
|
|
144
|
-
const isHtml = record.mimeType && record.mimeType.includes('html');
|
|
145
|
-
|
|
146
|
-
// Filter stylesheet, javascript, and html import mimetypes.
|
|
147
|
-
// Include 404 scripts/links generated by the parser because they are likely blocking.
|
|
148
|
-
if (isHtml || isParserScriptOrStyle || (isFailedRequest && isParserGenerated)) {
|
|
149
|
-
result.set(record.url, record);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return result;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* @param {LH.Gatherer.Driver} driver
|
|
158
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
159
|
-
* @return {Promise<Array<LH.Artifacts.TagBlockingFirstPaint>>}
|
|
160
|
-
*/
|
|
161
|
-
static async findBlockingTags(driver, networkRecords) {
|
|
162
|
-
const firstRequestEndTime = networkRecords.reduce(
|
|
163
|
-
(min, record) => Math.min(min, record.networkEndTime),
|
|
164
|
-
Infinity
|
|
165
|
-
);
|
|
166
|
-
const tags = await driver.executionContext.evaluate(collectTagsThatBlockFirstPaint, {args: []});
|
|
167
|
-
const requests = TagsBlockingFirstPaint._filteredAndIndexedByUrl(networkRecords);
|
|
168
|
-
|
|
169
|
-
/** @type {Array<LH.Artifacts.TagBlockingFirstPaint>} */
|
|
170
|
-
const result = [];
|
|
171
|
-
for (const tag of tags) {
|
|
172
|
-
const request = requests.get(tag.url);
|
|
173
|
-
if (!request || request.isLinkPreload) continue;
|
|
174
|
-
|
|
175
|
-
let endTime = request.networkEndTime;
|
|
176
|
-
let mediaChanges;
|
|
177
|
-
|
|
178
|
-
if (tag.tagName === 'LINK') {
|
|
179
|
-
// Even if the request was initially blocking or appeared to be blocking once the
|
|
180
|
-
// page was loaded, the media attribute could have been changed during load, capping the
|
|
181
|
-
// amount of time it was render blocking. See https://github.com/GoogleChrome/lighthouse/issues/2832.
|
|
182
|
-
const timesResourceBecameNonBlocking = tag.mediaChanges
|
|
183
|
-
.filter(change => !change.matches)
|
|
184
|
-
.map(change => change.msSinceHTMLEnd);
|
|
185
|
-
if (timesResourceBecameNonBlocking.length > 0) {
|
|
186
|
-
const earliestNonBlockingTime = Math.min(...timesResourceBecameNonBlocking);
|
|
187
|
-
const lastTimeResourceWasBlocking = Math.max(
|
|
188
|
-
request.networkRequestTime,
|
|
189
|
-
firstRequestEndTime + earliestNonBlockingTime / 1000
|
|
190
|
-
);
|
|
191
|
-
endTime = Math.min(endTime, lastTimeResourceWasBlocking);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
mediaChanges = tag.mediaChanges;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const {tagName, url} = tag;
|
|
198
|
-
|
|
199
|
-
result.push({
|
|
200
|
-
tag: {tagName, url, mediaChanges},
|
|
201
|
-
transferSize: request.transferSize,
|
|
202
|
-
startTime: request.networkRequestTime,
|
|
203
|
-
endTime,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Prevent duplicates from showing up again
|
|
207
|
-
requests.delete(tag.url);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return result;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* @param {LH.Gatherer.Context} context
|
|
215
|
-
*/
|
|
216
|
-
async startSensitiveInstrumentation(context) {
|
|
217
|
-
const {executionContext} = context.driver;
|
|
218
|
-
// Don't return return value of `evaluateOnNewDocument`.
|
|
219
|
-
await executionContext.evaluateOnNewDocument(installMediaListener, {args: []});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* @param {LH.Gatherer.Context<'DevtoolsLog'>} context
|
|
224
|
-
* @return {Promise<LH.Artifacts['TagsBlockingFirstPaint']>}
|
|
225
|
-
*/
|
|
226
|
-
async getArtifact(context) {
|
|
227
|
-
const devtoolsLog = context.dependencies.DevtoolsLog;
|
|
228
|
-
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
229
|
-
return TagsBlockingFirstPaint.findBlockingTags(context.driver, networkRecords);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export default TagsBlockingFirstPaint;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export default ServiceWorker;
|
|
2
|
-
declare class ServiceWorker extends BaseGatherer {
|
|
3
|
-
/**
|
|
4
|
-
* @param {LH.Gatherer.Context} context
|
|
5
|
-
* @return {Promise<LH.Artifacts['ServiceWorker']>}
|
|
6
|
-
*/
|
|
7
|
-
getArtifact(context: LH.Gatherer.Context): Promise<LH.Artifacts['ServiceWorker']>;
|
|
8
|
-
}
|
|
9
|
-
import BaseGatherer from '../base-gatherer.js';
|
|
10
|
-
//# sourceMappingURL=service-worker.d.ts.map
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import BaseGatherer from '../base-gatherer.js';
|
|
8
|
-
import * as serviceWorkers from '../driver/service-workers.js';
|
|
9
|
-
|
|
10
|
-
class ServiceWorker extends BaseGatherer {
|
|
11
|
-
/** @type {LH.Gatherer.GathererMeta} */
|
|
12
|
-
meta = {
|
|
13
|
-
supportedModes: ['navigation'],
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @param {LH.Gatherer.Context} context
|
|
18
|
-
* @return {Promise<LH.Artifacts['ServiceWorker']>}
|
|
19
|
-
*/
|
|
20
|
-
async getArtifact(context) {
|
|
21
|
-
const session = context.driver.defaultSession;
|
|
22
|
-
const {versions} = await serviceWorkers.getServiceWorkerVersions(session);
|
|
23
|
-
const {registrations} = await serviceWorkers.getServiceWorkerRegistrations(session);
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
versions,
|
|
27
|
-
registrations,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default ServiceWorker;
|