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.
Files changed (42) hide show
  1. package/core/audits/byte-efficiency/render-blocking-resources.js +25 -26
  2. package/core/audits/layout-shifts.js +1 -1
  3. package/core/computed/metrics/lantern-first-contentful-paint.js +3 -2
  4. package/core/computed/metrics/lantern-first-meaningful-paint.js +3 -2
  5. package/core/computed/metrics/lantern-interactive.js +3 -2
  6. package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -2
  7. package/core/computed/metrics/lantern-max-potential-fid.js +3 -2
  8. package/core/computed/metrics/lantern-metric.d.ts +5 -0
  9. package/core/computed/metrics/lantern-metric.js +23 -1
  10. package/core/computed/metrics/lantern-speed-index.js +3 -2
  11. package/core/computed/navigation-insights.d.ts +36 -0
  12. package/core/computed/navigation-insights.js +35 -0
  13. package/core/computed/trace-engine-result.d.ts +16 -3
  14. package/core/computed/trace-engine-result.js +19 -14
  15. package/core/config/default-config.js +0 -2
  16. package/core/config/filters.js +0 -1
  17. package/core/gather/gatherers/root-causes.d.ts +2 -2
  18. package/core/gather/gatherers/root-causes.js +6 -5
  19. package/core/gather/gatherers/trace-elements.d.ts +2 -2
  20. package/core/gather/gatherers/trace-elements.js +2 -2
  21. package/core/lib/lantern/cpu-node.d.ts +7 -1
  22. package/core/lib/lantern/cpu-node.js +12 -2
  23. package/core/lib/lantern/lantern-error.d.ts +8 -0
  24. package/core/lib/lantern/lantern-error.js +9 -0
  25. package/core/lib/lantern/metrics/first-meaningful-paint.js +2 -4
  26. package/core/lib/lantern/metrics/interactive.js +1 -1
  27. package/core/lib/lantern/metrics/largest-contentful-paint.js +3 -3
  28. package/core/lib/lantern/page-dependency-graph.js +14 -2
  29. package/core/lib/lantern/simulator/simulator.js +1 -1
  30. package/core/lib/lh-error.js +3 -3
  31. package/core/runner.js +0 -1
  32. package/package.json +1 -1
  33. package/report/renderer/report-renderer.js +1 -1
  34. package/tsconfig.json +0 -2
  35. package/types/artifacts.d.ts +15 -65
  36. package/types/lhr/lhr.d.ts +0 -2
  37. package/core/gather/driver/service-workers.d.ts +0 -16
  38. package/core/gather/driver/service-workers.js +0 -52
  39. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
  40. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
  41. package/core/gather/gatherers/service-worker.d.ts +0 -10
  42. 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;