lighthouse 8.6.0-dev.20211031 → 8.6.0-dev.20211101
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/dist/report/flow.js +7 -7
- package/dist/report/standalone.js +7 -7
- package/flow-report/src/app.tsx +10 -11
- package/flow-report/src/common.tsx +11 -5
- package/flow-report/src/header.tsx +3 -3
- package/flow-report/src/sidebar/flow.tsx +3 -3
- package/flow-report/src/sidebar/sidebar.tsx +3 -3
- package/flow-report/src/summary/category.tsx +2 -2
- package/flow-report/src/util.ts +12 -12
- package/flow-report/src/wrappers/report.tsx +11 -6
- package/flow-report/test/common-test.tsx +25 -10
- package/flow-report/test/header-test.tsx +6 -6
- package/flow-report/test/summary/category-test.tsx +5 -5
- package/flow-report/test/util-test.tsx +32 -11
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +4 -2
- package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +3 -4
- package/lighthouse-core/computed/metrics/tbt-utils.js +57 -0
- package/lighthouse-core/computed/metrics/total-blocking-time.js +3 -53
- package/lighthouse-core/config/config-helpers.js +20 -10
- package/lighthouse-core/config/default-config.js +59 -53
- package/lighthouse-core/fraggle-rock/config/default-config.js +1 -1
- package/lighthouse-core/fraggle-rock/gather/base-gatherer.js +6 -1
- package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +1 -0
- package/lighthouse-core/lib/asset-saver.js +4 -1
- package/lighthouse-core/lib/lh-env.js +2 -0
- package/lighthouse-core/lib/tappable-rects.js +8 -15
- package/lighthouse-core/runner.js +1 -1
- package/lighthouse-core/util-commonjs.js +1 -2
- package/package.json +9 -6
- package/report/clients/standalone.js +0 -2
- package/report/renderer/element-screenshot-renderer.js +2 -2
- package/report/renderer/performance-category-renderer.js +20 -5
- package/report/renderer/util.js +1 -2
- package/report/test/renderer/performance-category-renderer-test.js +34 -8
- package/report/test/renderer/report-renderer-test.js +1 -1
- package/report/test/renderer/report-ui-features-test.js +3 -0
- package/report/test/renderer/util-test.js +13 -31
- package/shared/localization/format.js +9 -4
- package/shared/localization/locales/en-US.json +1 -1
- package/shared/localization/locales/en-XL.json +1 -1
- package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
- package/types/lhr/flow.d.ts +3 -2
- package/types/rollup-plugin-postprocess.d.ts +10 -0
|
@@ -10,6 +10,7 @@ const ComputedMetric = require('./metric.js');
|
|
|
10
10
|
const TraceProcessor = require('../../lib/tracehouse/trace-processor.js');
|
|
11
11
|
const LanternTotalBlockingTime = require('./lantern-total-blocking-time.js');
|
|
12
12
|
const TimetoInteractive = require('./interactive.js');
|
|
13
|
+
const {calculateSumOfBlockingTime} = require('./tbt-utils.js');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @fileoverview This audit determines Total Blocking Time.
|
|
@@ -25,57 +26,6 @@ const TimetoInteractive = require('./interactive.js');
|
|
|
25
26
|
* to smaller improvements to main thread responsiveness.
|
|
26
27
|
*/
|
|
27
28
|
class TotalBlockingTime extends ComputedMetric {
|
|
28
|
-
/**
|
|
29
|
-
* @return {number}
|
|
30
|
-
*/
|
|
31
|
-
static get BLOCKING_TIME_THRESHOLD() {
|
|
32
|
-
return 50;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @param {Array<{start: number, end: number, duration: number}>} topLevelEvents
|
|
37
|
-
* @param {number} startTimeMs
|
|
38
|
-
* @param {number} endTimeMs
|
|
39
|
-
* @return {number}
|
|
40
|
-
*/
|
|
41
|
-
static calculateSumOfBlockingTime(topLevelEvents, startTimeMs, endTimeMs) {
|
|
42
|
-
if (endTimeMs <= startTimeMs) return 0;
|
|
43
|
-
|
|
44
|
-
const threshold = TotalBlockingTime.BLOCKING_TIME_THRESHOLD;
|
|
45
|
-
let sumBlockingTime = 0;
|
|
46
|
-
for (const event of topLevelEvents) {
|
|
47
|
-
// Early exit for small tasks, which should far outnumber long tasks.
|
|
48
|
-
if (event.duration < threshold) continue;
|
|
49
|
-
|
|
50
|
-
// We only want to consider tasks that fall in our time range (FCP and TTI for navigations).
|
|
51
|
-
// FCP is picked as the lower bound because there is little risk of user input happening
|
|
52
|
-
// before FCP so Long Queuing Qelay regions do not harm user experience. Developers should be
|
|
53
|
-
// optimizing to reach FCP as fast as possible without having to worry about task lengths.
|
|
54
|
-
if (event.end < startTimeMs) continue;
|
|
55
|
-
|
|
56
|
-
// TTI is picked as the upper bound because we want a well defined end point for page load.
|
|
57
|
-
if (event.start > endTimeMs) continue;
|
|
58
|
-
|
|
59
|
-
// We first perform the clipping, and then calculate Blocking Region. So if we have a 150ms
|
|
60
|
-
// task [0, 150] and FCP happens midway at 50ms, we first clip the task to [50, 150], and then
|
|
61
|
-
// calculate the Blocking Region to be [100, 150]. The rational here is that tasks before FCP
|
|
62
|
-
// are unimportant, so we care whether the main thread is busy more than 50ms at a time only
|
|
63
|
-
// after FCP.
|
|
64
|
-
const clippedStart = Math.max(event.start, startTimeMs);
|
|
65
|
-
const clippedEnd = Math.min(event.end, endTimeMs);
|
|
66
|
-
const clippedDuration = clippedEnd - clippedStart;
|
|
67
|
-
if (clippedDuration < threshold) continue;
|
|
68
|
-
|
|
69
|
-
// The duration of the task beyond 50ms at the beginning is considered the Blocking Region.
|
|
70
|
-
// Example:
|
|
71
|
-
// [ 250ms Task ]
|
|
72
|
-
// | First 50ms | Blocking Region (200ms) |
|
|
73
|
-
sumBlockingTime += clippedDuration - threshold;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return sumBlockingTime;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
29
|
/**
|
|
80
30
|
* @param {LH.Artifacts.MetricComputationData} data
|
|
81
31
|
* @param {LH.Artifacts.ComputedContext} context
|
|
@@ -100,7 +50,7 @@ class TotalBlockingTime extends ComputedMetric {
|
|
|
100
50
|
const interactiveTimeMs = (await TimetoInteractive.request(metricData, context)).timing;
|
|
101
51
|
|
|
102
52
|
return {
|
|
103
|
-
timing:
|
|
53
|
+
timing: calculateSumOfBlockingTime(
|
|
104
54
|
events,
|
|
105
55
|
firstContentfulPaint,
|
|
106
56
|
interactiveTimeMs
|
|
@@ -108,7 +58,7 @@ class TotalBlockingTime extends ComputedMetric {
|
|
|
108
58
|
};
|
|
109
59
|
} else {
|
|
110
60
|
return {
|
|
111
|
-
timing:
|
|
61
|
+
timing: calculateSumOfBlockingTime(
|
|
112
62
|
events,
|
|
113
63
|
0,
|
|
114
64
|
data.processedTrace.timestamps.traceEnd
|
|
@@ -203,6 +203,18 @@ function expandAuditShorthand(audit) {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
/** @type {Map<string, any>} */
|
|
207
|
+
const bundledModules = new Map(/* BUILD_REPLACE_BUNDLED_MODULES */);
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Wraps `require` with an entrypoint for bundled dynamic modules.
|
|
211
|
+
* See build-bundle.js
|
|
212
|
+
* @param {string} requirePath
|
|
213
|
+
*/
|
|
214
|
+
function requireWrapper(requirePath) {
|
|
215
|
+
return bundledModules.get(requirePath) || require(requirePath);
|
|
216
|
+
}
|
|
217
|
+
|
|
206
218
|
/**
|
|
207
219
|
* @param {string} gathererPath
|
|
208
220
|
* @param {Array<string>} coreGathererList
|
|
@@ -218,7 +230,7 @@ function requireGatherer(gathererPath, coreGathererList, configDir) {
|
|
|
218
230
|
requirePath = resolveModulePath(gathererPath, configDir, 'gatherer');
|
|
219
231
|
}
|
|
220
232
|
|
|
221
|
-
const GathererClass = /** @type {GathererConstructor} */ (
|
|
233
|
+
const GathererClass = /** @type {GathererConstructor} */ (requireWrapper(requirePath));
|
|
222
234
|
|
|
223
235
|
return {
|
|
224
236
|
instance: new GathererClass(),
|
|
@@ -228,14 +240,13 @@ function requireGatherer(gathererPath, coreGathererList, configDir) {
|
|
|
228
240
|
}
|
|
229
241
|
|
|
230
242
|
/**
|
|
231
|
-
*
|
|
232
243
|
* @param {string} auditPath
|
|
233
244
|
* @param {Array<string>} coreAuditList
|
|
234
245
|
* @param {string=} configDir
|
|
235
246
|
* @return {LH.Config.AuditDefn['implementation']}
|
|
236
247
|
*/
|
|
237
248
|
function requireAudit(auditPath, coreAuditList, configDir) {
|
|
238
|
-
// See if the audit is a Lighthouse core audit.
|
|
249
|
+
// See if the audit is a Lighthouse core audit.
|
|
239
250
|
const auditPathJs = `${auditPath}.js`;
|
|
240
251
|
const coreAudit = coreAuditList.find(a => a === auditPathJs);
|
|
241
252
|
let requirePath = `../audits/${auditPath}`;
|
|
@@ -244,14 +255,14 @@ function requireAudit(auditPath, coreAuditList, configDir) {
|
|
|
244
255
|
// This is for pubads bundling.
|
|
245
256
|
requirePath = auditPath;
|
|
246
257
|
} else {
|
|
247
|
-
|
|
258
|
+
// Otherwise, attempt to find it elsewhere. This throws if not found.
|
|
248
259
|
const absolutePath = resolveModulePath(auditPath, configDir, 'audit');
|
|
249
260
|
// Use a relative path so bundler can easily expose it.
|
|
250
261
|
requirePath = path.relative(__dirname, absolutePath);
|
|
251
262
|
}
|
|
252
263
|
}
|
|
253
264
|
|
|
254
|
-
return
|
|
265
|
+
return requireWrapper(requirePath);
|
|
255
266
|
}
|
|
256
267
|
|
|
257
268
|
/**
|
|
@@ -313,7 +324,6 @@ function resolveSettings(settingsJson = {}, overrides = undefined) {
|
|
|
313
324
|
return settingsWithFlags;
|
|
314
325
|
}
|
|
315
326
|
|
|
316
|
-
|
|
317
327
|
/**
|
|
318
328
|
* @param {LH.Config.Json} configJSON
|
|
319
329
|
* @param {string | undefined} configDir
|
|
@@ -332,7 +342,7 @@ function mergePlugins(configJSON, configDir, flags) {
|
|
|
332
342
|
const pluginPath = isBundledEnvironment() ?
|
|
333
343
|
pluginName :
|
|
334
344
|
resolveModulePath(pluginName, configDir, 'plugin');
|
|
335
|
-
const rawPluginJson =
|
|
345
|
+
const rawPluginJson = requireWrapper(pluginPath);
|
|
336
346
|
const pluginJson = ConfigPlugin.parsePlugin(rawPluginJson, pluginName);
|
|
337
347
|
|
|
338
348
|
configJSON = mergeConfigFragment(configJSON, pluginJson);
|
|
@@ -569,12 +579,12 @@ function deepCloneConfigJson(json) {
|
|
|
569
579
|
module.exports = {
|
|
570
580
|
deepClone,
|
|
571
581
|
deepCloneConfigJson,
|
|
572
|
-
mergeOptionsOfItems,
|
|
573
582
|
mergeConfigFragment,
|
|
574
583
|
mergeConfigFragmentArrayByKey,
|
|
584
|
+
mergeOptionsOfItems,
|
|
575
585
|
mergePlugins,
|
|
576
|
-
resolveSettings,
|
|
577
|
-
resolveGathererToDefn,
|
|
578
586
|
resolveAuditsToDefns,
|
|
587
|
+
resolveGathererToDefn,
|
|
579
588
|
resolveModulePath,
|
|
589
|
+
resolveSettings,
|
|
580
590
|
};
|
|
@@ -96,7 +96,7 @@ const UIStrings = {
|
|
|
96
96
|
/** Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers. */
|
|
97
97
|
seoCrawlingGroupDescription: 'To appear in search results, crawlers need access to your app.',
|
|
98
98
|
/** Title of the Progressive Web Application (PWA) category of audits. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. Also used as a label of a score gauge. */
|
|
99
|
-
pwaCategoryTitle: '
|
|
99
|
+
pwaCategoryTitle: 'PWA',
|
|
100
100
|
/** Description of the Progressive Web Application (PWA) category. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. No character length limits. 'Learn More' becomes link text to additional documentation. */
|
|
101
101
|
pwaCategoryDescription: 'These checks validate the aspects of a Progressive Web App. ' +
|
|
102
102
|
'[Learn more](https://developers.google.com/web/progressive-web-apps/checklist).',
|
|
@@ -405,6 +405,8 @@ const defaultConfig = {
|
|
|
405
405
|
'best-practices-general': {
|
|
406
406
|
title: str_(UIStrings.bestPracticesGeneralGroupTitle),
|
|
407
407
|
},
|
|
408
|
+
// Group for audits that should not be displayed.
|
|
409
|
+
'hidden': {title: ''},
|
|
408
410
|
},
|
|
409
411
|
categories: {
|
|
410
412
|
'performance': {
|
|
@@ -418,61 +420,65 @@ const defaultConfig = {
|
|
|
418
420
|
{id: 'total-blocking-time', weight: 30, group: 'metrics', acronym: 'TBT', relevantAudits: m2a.tbtRelevantAudits},
|
|
419
421
|
{id: 'cumulative-layout-shift', weight: 15, group: 'metrics', acronym: 'CLS', relevantAudits: m2a.clsRelevantAudits},
|
|
420
422
|
|
|
421
|
-
// These are our "invisible" metrics. Not displayed, but still in the LHR
|
|
422
|
-
{id: 'max-potential-fid', weight: 0},
|
|
423
|
-
{id: 'first-meaningful-paint', weight: 0, acronym: 'FMP'},
|
|
423
|
+
// These are our "invisible" metrics. Not displayed, but still in the LHR.
|
|
424
|
+
{id: 'max-potential-fid', weight: 0, group: 'hidden'},
|
|
425
|
+
{id: 'first-meaningful-paint', weight: 0, acronym: 'FMP', group: 'hidden'},
|
|
424
426
|
|
|
425
|
-
|
|
426
|
-
{id: '
|
|
427
|
-
{id: '
|
|
428
|
-
{id: '
|
|
429
|
-
{id: 'unminified-
|
|
430
|
-
{id: '
|
|
431
|
-
{id: 'unused-
|
|
432
|
-
{id: '
|
|
433
|
-
{id: '
|
|
434
|
-
{id: '
|
|
435
|
-
{id: 'uses-
|
|
436
|
-
{id: '
|
|
437
|
-
{id: '
|
|
438
|
-
{id: '
|
|
439
|
-
{id: 'uses-
|
|
440
|
-
{id: '
|
|
441
|
-
{id: '
|
|
442
|
-
{id: '
|
|
443
|
-
{id: '
|
|
444
|
-
{id: '
|
|
445
|
-
{id: '
|
|
446
|
-
{id: '
|
|
447
|
-
{id: '
|
|
448
|
-
{id: '
|
|
449
|
-
{id: '
|
|
450
|
-
{id: '
|
|
451
|
-
{id: '
|
|
427
|
+
// These audits will be put in "load-opportunities" or "diagnostics" based on their details type.
|
|
428
|
+
{id: 'render-blocking-resources', weight: 0},
|
|
429
|
+
{id: 'uses-responsive-images', weight: 0},
|
|
430
|
+
{id: 'offscreen-images', weight: 0},
|
|
431
|
+
{id: 'unminified-css', weight: 0},
|
|
432
|
+
{id: 'unminified-javascript', weight: 0},
|
|
433
|
+
{id: 'unused-css-rules', weight: 0},
|
|
434
|
+
{id: 'unused-javascript', weight: 0},
|
|
435
|
+
{id: 'uses-optimized-images', weight: 0},
|
|
436
|
+
{id: 'modern-image-formats', weight: 0},
|
|
437
|
+
{id: 'uses-text-compression', weight: 0},
|
|
438
|
+
{id: 'uses-rel-preconnect', weight: 0},
|
|
439
|
+
{id: 'server-response-time', weight: 0},
|
|
440
|
+
{id: 'redirects', weight: 0},
|
|
441
|
+
{id: 'uses-rel-preload', weight: 0},
|
|
442
|
+
{id: 'uses-http2', weight: 0},
|
|
443
|
+
{id: 'efficient-animated-content', weight: 0},
|
|
444
|
+
{id: 'duplicated-javascript', weight: 0},
|
|
445
|
+
{id: 'legacy-javascript', weight: 0},
|
|
446
|
+
{id: 'preload-lcp-image', weight: 0},
|
|
447
|
+
{id: 'total-byte-weight', weight: 0},
|
|
448
|
+
{id: 'uses-long-cache-ttl', weight: 0},
|
|
449
|
+
{id: 'dom-size', weight: 0},
|
|
450
|
+
{id: 'critical-request-chains', weight: 0},
|
|
451
|
+
{id: 'user-timings', weight: 0},
|
|
452
|
+
{id: 'bootup-time', weight: 0},
|
|
453
|
+
{id: 'mainthread-work-breakdown', weight: 0},
|
|
454
|
+
{id: 'font-display', weight: 0},
|
|
455
|
+
{id: 'resource-summary', weight: 0},
|
|
456
|
+
{id: 'third-party-summary', weight: 0},
|
|
457
|
+
{id: 'third-party-facades', weight: 0},
|
|
458
|
+
{id: 'largest-contentful-paint-element', weight: 0},
|
|
459
|
+
{id: 'lcp-lazy-loaded', weight: 0},
|
|
460
|
+
{id: 'layout-shift-elements', weight: 0},
|
|
461
|
+
{id: 'uses-passive-event-listeners', weight: 0},
|
|
462
|
+
{id: 'no-document-write', weight: 0},
|
|
463
|
+
{id: 'long-tasks', weight: 0},
|
|
464
|
+
{id: 'non-composited-animations', weight: 0},
|
|
465
|
+
{id: 'unsized-images', weight: 0},
|
|
466
|
+
{id: 'viewport', weight: 0},
|
|
467
|
+
|
|
468
|
+
// Budget audits.
|
|
452
469
|
{id: 'performance-budget', weight: 0, group: 'budgets'},
|
|
453
470
|
{id: 'timing-budget', weight: 0, group: 'budgets'},
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
{id: '
|
|
457
|
-
{id: '
|
|
458
|
-
{id: '
|
|
459
|
-
{id: '
|
|
460
|
-
{id: '
|
|
461
|
-
{id: '
|
|
462
|
-
{id: '
|
|
463
|
-
{id: '
|
|
464
|
-
{id: '
|
|
465
|
-
{id: 'viewport', weight: 0, group: 'diagnostics'},
|
|
466
|
-
// Audits past this point don't belong to a group and will not be shown automatically
|
|
467
|
-
{id: 'network-requests', weight: 0},
|
|
468
|
-
{id: 'network-rtt', weight: 0},
|
|
469
|
-
{id: 'network-server-latency', weight: 0},
|
|
470
|
-
{id: 'main-thread-tasks', weight: 0},
|
|
471
|
-
{id: 'diagnostics', weight: 0},
|
|
472
|
-
{id: 'metrics', weight: 0},
|
|
473
|
-
{id: 'screenshot-thumbnails', weight: 0},
|
|
474
|
-
{id: 'final-screenshot', weight: 0},
|
|
475
|
-
{id: 'script-treemap-data', weight: 0},
|
|
471
|
+
|
|
472
|
+
// Audits past this point contain useful data but are not displayed with other audits.
|
|
473
|
+
{id: 'network-requests', weight: 0, group: 'hidden'},
|
|
474
|
+
{id: 'network-rtt', weight: 0, group: 'hidden'},
|
|
475
|
+
{id: 'network-server-latency', weight: 0, group: 'hidden'},
|
|
476
|
+
{id: 'main-thread-tasks', weight: 0, group: 'hidden'},
|
|
477
|
+
{id: 'diagnostics', weight: 0, group: 'hidden'},
|
|
478
|
+
{id: 'metrics', weight: 0, group: 'hidden'},
|
|
479
|
+
{id: 'screenshot-thumbnails', weight: 0, group: 'hidden'},
|
|
480
|
+
{id: 'final-screenshot', weight: 0, group: 'hidden'},
|
|
481
|
+
{id: 'script-treemap-data', weight: 0, group: 'hidden'},
|
|
476
482
|
],
|
|
477
483
|
},
|
|
478
484
|
'accessibility': {
|
|
@@ -15,7 +15,7 @@ const frAudits = [
|
|
|
15
15
|
/** @type {Record<string, LH.Config.AuditRefJson[]>} */
|
|
16
16
|
const frCategoryAuditRefExtensions = {
|
|
17
17
|
'performance': [
|
|
18
|
-
{id: 'uses-responsive-images-snapshot', weight: 0
|
|
18
|
+
{id: 'uses-responsive-images-snapshot', weight: 0},
|
|
19
19
|
],
|
|
20
20
|
};
|
|
21
21
|
|
|
@@ -62,8 +62,13 @@ class FRGatherer {
|
|
|
62
62
|
* @return {keyof LH.GathererArtifacts}
|
|
63
63
|
*/
|
|
64
64
|
get name() {
|
|
65
|
+
let name = this.constructor.name;
|
|
66
|
+
// Rollup will mangle class names in an known way–just trim until `$`.
|
|
67
|
+
if (name.includes('$')) {
|
|
68
|
+
name = name.substr(0, name.indexOf('$'));
|
|
69
|
+
}
|
|
65
70
|
// @ts-expect-error - assume that class name has been added to LH.GathererArtifacts.
|
|
66
|
-
return
|
|
71
|
+
return name;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
/**
|
|
@@ -16,7 +16,10 @@ const Metrics = require('./traces/pwmetrics-events.js');
|
|
|
16
16
|
const NetworkAnalysisComputed = require('../computed/network-analysis.js');
|
|
17
17
|
const LoadSimulatorComputed = require('../computed/load-simulator.js');
|
|
18
18
|
const LHError = require('../lib/lh-error.js');
|
|
19
|
-
|
|
19
|
+
// TODO(esmodules): Rollup does not support `promisfy` or `stream.pipeline`. Bundled files
|
|
20
|
+
// don't need anything in this file except for `stringifyReplacer`, so a check for
|
|
21
|
+
// truthiness before using is enough.
|
|
22
|
+
const pipeline = promisify && promisify(stream.pipeline);
|
|
20
23
|
|
|
21
24
|
const artifactsFilename = 'artifacts.json';
|
|
22
25
|
const traceSuffix = '.trace.json';
|
|
@@ -5,14 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
filterOutRectsContainedByOthers,
|
|
10
|
-
filterOutTinyRects,
|
|
11
|
-
rectsTouchOrOverlap,
|
|
12
|
-
rectContainsPoint,
|
|
13
|
-
getBoundingRect,
|
|
14
|
-
getRectCenterPoint,
|
|
15
|
-
} = require('./rect-helpers.js');
|
|
8
|
+
const RectHelpers = require('./rect-helpers.js');
|
|
16
9
|
|
|
17
10
|
/**
|
|
18
11
|
* Merge client rects together and remove small ones. This may result in a larger overall
|
|
@@ -23,8 +16,8 @@ const {
|
|
|
23
16
|
function getTappableRectsFromClientRects(clientRects) {
|
|
24
17
|
// 1x1px rect shouldn't be reason to treat the rect as something the user should tap on.
|
|
25
18
|
// Often they're made invisble in some obscure way anyway, and only exist for e.g. accessibiliity.
|
|
26
|
-
clientRects = filterOutTinyRects(clientRects);
|
|
27
|
-
clientRects = filterOutRectsContainedByOthers(clientRects);
|
|
19
|
+
clientRects = RectHelpers.filterOutTinyRects(clientRects);
|
|
20
|
+
clientRects = RectHelpers.filterOutRectsContainedByOthers(clientRects);
|
|
28
21
|
clientRects = mergeTouchingClientRects(clientRects);
|
|
29
22
|
return clientRects;
|
|
30
23
|
}
|
|
@@ -68,17 +61,17 @@ function mergeTouchingClientRects(clientRects) {
|
|
|
68
61
|
const rectsLineUpVertically =
|
|
69
62
|
almostEqual(crA.left, crB.left) || almostEqual(crA.right, crB.right);
|
|
70
63
|
const canMerge =
|
|
71
|
-
rectsTouchOrOverlap(crA, crB) &&
|
|
64
|
+
RectHelpers.rectsTouchOrOverlap(crA, crB) &&
|
|
72
65
|
(rectsLineUpHorizontally || rectsLineUpVertically);
|
|
73
66
|
|
|
74
67
|
if (canMerge) {
|
|
75
|
-
const replacementClientRect = getBoundingRect([crA, crB]);
|
|
76
|
-
const mergedRectCenter = getRectCenterPoint(replacementClientRect);
|
|
68
|
+
const replacementClientRect = RectHelpers.getBoundingRect([crA, crB]);
|
|
69
|
+
const mergedRectCenter = RectHelpers.getRectCenterPoint(replacementClientRect);
|
|
77
70
|
|
|
78
71
|
if (
|
|
79
72
|
!(
|
|
80
|
-
rectContainsPoint(crA, mergedRectCenter) ||
|
|
81
|
-
rectContainsPoint(crB, mergedRectCenter)
|
|
73
|
+
RectHelpers.rectContainsPoint(crA, mergedRectCenter) ||
|
|
74
|
+
RectHelpers.rectContainsPoint(crB, mergedRectCenter)
|
|
82
75
|
)
|
|
83
76
|
) {
|
|
84
77
|
// Don't merge because the new shape is too different from the
|
|
@@ -20,6 +20,7 @@ const URL = require('./lib/url-shim.js');
|
|
|
20
20
|
const Sentry = require('./lib/sentry.js');
|
|
21
21
|
const generateReport = require('../report/generator/report-generator.js').generateReport;
|
|
22
22
|
const LHError = require('./lib/lh-error.js');
|
|
23
|
+
const {version: lighthouseVersion} = require('../package.json');
|
|
23
24
|
|
|
24
25
|
/** @typedef {import('./gather/connections/connection.js')} Connection */
|
|
25
26
|
/** @typedef {import('./lib/arbitrary-equality-map.js')} ArbitraryEqualityMap */
|
|
@@ -112,7 +113,6 @@ class Runner {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
// Entering: conclusion of the lighthouse result object
|
|
115
|
-
const lighthouseVersion = require('../package.json').version;
|
|
116
116
|
|
|
117
117
|
// Use version from gathering stage.
|
|
118
118
|
// If accessibility gatherer didn't run or errored, it won't be in credits.
|
|
@@ -519,10 +519,9 @@ class Util {
|
|
|
519
519
|
let totalWeight = 0;
|
|
520
520
|
for (const auditRef of category.auditRefs) {
|
|
521
521
|
const auditPassed = Util.showAsPassed(auditRef.result);
|
|
522
|
-
const notDisplayed = !auditRef.group && category.id === 'performance';
|
|
523
522
|
|
|
524
523
|
// Don't count the audit if it's manual, N/A, or isn't displayed.
|
|
525
|
-
if (
|
|
524
|
+
if (auditRef.group === 'hidden' ||
|
|
526
525
|
auditRef.result.scoreDisplayMode === 'manual' ||
|
|
527
526
|
auditRef.result.scoreDisplayMode === 'notApplicable') {
|
|
528
527
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "8.6.0-dev.
|
|
3
|
+
"version": "8.6.0-dev.20211101",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -95,9 +95,16 @@
|
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@build-tracker/cli": "^1.0.0-beta.15",
|
|
98
|
+
"@firebase/app-types": "0.3.1",
|
|
99
|
+
"@firebase/auth-types": "0.3.2",
|
|
100
|
+
"@firebase/util": "0.2.1",
|
|
101
|
+
"@rollup/plugin-alias": "^3.1.2",
|
|
98
102
|
"@rollup/plugin-commonjs": "^20.0.0",
|
|
103
|
+
"@rollup/plugin-dynamic-import-vars": "^1.1.1",
|
|
104
|
+
"@rollup/plugin-json": "^4.1.0",
|
|
99
105
|
"@rollup/plugin-node-resolve": "^13.0.4",
|
|
100
106
|
"@rollup/plugin-typescript": "^8.2.5",
|
|
107
|
+
"@stadtlandnetz/rollup-plugin-postprocess": "^1.1.0",
|
|
101
108
|
"@testing-library/preact": "^2.0.1",
|
|
102
109
|
"@testing-library/preact-hooks": "^1.1.0",
|
|
103
110
|
"@types/archiver": "^2.1.2",
|
|
@@ -107,7 +114,6 @@
|
|
|
107
114
|
"@types/cpy": "^5.1.0",
|
|
108
115
|
"@types/eslint": "^4.16.6",
|
|
109
116
|
"@types/estree": "^0.0.50",
|
|
110
|
-
"@types/exorcist": "^0.4.5",
|
|
111
117
|
"@types/gh-pages": "^2.0.0",
|
|
112
118
|
"@types/google.analytics": "0.0.39",
|
|
113
119
|
"@types/jest": "^24.0.9",
|
|
@@ -133,7 +139,6 @@
|
|
|
133
139
|
"angular": "^1.7.4",
|
|
134
140
|
"archiver": "^3.0.0",
|
|
135
141
|
"browserify": "^17.0.0",
|
|
136
|
-
"browserify-banner": "^1.0.15",
|
|
137
142
|
"c8": "^7.4.0",
|
|
138
143
|
"chalk": "^2.4.1",
|
|
139
144
|
"chrome-devtools-frontend": "1.0.922924",
|
|
@@ -148,7 +153,6 @@
|
|
|
148
153
|
"eslint-plugin-import": "^2.24.2",
|
|
149
154
|
"eslint-plugin-local-rules": "0.1.0",
|
|
150
155
|
"event-target-shim": "^6.0.2",
|
|
151
|
-
"exorcist": "^1.0.1",
|
|
152
156
|
"firebase": "^9.0.2",
|
|
153
157
|
"gh-pages": "^2.0.1",
|
|
154
158
|
"glob": "^7.1.3",
|
|
@@ -163,13 +167,12 @@
|
|
|
163
167
|
"mime-types": "^2.1.30",
|
|
164
168
|
"node-fetch": "^2.6.1",
|
|
165
169
|
"npm-run-posix-or-windows": "^2.0.2",
|
|
166
|
-
"package-json-versionify": "^1.0.4",
|
|
167
170
|
"pako": "^2.0.3",
|
|
168
171
|
"preact": "^10.5.14",
|
|
169
172
|
"pretty-json-stringify": "^0.0.2",
|
|
170
173
|
"puppeteer": "^10.2.0",
|
|
171
174
|
"resolve": "^1.20.0",
|
|
172
|
-
"rollup": "^2.
|
|
175
|
+
"rollup": "^2.52.7",
|
|
173
176
|
"rollup-plugin-node-resolve": "^5.2.0",
|
|
174
177
|
"rollup-plugin-polyfill-node": "^0.7.0",
|
|
175
178
|
"rollup-plugin-replace": "^2.2.0",
|
|
@@ -18,8 +18,6 @@ import {Logger} from '../renderer/logger.js';
|
|
|
18
18
|
import {ReportRenderer} from '../renderer/report-renderer.js';
|
|
19
19
|
import {ReportUIFeatures} from '../renderer/report-ui-features.js';
|
|
20
20
|
|
|
21
|
-
// Used by standalone.html
|
|
22
|
-
// eslint-disable-next-line no-unused-vars
|
|
23
21
|
function __initLighthouseReport__() {
|
|
24
22
|
const dom = new DOM(document);
|
|
25
23
|
const renderer = new ReportRenderer(dom);
|
|
@@ -51,7 +51,7 @@ function clamp(value, min, max) {
|
|
|
51
51
|
/**
|
|
52
52
|
* @param {Rect} rect
|
|
53
53
|
*/
|
|
54
|
-
function
|
|
54
|
+
function getElementRectCenterPoint(rect) {
|
|
55
55
|
return {
|
|
56
56
|
x: rect.left + rect.width / 2,
|
|
57
57
|
y: rect.top + rect.height / 2,
|
|
@@ -68,7 +68,7 @@ export class ElementScreenshotRenderer {
|
|
|
68
68
|
* @param {Size} screenshotSize
|
|
69
69
|
*/
|
|
70
70
|
static getScreenshotPositions(elementRectSC, elementPreviewSizeSC, screenshotSize) {
|
|
71
|
-
const elementRectCenter =
|
|
71
|
+
const elementRectCenter = getElementRectCenterPoint(elementRectSC);
|
|
72
72
|
|
|
73
73
|
// Try to center clipped region.
|
|
74
74
|
const screenshotLeftVisibleEdge = clamp(
|
|
@@ -152,6 +152,21 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
152
152
|
return url.href;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* For performance, audits with no group should be a diagnostic or opportunity.
|
|
157
|
+
* The audit details type will determine which of the two groups an audit is in.
|
|
158
|
+
*
|
|
159
|
+
* @param {LH.ReportResult.AuditRef} audit
|
|
160
|
+
* @return {'load-opportunity'|'diagnostic'|null}
|
|
161
|
+
*/
|
|
162
|
+
_classifyPerformanceAudit(audit) {
|
|
163
|
+
if (audit.group) return null;
|
|
164
|
+
if (audit.result.details && audit.result.details.type === 'opportunity') {
|
|
165
|
+
return 'load-opportunity';
|
|
166
|
+
}
|
|
167
|
+
return 'diagnostic';
|
|
168
|
+
}
|
|
169
|
+
|
|
155
170
|
/**
|
|
156
171
|
* @param {LH.ReportResult.Category} category
|
|
157
172
|
* @param {Object<string, LH.Result.ReportGroup>} groups
|
|
@@ -209,10 +224,10 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
209
224
|
|
|
210
225
|
// Opportunities
|
|
211
226
|
const opportunityAudits = category.auditRefs
|
|
212
|
-
.filter(audit => audit
|
|
227
|
+
.filter(audit => this._classifyPerformanceAudit(audit) === 'load-opportunity')
|
|
228
|
+
.filter(audit => !Util.showAsPassed(audit.result))
|
|
213
229
|
.sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
|
|
214
230
|
|
|
215
|
-
|
|
216
231
|
const filterableMetrics = metricAudits.filter(a => !!a.relevantAudits);
|
|
217
232
|
// TODO: only add if there are opportunities & diagnostics rendered.
|
|
218
233
|
if (filterableMetrics.length) {
|
|
@@ -242,7 +257,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
242
257
|
|
|
243
258
|
// Diagnostics
|
|
244
259
|
const diagnosticAudits = category.auditRefs
|
|
245
|
-
.filter(audit => audit
|
|
260
|
+
.filter(audit => this._classifyPerformanceAudit(audit) === 'diagnostic')
|
|
261
|
+
.filter(audit => !Util.showAsPassed(audit.result))
|
|
246
262
|
.sort((a, b) => {
|
|
247
263
|
const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
|
|
248
264
|
const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
|
|
@@ -258,8 +274,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
|
|
|
258
274
|
|
|
259
275
|
// Passed audits
|
|
260
276
|
const passedAudits = category.auditRefs
|
|
261
|
-
.filter(audit => (audit
|
|
262
|
-
Util.showAsPassed(audit.result));
|
|
277
|
+
.filter(audit => this._classifyPerformanceAudit(audit) && Util.showAsPassed(audit.result));
|
|
263
278
|
|
|
264
279
|
if (!passedAudits.length) return element;
|
|
265
280
|
|
package/report/renderer/util.js
CHANGED
|
@@ -516,10 +516,9 @@ export class Util {
|
|
|
516
516
|
let totalWeight = 0;
|
|
517
517
|
for (const auditRef of category.auditRefs) {
|
|
518
518
|
const auditPassed = Util.showAsPassed(auditRef.result);
|
|
519
|
-
const notDisplayed = !auditRef.group && category.id === 'performance';
|
|
520
519
|
|
|
521
520
|
// Don't count the audit if it's manual, N/A, or isn't displayed.
|
|
522
|
-
if (
|
|
521
|
+
if (auditRef.group === 'hidden' ||
|
|
523
522
|
auditRef.result.scoreDisplayMode === 'manual' ||
|
|
524
523
|
auditRef.result.scoreDisplayMode === 'notApplicable') {
|
|
525
524
|
continue;
|