lighthouse 9.5.0-dev.20220329 → 9.5.0-dev.20220401
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/bundle.esm.js +20 -18
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +28 -18
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -1
- package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +3 -2
- package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +1 -1
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +2 -1
- package/lighthouse-core/audits/long-tasks.js +3 -2
- package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +2 -2
- package/lighthouse-core/audits/metrics/first-contentful-paint.js +3 -2
- package/lighthouse-core/audits/metrics/first-meaningful-paint.js +3 -2
- package/lighthouse-core/audits/metrics/interactive.js +3 -2
- package/lighthouse-core/audits/metrics/largest-contentful-paint.js +3 -2
- package/lighthouse-core/audits/metrics/max-potential-fid.js +3 -2
- package/lighthouse-core/audits/metrics/speed-index.js +3 -2
- package/lighthouse-core/audits/metrics/total-blocking-time.js +3 -2
- package/lighthouse-core/audits/metrics.js +3 -2
- package/lighthouse-core/audits/predictive-perf.js +2 -1
- package/lighthouse-core/audits/preload-lcp-image.js +3 -3
- package/lighthouse-core/audits/redirects.js +1 -1
- package/lighthouse-core/audits/script-treemap-data.js +31 -32
- package/lighthouse-core/audits/timing-budget.js +3 -2
- package/lighthouse-core/audits/uses-rel-preconnect.js +1 -1
- package/lighthouse-core/audits/uses-rel-preload.js +1 -1
- package/lighthouse-core/computed/critical-request-chains.js +2 -9
- package/lighthouse-core/computed/main-resource.js +1 -1
- package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
- package/lighthouse-core/computed/metrics/first-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/first-meaningful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/interactive.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-interactive.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-max-potential-fid.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-metric.js +3 -5
- package/lighthouse-core/computed/metrics/lantern-speed-index.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +1 -1
- package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
- package/lighthouse-core/computed/metrics/largest-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/max-potential-fid.js +1 -1
- package/lighthouse-core/computed/metrics/metric.js +1 -0
- package/lighthouse-core/computed/metrics/speed-index.js +1 -1
- package/lighthouse-core/computed/metrics/timing-summary.js +13 -6
- package/lighthouse-core/computed/metrics/total-blocking-time.js +1 -1
- package/lighthouse-core/computed/page-dependency-graph.js +54 -31
- package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +0 -3
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +0 -1
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +0 -2
- package/lighthouse-core/gather/connections/cri.js +1 -0
- package/lighthouse-core/gather/driver/execution-context.js +4 -2
- package/lighthouse-core/gather/gatherers/js-usage.js +7 -6
- package/lighthouse-core/gather/gatherers/scripts.js +20 -2
- package/lighthouse-core/gather/gatherers/seo/robots-txt.js +2 -1
- package/lighthouse-core/gather/gatherers/web-app-manifest.js +2 -2
- package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +8 -29
- package/lighthouse-core/lib/navigation-error.js +13 -4
- package/lighthouse-core/util-commonjs.js +20 -18
- package/package.json +4 -4
- package/report/renderer/util.js +20 -18
- package/shared/localization/locales/en-US.json +1 -1
- package/shared/localization/locales/en-XL.json +1 -1
- package/types/artifacts.d.ts +1 -0
- package/types/gatherer.d.ts +0 -2
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
const makeComputedArtifact = require('./computed-artifact.js');
|
|
9
9
|
const NetworkNode = require('../lib/dependency-graph/network-node.js');
|
|
10
10
|
const CPUNode = require('../lib/dependency-graph/cpu-node.js');
|
|
11
|
-
const NetworkAnalyzer = require('../lib/dependency-graph/simulator/network-analyzer.js');
|
|
12
11
|
const TracingProcessor = require('../lib/tracehouse/trace-processor.js');
|
|
13
12
|
const NetworkRequest = require('../lib/network-request.js');
|
|
14
13
|
const ProcessedTrace = require('./processed-trace.js');
|
|
15
14
|
const NetworkRecords = require('./network-records.js');
|
|
15
|
+
const NetworkAnalyzer = require('../lib/dependency-graph/simulator/network-analyzer.js');
|
|
16
16
|
|
|
17
17
|
/** @typedef {import('../lib/dependency-graph/base-node.js').Node} Node */
|
|
18
|
+
/** @typedef {Omit<LH.Artifacts['URL'], 'initialUrl'|'finalUrl'>} URLArtifact */
|
|
18
19
|
|
|
19
20
|
// Shorter tasks have negligible impact on simulation results.
|
|
20
21
|
const SIGNIFICANT_DUR_THRESHOLD_MS = 10;
|
|
@@ -388,37 +389,24 @@ class PageDependencyGraph {
|
|
|
388
389
|
/**
|
|
389
390
|
* @param {LH.Artifacts.ProcessedTrace} processedTrace
|
|
390
391
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
392
|
+
* @param {URLArtifact} URL
|
|
391
393
|
* @return {Node}
|
|
392
394
|
*/
|
|
393
|
-
static createGraph(processedTrace, networkRecords) {
|
|
395
|
+
static createGraph(processedTrace, networkRecords, URL) {
|
|
394
396
|
const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords);
|
|
395
397
|
const cpuNodes = PageDependencyGraph.getCPUNodes(processedTrace);
|
|
398
|
+
const {requestedUrl, mainDocumentUrl} = URL;
|
|
399
|
+
if (!mainDocumentUrl) throw new Error('mainDocumentUrl is required to get the main resource');
|
|
396
400
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
// This will be different from the root request when there are server redirects.
|
|
402
|
-
const mainDocumentRequest = NetworkAnalyzer.findMainDocument(networkRecords);
|
|
403
|
-
const mainDocumentNode = networkNodeOutput.idToNodeMap.get(mainDocumentRequest.requestId);
|
|
404
|
-
if (!mainDocumentNode) {
|
|
405
|
-
// mainDocumentNode should always be found.
|
|
406
|
-
throw new Error('mainDocumentNode not found.');
|
|
407
|
-
}
|
|
401
|
+
const rootRequest = NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl);
|
|
402
|
+
if (!rootRequest) throw new Error('rootRequest not found');
|
|
403
|
+
const rootNode = networkNodeOutput.idToNodeMap.get(rootRequest.requestId);
|
|
404
|
+
if (!rootNode) throw new Error('rootNode not found');
|
|
408
405
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
let rootNode;
|
|
414
|
-
if (rootRequest) {
|
|
415
|
-
// rootNode should always be found.
|
|
416
|
-
rootNode = rootRequest && networkNodeOutput.idToNodeMap.get(rootRequest.requestId);
|
|
417
|
-
if (!rootNode) throw new Error('rootNode not found');
|
|
418
|
-
} else {
|
|
419
|
-
// Use main document as root if there were no redirects.
|
|
420
|
-
rootNode = mainDocumentNode;
|
|
421
|
-
}
|
|
406
|
+
const mainDocumentRequest = NetworkAnalyzer.findResourceForUrl(networkRecords, mainDocumentUrl);
|
|
407
|
+
if (!mainDocumentRequest) throw new Error('mainDocumentRequest not found');
|
|
408
|
+
const mainDocumentNode = networkNodeOutput.idToNodeMap.get(mainDocumentRequest.requestId);
|
|
409
|
+
if (!mainDocumentNode) throw new Error('mainDocumentNode not found');
|
|
422
410
|
|
|
423
411
|
PageDependencyGraph.linkNetworkNodes(rootNode, networkNodeOutput);
|
|
424
412
|
PageDependencyGraph.linkCPUNodes(rootNode, networkNodeOutput, cpuNodes);
|
|
@@ -464,23 +452,58 @@ class PageDependencyGraph {
|
|
|
464
452
|
}
|
|
465
453
|
|
|
466
454
|
/**
|
|
467
|
-
*
|
|
455
|
+
* Recalculate `artifacts.URL` for clients that don't provide it.
|
|
456
|
+
*
|
|
457
|
+
* @param {LH.DevtoolsLog} devtoolsLog
|
|
458
|
+
* @param {LH.Artifacts.NetworkRequest[]} networkRecords
|
|
459
|
+
* @param {LH.Artifacts.ProcessedTrace} processedTrace
|
|
460
|
+
* @return {URLArtifact}
|
|
461
|
+
*/
|
|
462
|
+
static getDocumentUrls(devtoolsLog, networkRecords, processedTrace) {
|
|
463
|
+
const mainFrameId = processedTrace.mainFrameIds.frameId;
|
|
464
|
+
|
|
465
|
+
/** @type {string|undefined} */
|
|
466
|
+
let requestedUrl;
|
|
467
|
+
/** @type {string|undefined} */
|
|
468
|
+
let mainDocumentUrl;
|
|
469
|
+
for (const event of devtoolsLog) {
|
|
470
|
+
if (event.method === 'Page.frameNavigated' && event.params.frame.id === mainFrameId) {
|
|
471
|
+
const {url} = event.params.frame;
|
|
472
|
+
// Only set requestedUrl on the first main frame navigation.
|
|
473
|
+
if (!requestedUrl) requestedUrl = url;
|
|
474
|
+
mainDocumentUrl = url;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (!requestedUrl || !mainDocumentUrl) throw new Error('No main frame navigations found');
|
|
478
|
+
|
|
479
|
+
const initialRequest = NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl);
|
|
480
|
+
if (initialRequest?.redirects?.length) requestedUrl = initialRequest.redirects[0].url;
|
|
481
|
+
|
|
482
|
+
return {requestedUrl, mainDocumentUrl};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, URL: LH.Artifacts['URL']}} data
|
|
468
487
|
* @param {LH.Artifacts.ComputedContext} context
|
|
469
488
|
* @return {Promise<Node>}
|
|
470
489
|
*/
|
|
471
490
|
static async compute_(data, context) {
|
|
472
|
-
const trace = data
|
|
473
|
-
const devtoolsLog = data.devtoolsLog;
|
|
491
|
+
const {trace, devtoolsLog} = data;
|
|
474
492
|
const [processedTrace, networkRecords] = await Promise.all([
|
|
475
493
|
ProcessedTrace.request(trace, context),
|
|
476
494
|
NetworkRecords.request(devtoolsLog, context),
|
|
477
495
|
]);
|
|
478
496
|
|
|
479
|
-
|
|
497
|
+
// COMPAT: Backport for pre-10.0 clients that don't pass the URL artifact here (e.g. pubads).
|
|
498
|
+
// Calculates the URL artifact from the processed trace and DT log.
|
|
499
|
+
const URL = data.URL ||
|
|
500
|
+
PageDependencyGraph.getDocumentUrls(devtoolsLog, networkRecords, processedTrace);
|
|
501
|
+
|
|
502
|
+
return PageDependencyGraph.createGraph(processedTrace, networkRecords, URL);
|
|
480
503
|
}
|
|
481
504
|
}
|
|
482
505
|
|
|
483
|
-
module.exports = makeComputedArtifact(PageDependencyGraph, ['devtoolsLog', 'trace']);
|
|
506
|
+
module.exports = makeComputedArtifact(PageDependencyGraph, ['devtoolsLog', 'trace', 'URL']);
|
|
484
507
|
|
|
485
508
|
/**
|
|
486
509
|
* @typedef {Object} NetworkNodeOutput
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
* @property {LH.Gatherer.GatherMode} gatherMode
|
|
16
16
|
* @property {Map<string, LH.ArbitraryEqualityMap>} computedCache
|
|
17
17
|
* @property {LH.Config.Settings} settings
|
|
18
|
-
* @property {string} url
|
|
19
18
|
*/
|
|
20
19
|
|
|
21
20
|
/** @typedef {Record<string, Promise<any>>} IntermediateArtifacts */
|
|
@@ -75,7 +74,6 @@ async function collectPhaseArtifacts(options) {
|
|
|
75
74
|
gatherMode,
|
|
76
75
|
computedCache,
|
|
77
76
|
settings,
|
|
78
|
-
url,
|
|
79
77
|
} = options;
|
|
80
78
|
const priorPhase = phaseToPriorPhase[phase];
|
|
81
79
|
const priorPhaseArtifacts = (priorPhase && artifactState[priorPhase]) || {};
|
|
@@ -92,7 +90,6 @@ async function collectPhaseArtifacts(options) {
|
|
|
92
90
|
: /** @type {Dependencies} */ ({});
|
|
93
91
|
|
|
94
92
|
return gatherer[phase]({
|
|
95
|
-
url,
|
|
96
93
|
gatherMode,
|
|
97
94
|
driver,
|
|
98
95
|
baseArtifacts,
|
|
@@ -37,7 +37,6 @@ async function startTimespanGather(options) {
|
|
|
37
37
|
const artifactState = getEmptyArtifactState();
|
|
38
38
|
/** @type {Omit<import('./runner-helpers.js').CollectPhaseArtifactOptions, 'phase'>} */
|
|
39
39
|
const phaseOptions = {
|
|
40
|
-
url: initialUrl,
|
|
41
40
|
driver,
|
|
42
41
|
artifactDefinitions,
|
|
43
42
|
artifactState,
|
|
@@ -54,7 +53,6 @@ async function startTimespanGather(options) {
|
|
|
54
53
|
return {
|
|
55
54
|
async endTimespanGather() {
|
|
56
55
|
const finalUrl = await driver.url();
|
|
57
|
-
phaseOptions.url = finalUrl;
|
|
58
56
|
|
|
59
57
|
const runnerOptions = {config, computedCache};
|
|
60
58
|
const artifacts = await Runner.gather(
|
|
@@ -97,7 +97,8 @@ class ExecutionContext {
|
|
|
97
97
|
.catch(${pageFunctions.wrapRuntimeEvalErrorInBrowserString})
|
|
98
98
|
.then(resolve);
|
|
99
99
|
});
|
|
100
|
-
}())
|
|
100
|
+
}())
|
|
101
|
+
//# sourceURL=_lighthouse-eval.js`,
|
|
101
102
|
includeCommandLineAPI: true,
|
|
102
103
|
awaitPromise: true,
|
|
103
104
|
returnByValue: true,
|
|
@@ -195,7 +196,8 @@ class ExecutionContext {
|
|
|
195
196
|
${ExecutionContext._cachedNativesPreamble};
|
|
196
197
|
${depsSerialized};
|
|
197
198
|
(${mainFn})(${argsSerialized});
|
|
198
|
-
})()
|
|
199
|
+
})()
|
|
200
|
+
//# sourceURL=_lighthouse-eval.js`;
|
|
199
201
|
|
|
200
202
|
await this._session.sendCommand('Page.addScriptToEvaluateOnNewDocument', {source: expression});
|
|
201
203
|
}
|
|
@@ -50,12 +50,13 @@ class JsUsage extends FRGatherer {
|
|
|
50
50
|
const usageByScriptId = {};
|
|
51
51
|
|
|
52
52
|
for (const scriptUsage of this._scriptUsages) {
|
|
53
|
-
// If `url` is blank, that means the script was
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
// If `url` is blank, that means the script was dynamically
|
|
54
|
+
// created (eval, new Function, onload, ...)
|
|
55
|
+
if (scriptUsage.url === '' || scriptUsage.url === '_lighthouse-eval.js') {
|
|
56
|
+
// We currently don't consider coverage of dynamic scripts, and we definitely don't want
|
|
57
|
+
// coverage of code Lighthouse ran to inspect the page, so we ignore this ScriptCoverage.
|
|
58
|
+
// Audits would work the same without this, it is only an optimization (not tracking coverage
|
|
59
|
+
// for scripts we don't care about).
|
|
59
60
|
continue;
|
|
60
61
|
}
|
|
61
62
|
|
|
@@ -28,6 +28,23 @@ async function runInSeriesOrParallel(values, promiseMapper, runInSeries) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Returns true if the script was created via our own calls
|
|
33
|
+
* to Runtime.evaluate.
|
|
34
|
+
* @param {LH.Crdp.Debugger.ScriptParsedEvent} script
|
|
35
|
+
*/
|
|
36
|
+
function isLighthouseRuntimeEvaluateScript(script) {
|
|
37
|
+
// Scripts created by Runtime.evaluate that run on the main session/frame
|
|
38
|
+
// result in an empty string for the embedderName.
|
|
39
|
+
// Or, it means the script was dynamically created (eval, new Function, onload, ...)
|
|
40
|
+
if (!script.embedderName) return true;
|
|
41
|
+
|
|
42
|
+
// Otherwise, when running our own code inside other frames, the embedderName
|
|
43
|
+
// is set to the frame's url. In that case, we rely on the special sourceURL that
|
|
44
|
+
// we set.
|
|
45
|
+
return script.hasSourceURL && script.url === '_lighthouse-eval.js';
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
/**
|
|
32
49
|
* @fileoverview Gets JavaScript file contents.
|
|
33
50
|
*/
|
|
@@ -68,8 +85,9 @@ class Scripts extends FRGatherer {
|
|
|
68
85
|
// it also blocks scripts from the same origin but that happen to run in a different process,
|
|
69
86
|
// like a worker.
|
|
70
87
|
if (event.method === 'Debugger.scriptParsed' && !sessionId) {
|
|
71
|
-
|
|
72
|
-
|
|
88
|
+
if (!isLighthouseRuntimeEvaluateScript(event.params)) {
|
|
89
|
+
this._scriptParsedEvents.push(event.params);
|
|
90
|
+
}
|
|
73
91
|
}
|
|
74
92
|
}
|
|
75
93
|
|
|
@@ -46,7 +46,8 @@ class RobotsTxt extends FRGatherer {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const
|
|
49
|
+
const {finalUrl} = passContext.baseArtifacts.URL;
|
|
50
|
+
const robotsUrl = new URL('/robots.txt', finalUrl).href;
|
|
50
51
|
await passContext.driver.fetcher.enable();
|
|
51
52
|
return passContext.driver.fetcher.fetchResource(robotsUrl)
|
|
52
53
|
.catch(err => ({status: null, content: null, errorMessage: err.message}));
|
|
@@ -94,8 +94,8 @@ class WebAppManifest extends FRGatherer {
|
|
|
94
94
|
*/
|
|
95
95
|
getArtifact(context) {
|
|
96
96
|
const driver = context.driver;
|
|
97
|
-
|
|
98
|
-
return WebAppManifest.getWebAppManifest(driver.defaultSession,
|
|
97
|
+
const {finalUrl} = context.baseArtifacts.URL;
|
|
98
|
+
return WebAppManifest.getWebAppManifest(driver.defaultSession, finalUrl);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const INITIAL_CWD = 14 * 1024;
|
|
9
|
-
const NetworkRequest = require('../../network-request.js');
|
|
10
9
|
const URL = require('../../url-shim.js');
|
|
11
10
|
|
|
12
11
|
// Assume that 40% of TTFB was server response time by default for static assets
|
|
@@ -434,40 +433,20 @@ class NetworkAnalyzer {
|
|
|
434
433
|
|
|
435
434
|
/**
|
|
436
435
|
* @param {Array<LH.Artifacts.NetworkRequest>} records
|
|
437
|
-
* @param {string}
|
|
438
|
-
* @return {LH.Artifacts.NetworkRequest}
|
|
439
|
-
*/
|
|
440
|
-
static findMainDocument(records, finalURL) {
|
|
441
|
-
const mainDocument = NetworkAnalyzer.findOptionalMainDocument(records, finalURL);
|
|
442
|
-
if (!mainDocument) throw new Error('Unable to identify the main resource');
|
|
443
|
-
return mainDocument;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} records
|
|
448
|
-
* @param {string} [finalURL]
|
|
436
|
+
* @param {string} resourceUrl
|
|
449
437
|
* @return {LH.Artifacts.NetworkRequest|undefined}
|
|
450
438
|
*/
|
|
451
|
-
static
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
if (mainResource) return mainResource;
|
|
458
|
-
// TODO: beacon !mainResource to Sentry, https://github.com/GoogleChrome/lighthouse/issues/7041
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const documentRequests = records.filter(record => record.resourceType ===
|
|
462
|
-
NetworkRequest.TYPES.Document);
|
|
463
|
-
if (!documentRequests.length) return undefined;
|
|
464
|
-
// The main document is the earliest document request, using position in networkRecords array to break ties.
|
|
465
|
-
return documentRequests.reduce((min, r) => (r.startTime < min.startTime ? r : min));
|
|
439
|
+
static findResourceForUrl(records, resourceUrl) {
|
|
440
|
+
// equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first
|
|
441
|
+
return records.find(request =>
|
|
442
|
+
resourceUrl.startsWith(request.url) &&
|
|
443
|
+
URL.equalWithExcludedFragments(request.url, resourceUrl)
|
|
444
|
+
);
|
|
466
445
|
}
|
|
467
446
|
|
|
468
447
|
/**
|
|
469
448
|
* Resolves redirect chain given a main document.
|
|
470
|
-
* See: {@link NetworkAnalyzer.
|
|
449
|
+
* See: {@link NetworkAnalyzer.findResourceForUrl}) for how to retrieve main document.
|
|
471
450
|
*
|
|
472
451
|
* @param {LH.Artifacts.NetworkRequest} request
|
|
473
452
|
* @return {LH.Artifacts.NetworkRequest}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const LHError = require('./lh-error.js');
|
|
9
9
|
const NetworkAnalyzer = require('./dependency-graph/simulator/network-analyzer.js');
|
|
10
|
+
const NetworkRequest = require('./network-request.js');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Returns an error if the original network request failed or wasn't found.
|
|
@@ -100,10 +101,18 @@ function getNonHtmlError(finalRecord) {
|
|
|
100
101
|
function getPageLoadError(navigationError, context) {
|
|
101
102
|
const {url, loadFailureMode, networkRecords} = context;
|
|
102
103
|
/** @type {LH.Artifacts.NetworkRequest|undefined} */
|
|
103
|
-
let mainRecord;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
let mainRecord = NetworkAnalyzer.findResourceForUrl(networkRecords, url);
|
|
105
|
+
|
|
106
|
+
// If the url doesn't give us a network request, it's possible we landed on a chrome-error:// page
|
|
107
|
+
// In this case, just get the first document request.
|
|
108
|
+
if (!mainRecord) {
|
|
109
|
+
const documentRequests = networkRecords.filter(record =>
|
|
110
|
+
record.resourceType === NetworkRequest.TYPES.Document
|
|
111
|
+
);
|
|
112
|
+
if (documentRequests.length) {
|
|
113
|
+
mainRecord = documentRequests.reduce((min, r) => (r.startTime < min.startTime ? r : min));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
107
116
|
|
|
108
117
|
// MIME Type is only set on the final redirected document request. Use this for the HTML check instead of root.
|
|
109
118
|
let finalRecord;
|
|
@@ -318,24 +318,26 @@ class Util {
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
const MAX_LENGTH = 64;
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
321
|
+
if (parsedUrl.protocol !== 'data:') {
|
|
322
|
+
// Always elide hexadecimal hash
|
|
323
|
+
name = name.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, `$1${ELLIPSIS}`);
|
|
324
|
+
// Also elide other hash-like mixed-case strings
|
|
325
|
+
name = name.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,
|
|
326
|
+
`$1${ELLIPSIS}`);
|
|
327
|
+
// Also elide long number sequences
|
|
328
|
+
name = name.replace(/(\d{3})\d{6,}/g, `$1${ELLIPSIS}`);
|
|
329
|
+
// Merge any adjacent ellipses
|
|
330
|
+
name = name.replace(/\u2026+/g, ELLIPSIS);
|
|
331
|
+
|
|
332
|
+
// Elide query params first
|
|
333
|
+
if (name.length > MAX_LENGTH && name.includes('?')) {
|
|
334
|
+
// Try to leave the first query parameter intact
|
|
335
|
+
name = name.replace(/\?([^=]*)(=)?.*/, `?$1$2${ELLIPSIS}`);
|
|
336
|
+
|
|
337
|
+
// Remove it all if it's still too long
|
|
338
|
+
if (name.length > MAX_LENGTH) {
|
|
339
|
+
name = name.replace(/\?.*/, `?${ELLIPSIS}`);
|
|
340
|
+
}
|
|
339
341
|
}
|
|
340
342
|
}
|
|
341
343
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "9.5.0-dev.
|
|
3
|
+
"version": "9.5.0-dev.20220401",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"compile-proto": "protoc --python_out=./ ./proto/lighthouse-result.proto && mv ./proto/*_pb2.py ./proto/scripts || (echo \"❌ Install protobuf ≥ 3.7.1 to compile the proto file.\" && false)",
|
|
90
90
|
"build-proto-roundtrip": "mkdir -p .tmp && cross-env PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2 python proto/scripts/json_roundtrip_via_proto.py",
|
|
91
91
|
"static-server": "node lighthouse-cli/test/fixtures/static-server.js",
|
|
92
|
-
"serve-dist": "cd dist &&
|
|
93
|
-
"serve-gh-pages": "cd dist/gh-pages &&
|
|
92
|
+
"serve-dist": "cd dist && python3 -m http.server",
|
|
93
|
+
"serve-gh-pages": "cd dist/gh-pages && python3 -m http.server",
|
|
94
94
|
"serve-treemap": "yarn serve-gh-pages",
|
|
95
95
|
"serve-viewer": "yarn serve-gh-pages",
|
|
96
96
|
"flow-report": "yarn build-report --flow && node ./lighthouse-core/scripts/build-test-flow-report.js"
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"robots-parser": "^3.0.0",
|
|
204
204
|
"semver": "^5.3.0",
|
|
205
205
|
"speedline-core": "^1.4.3",
|
|
206
|
-
"third-party-web": "^0.
|
|
206
|
+
"third-party-web": "^0.15.0",
|
|
207
207
|
"ws": "^7.0.0",
|
|
208
208
|
"yargs": "^17.3.1",
|
|
209
209
|
"yargs-parser": "^21.0.0"
|
package/report/renderer/util.js
CHANGED
|
@@ -315,24 +315,26 @@ class Util {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
const MAX_LENGTH = 64;
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
318
|
+
if (parsedUrl.protocol !== 'data:') {
|
|
319
|
+
// Always elide hexadecimal hash
|
|
320
|
+
name = name.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, `$1${ELLIPSIS}`);
|
|
321
|
+
// Also elide other hash-like mixed-case strings
|
|
322
|
+
name = name.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,
|
|
323
|
+
`$1${ELLIPSIS}`);
|
|
324
|
+
// Also elide long number sequences
|
|
325
|
+
name = name.replace(/(\d{3})\d{6,}/g, `$1${ELLIPSIS}`);
|
|
326
|
+
// Merge any adjacent ellipses
|
|
327
|
+
name = name.replace(/\u2026+/g, ELLIPSIS);
|
|
328
|
+
|
|
329
|
+
// Elide query params first
|
|
330
|
+
if (name.length > MAX_LENGTH && name.includes('?')) {
|
|
331
|
+
// Try to leave the first query parameter intact
|
|
332
|
+
name = name.replace(/\?([^=]*)(=)?.*/, `?$1$2${ELLIPSIS}`);
|
|
333
|
+
|
|
334
|
+
// Remove it all if it's still too long
|
|
335
|
+
if (name.length > MAX_LENGTH) {
|
|
336
|
+
name = name.replace(/\?.*/, `?${ELLIPSIS}`);
|
|
337
|
+
}
|
|
336
338
|
}
|
|
337
339
|
}
|
|
338
340
|
|
|
@@ -1272,7 +1272,7 @@
|
|
|
1272
1272
|
"message": "Fonts with `font-display: optional` are preloaded"
|
|
1273
1273
|
},
|
|
1274
1274
|
"lighthouse-core/audits/preload-lcp-image.js | description": {
|
|
1275
|
-
"message": "
|
|
1275
|
+
"message": "If the LCP element is dynamically added to the page, you should preload the image in order to improve LCP. [Learn more](https://web.dev/optimize-lcp/#preload-important-resources)."
|
|
1276
1276
|
},
|
|
1277
1277
|
"lighthouse-core/audits/preload-lcp-image.js | title": {
|
|
1278
1278
|
"message": "Preload Largest Contentful Paint image"
|
|
@@ -1272,7 +1272,7 @@
|
|
|
1272
1272
|
"message": "F̂ón̂t́ŝ ẃît́ĥ `font-display: optional` ár̂é p̂ŕêĺôád̂éd̂"
|
|
1273
1273
|
},
|
|
1274
1274
|
"lighthouse-core/audits/preload-lcp-image.js | description": {
|
|
1275
|
-
"message": "
|
|
1275
|
+
"message": "Îf́ t̂h́ê ĹĈṔ êĺêḿêńt̂ íŝ d́ŷńâḿîćâĺl̂ý âd́d̂éd̂ t́ô t́ĥé p̂áĝé, ŷóû śĥóûĺd̂ ṕr̂él̂óâd́ t̂h́ê ím̂áĝé îń ôŕd̂ér̂ t́ô ím̂ṕr̂óv̂é L̂ĆP̂. [Ĺêár̂ń m̂ór̂é](https://web.dev/optimize-lcp/#preload-important-resources)."
|
|
1276
1276
|
},
|
|
1277
1277
|
"lighthouse-core/audits/preload-lcp-image.js | title": {
|
|
1278
1278
|
"message": "P̂ŕêĺôád̂ Ĺâŕĝéŝt́ Ĉón̂t́êńt̂f́ûĺ P̂áîńt̂ ím̂áĝé"
|
package/types/artifacts.d.ts
CHANGED
|
@@ -641,6 +641,7 @@ declare module Artifacts {
|
|
|
641
641
|
settings: Immutable<Config.Settings>;
|
|
642
642
|
gatherContext: Artifacts['GatherContext'];
|
|
643
643
|
simulator?: InstanceType<typeof LanternSimulator>;
|
|
644
|
+
URL: Artifacts['URL'];
|
|
644
645
|
}
|
|
645
646
|
|
|
646
647
|
interface MetricComputationData extends MetricComputationDataInput {
|
package/types/gatherer.d.ts
CHANGED
|
@@ -47,8 +47,6 @@ declare module Gatherer {
|
|
|
47
47
|
|
|
48
48
|
/** The limited context interface shared between pre and post Fraggle Rock Lighthouse. */
|
|
49
49
|
interface FRTransitionalContext<TDependencies extends DependencyKey = DefaultDependenciesKey> {
|
|
50
|
-
/** The URL of the page that is currently active. Might be `about:blank` in the before phases */
|
|
51
|
-
url: string;
|
|
52
50
|
/** The gather mode Lighthouse is currently in. */
|
|
53
51
|
gatherMode: GatherMode;
|
|
54
52
|
/** The connection to the page being analyzed. */
|