lighthouse 9.5.0-dev.20220314 → 9.5.0-dev.20220317
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/lighthouse-cli/test/smokehouse/core-tests.js +2 -0
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +1 -0
- package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +11 -13
- package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +12 -16
- package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +10 -10
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +11 -6
- package/lighthouse-core/audits/deprecations.js +3 -3
- package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +1 -1
- package/lighthouse-core/audits/dobetterweb/no-document-write.js +1 -1
- package/lighthouse-core/audits/dobetterweb/notification-on-start.js +1 -1
- package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +1 -1
- package/lighthouse-core/audits/errors-in-console.js +2 -2
- package/lighthouse-core/audits/no-unload-listeners.js +7 -15
- package/lighthouse-core/audits/script-elements-test-audit.js +30 -0
- package/lighthouse-core/audits/script-treemap-data.js +22 -21
- package/lighthouse-core/audits/valid-source-maps.js +12 -14
- package/lighthouse-core/audits/violation-audit.js +1 -1
- package/lighthouse-core/computed/js-bundles.js +11 -11
- package/lighthouse-core/computed/module-duplication.js +6 -5
- package/lighthouse-core/computed/unused-javascript-summary.js +16 -36
- package/lighthouse-core/config/default-config.js +1 -0
- package/lighthouse-core/fraggle-rock/config/default-config.js +3 -0
- package/lighthouse-core/gather/gatherers/console-messages.js +4 -2
- package/lighthouse-core/gather/gatherers/js-usage.js +6 -52
- package/lighthouse-core/gather/gatherers/script-elements.js +7 -56
- package/lighthouse-core/gather/gatherers/scripts.js +136 -0
- package/lighthouse-core/gather/gatherers/source-maps.js +3 -0
- package/lighthouse-core/lib/script-helpers.js +22 -0
- package/lighthouse-core/lib/url-shim.js +2 -2
- package/package.json +3 -2
- package/types/artifacts.d.ts +22 -7
|
@@ -52,6 +52,7 @@ import pwaSvgomg from './test-definitions/pwa-svgomg.js';
|
|
|
52
52
|
import redirectsClientPaintServer from './test-definitions/redirects-client-paint-server.js';
|
|
53
53
|
import redirectsHistoryPushState from './test-definitions/redirects-history-push-state.js';
|
|
54
54
|
import redirectsMultipleServer from './test-definitions/redirects-multiple-server.js';
|
|
55
|
+
import redirectScripts from './test-definitions/redirects-scripts.js';
|
|
55
56
|
import redirectsSingleClient from './test-definitions/redirects-single-client.js';
|
|
56
57
|
import redirectsSingleServer from './test-definitions/redirects-single-server.js';
|
|
57
58
|
import redirectsSelf from './test-definitions/redirects-self.js';
|
|
@@ -112,6 +113,7 @@ const smokeTests = [
|
|
|
112
113
|
redirectsClientPaintServer,
|
|
113
114
|
redirectsHistoryPushState,
|
|
114
115
|
redirectsMultipleServer,
|
|
116
|
+
redirectScripts,
|
|
115
117
|
redirectsSingleClient,
|
|
116
118
|
redirectsSingleServer,
|
|
117
119
|
redirectsSelf,
|
|
@@ -158,6 +158,7 @@ class UnusedBytes extends Audit {
|
|
|
158
158
|
|
|
159
159
|
const simulationBeforeChanges = simulator.simulate(graph, {label: beforeLabel});
|
|
160
160
|
|
|
161
|
+
// TODO: change this to wastedBytesByScriptId
|
|
161
162
|
const wastedBytesByUrl = options.providedWastedBytesByUrl || new Map();
|
|
162
163
|
if (!options.providedWastedBytesByUrl) {
|
|
163
164
|
for (const {url, wastedBytes} of results) {
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
const ByteEfficiencyAudit = require('./byte-efficiency-audit.js');
|
|
13
13
|
const ModuleDuplication = require('../../computed/module-duplication.js');
|
|
14
|
-
const NetworkAnalyzer = require('../../lib/dependency-graph/simulator/network-analyzer.js');
|
|
15
14
|
const i18n = require('../../lib/i18n/i18n.js');
|
|
15
|
+
const {getRequestForScript} = require('../../lib/script-helpers.js');
|
|
16
16
|
|
|
17
17
|
const UIStrings = {
|
|
18
18
|
/** Imperative title of a Lighthouse audit that tells the user to remove duplicate JavaScript from their code. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -48,7 +48,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
48
48
|
title: str_(UIStrings.title),
|
|
49
49
|
description: str_(UIStrings.description),
|
|
50
50
|
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
|
|
51
|
-
requiredArtifacts: ['devtoolsLogs', 'traces', 'SourceMaps', '
|
|
51
|
+
requiredArtifacts: ['devtoolsLogs', 'traces', 'SourceMaps', 'Scripts',
|
|
52
52
|
'GatherContext', 'URL'],
|
|
53
53
|
};
|
|
54
54
|
}
|
|
@@ -85,10 +85,10 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
85
85
|
|
|
86
86
|
const normalizedSource = 'node_modules/' + DuplicatedJavascript._getNodeModuleName(source);
|
|
87
87
|
const aggregatedSourceDatas = groupedDuplication.get(normalizedSource) || [];
|
|
88
|
-
for (const {scriptUrl, resourceSize} of sourceDatas) {
|
|
89
|
-
let sourceData = aggregatedSourceDatas.find(d => d.
|
|
88
|
+
for (const {scriptId, scriptUrl, resourceSize} of sourceDatas) {
|
|
89
|
+
let sourceData = aggregatedSourceDatas.find(d => d.scriptId === scriptId);
|
|
90
90
|
if (!sourceData) {
|
|
91
|
-
sourceData = {scriptUrl, resourceSize: 0};
|
|
91
|
+
sourceData = {scriptId, scriptUrl, resourceSize: 0};
|
|
92
92
|
aggregatedSourceDatas.push(sourceData);
|
|
93
93
|
}
|
|
94
94
|
sourceData.resourceSize += resourceSize;
|
|
@@ -130,7 +130,6 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
130
130
|
context.options?.ignoreThresholdInBytes || IGNORE_THRESHOLD_IN_BYTES;
|
|
131
131
|
const duplication =
|
|
132
132
|
await DuplicatedJavascript._getDuplicationGroupedByNodeModules(artifacts, context);
|
|
133
|
-
const mainDocumentRecord = NetworkAnalyzer.findOptionalMainDocument(networkRecords);
|
|
134
133
|
|
|
135
134
|
/** @type {Map<string, number>} */
|
|
136
135
|
const transferRatioByUrl = new Map();
|
|
@@ -157,22 +156,21 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
157
156
|
let wastedBytesTotal = 0;
|
|
158
157
|
for (let i = 0; i < sourceDatas.length; i++) {
|
|
159
158
|
const sourceData = sourceDatas[i];
|
|
160
|
-
const
|
|
159
|
+
const scriptId = sourceData.scriptId;
|
|
160
|
+
const script = artifacts.Scripts.find(script => script.scriptId === scriptId);
|
|
161
|
+
const url = script?.url || '';
|
|
161
162
|
|
|
162
163
|
/** @type {number|undefined} */
|
|
163
164
|
let transferRatio = transferRatioByUrl.get(url);
|
|
164
165
|
if (transferRatio === undefined) {
|
|
165
|
-
const networkRecord =
|
|
166
|
-
mainDocumentRecord :
|
|
167
|
-
networkRecords.find(n => n.url === url);
|
|
166
|
+
const networkRecord = getRequestForScript(networkRecords, script);
|
|
168
167
|
|
|
169
|
-
|
|
170
|
-
if (!script || script.content === null) {
|
|
168
|
+
if (!script || script.length === undefined) {
|
|
171
169
|
// This should never happen because we found the wasted bytes from bundles, which required contents in a ScriptElement.
|
|
172
170
|
continue;
|
|
173
171
|
}
|
|
174
172
|
|
|
175
|
-
const contentLength = script.
|
|
173
|
+
const contentLength = script.length;
|
|
176
174
|
transferRatio = DuplicatedJavascript._estimateTransferRatio(networkRecord, contentLength);
|
|
177
175
|
transferRatioByUrl.set(url, transferRatio);
|
|
178
176
|
}
|
|
@@ -22,7 +22,7 @@ const ByteEfficiencyAudit = require('./byte-efficiency-audit.js');
|
|
|
22
22
|
const JsBundles = require('../../computed/js-bundles.js');
|
|
23
23
|
const i18n = require('../../lib/i18n/i18n.js');
|
|
24
24
|
const thirdPartyWeb = require('../../lib/third-party-web.js');
|
|
25
|
-
const
|
|
25
|
+
const {getRequestForScript} = require('../../lib/script-helpers.js');
|
|
26
26
|
|
|
27
27
|
const UIStrings = {
|
|
28
28
|
/** Title of a Lighthouse audit that tells the user about legacy polyfills and transforms used on the page. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -109,7 +109,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
109
109
|
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
|
|
110
110
|
description: str_(UIStrings.description),
|
|
111
111
|
title: str_(UIStrings.title),
|
|
112
|
-
requiredArtifacts: ['devtoolsLogs', 'traces', '
|
|
112
|
+
requiredArtifacts: ['devtoolsLogs', 'traces', 'Scripts', 'SourceMaps',
|
|
113
113
|
'GatherContext', 'URL'],
|
|
114
114
|
};
|
|
115
115
|
}
|
|
@@ -274,7 +274,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
274
274
|
* Returns a collection of match results grouped by script url.
|
|
275
275
|
*
|
|
276
276
|
* @param {CodePatternMatcher} matcher
|
|
277
|
-
* @param {LH.
|
|
277
|
+
* @param {LH.Artifacts['Scripts']} scripts
|
|
278
278
|
* @param {LH.Artifacts.NetworkRequest[]} networkRecords
|
|
279
279
|
* @param {LH.Artifacts.Bundle[]} bundles
|
|
280
280
|
* @return {Map<string, PatternMatchResult[]>}
|
|
@@ -284,16 +284,14 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
284
284
|
const urlToMatchResults = new Map();
|
|
285
285
|
const polyfillData = this.getPolyfillData();
|
|
286
286
|
|
|
287
|
-
for (const {
|
|
287
|
+
for (const {scriptId, url, content} of Object.values(scripts)) {
|
|
288
288
|
if (!content) continue;
|
|
289
|
-
const networkRecord = networkRecords.find(record => record.requestId === requestId);
|
|
290
|
-
if (!networkRecord) continue;
|
|
291
289
|
|
|
292
290
|
// Start with pattern matching against the downloaded script.
|
|
293
291
|
const matches = matcher.match(content);
|
|
294
292
|
|
|
295
293
|
// If it's a bundle with source maps, add in the polyfill modules by name too.
|
|
296
|
-
const bundle = bundles.find(b => b.script.
|
|
294
|
+
const bundle = bundles.find(b => b.script.scriptId === scriptId);
|
|
297
295
|
if (bundle) {
|
|
298
296
|
for (const {coreJs2Module, coreJs3Module, name} of polyfillData) {
|
|
299
297
|
// Skip if the pattern matching found a match for this polyfill.
|
|
@@ -313,7 +311,8 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
313
311
|
}
|
|
314
312
|
|
|
315
313
|
if (!matches.length) continue;
|
|
316
|
-
|
|
314
|
+
// TODO: scriptId
|
|
315
|
+
urlToMatchResults.set(url, matches);
|
|
317
316
|
}
|
|
318
317
|
|
|
319
318
|
return urlToMatchResults;
|
|
@@ -373,17 +372,14 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
373
372
|
let transferRatio = transferRatioByUrl.get(url);
|
|
374
373
|
if (transferRatio !== undefined) return transferRatio;
|
|
375
374
|
|
|
376
|
-
const
|
|
377
|
-
const networkRecord =
|
|
378
|
-
mainDocumentRecord :
|
|
379
|
-
networkRecords.find(n => n.url === url);
|
|
380
|
-
const script = artifacts.ScriptElements.find(script => script.src === url);
|
|
375
|
+
const script = artifacts.Scripts.find(script => script.url === url);
|
|
376
|
+
const networkRecord = getRequestForScript(networkRecords, script);
|
|
381
377
|
|
|
382
378
|
if (!script || script.content === null) {
|
|
383
379
|
// Can't find content, so just use 1.
|
|
384
380
|
transferRatio = 1;
|
|
385
381
|
} else {
|
|
386
|
-
const contentLength = script.
|
|
382
|
+
const contentLength = script.length || 0;
|
|
387
383
|
const transferSize =
|
|
388
384
|
ByteEfficiencyAudit.estimateTransferSize(networkRecord, contentLength, 'Script');
|
|
389
385
|
transferRatio = transferSize / contentLength;
|
|
@@ -415,7 +411,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
415
411
|
const transferRatioByUrl = new Map();
|
|
416
412
|
|
|
417
413
|
const urlToMatchResults =
|
|
418
|
-
this.detectAcrossScripts(matcher, artifacts.
|
|
414
|
+
this.detectAcrossScripts(matcher, artifacts.Scripts, networkRecords, bundles);
|
|
419
415
|
for (const [url, matches] of urlToMatchResults.entries()) {
|
|
420
416
|
const transferRatio = await this.estimateTransferRatioForScript(
|
|
421
417
|
transferRatioByUrl, url, artifacts, networkRecords);
|
|
@@ -432,7 +428,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
432
428
|
totalBytes: 0,
|
|
433
429
|
};
|
|
434
430
|
|
|
435
|
-
const bundle = bundles.find(bundle => bundle.script.
|
|
431
|
+
const bundle = bundles.find(bundle => bundle.script.url === url); // TODO: scriptId
|
|
436
432
|
for (const match of matches) {
|
|
437
433
|
const {name, line, column} = match;
|
|
438
434
|
/** @type {SubItem} */
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const ByteEfficiencyAudit = require('./byte-efficiency-audit.js');
|
|
9
9
|
const i18n = require('../../lib/i18n/i18n.js');
|
|
10
10
|
const computeTokenLength = require('../../lib/minification-estimator.js').computeJSTokenLength;
|
|
11
|
+
const {getRequestForScript} = require('../../lib/script-helpers.js');
|
|
11
12
|
|
|
12
13
|
const UIStrings = {
|
|
13
14
|
/** Imperative title of a Lighthouse audit that tells the user to minify the page’s JS code to reduce file size. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -42,7 +43,7 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
|
|
|
42
43
|
title: str_(UIStrings.title),
|
|
43
44
|
description: str_(UIStrings.description),
|
|
44
45
|
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
|
|
45
|
-
requiredArtifacts: ['
|
|
46
|
+
requiredArtifacts: ['Scripts', 'devtoolsLogs', 'traces', 'GatherContext', 'URL'],
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -78,15 +79,15 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
|
|
|
78
79
|
/** @type {Array<LH.Audit.ByteEfficiencyItem>} */
|
|
79
80
|
const items = [];
|
|
80
81
|
const warnings = [];
|
|
81
|
-
for (const
|
|
82
|
-
if (!content) continue;
|
|
82
|
+
for (const script of artifacts.Scripts) {
|
|
83
|
+
if (!script.content) continue;
|
|
83
84
|
|
|
84
|
-
const networkRecord = networkRecords
|
|
85
|
-
const displayUrl =
|
|
86
|
-
`inline: ${content.
|
|
87
|
-
|
|
85
|
+
const networkRecord = getRequestForScript(networkRecords, script);
|
|
86
|
+
const displayUrl = script.name === artifacts.URL.finalUrl ?
|
|
87
|
+
`inline: ${script.content.substring(0, 40)}...` :
|
|
88
|
+
script.url;
|
|
88
89
|
try {
|
|
89
|
-
const result = UnminifiedJavaScript.computeWaste(content, displayUrl, networkRecord);
|
|
90
|
+
const result = UnminifiedJavaScript.computeWaste(script.content, displayUrl, networkRecord);
|
|
90
91
|
// If the ratio is minimal, the file is likely already minified, so ignore it.
|
|
91
92
|
// If the total number of bytes to be saved is quite small, it's also safe to ignore.
|
|
92
93
|
if (result.wastedPercent < IGNORE_THRESHOLD_IN_PERCENT ||
|
|
@@ -94,8 +95,7 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
|
|
|
94
95
|
!Number.isFinite(result.wastedBytes)) continue;
|
|
95
96
|
items.push(result);
|
|
96
97
|
} catch (err) {
|
|
97
|
-
|
|
98
|
-
warnings.push(`Unable to process script ${url}: ${err.message}`);
|
|
98
|
+
warnings.push(`Unable to process script ${script.url}: ${err.message}`);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -9,6 +9,7 @@ const ByteEfficiencyAudit = require('./byte-efficiency-audit.js');
|
|
|
9
9
|
const UnusedJavaScriptSummary = require('../../computed/unused-javascript-summary.js');
|
|
10
10
|
const JsBundles = require('../../computed/js-bundles.js');
|
|
11
11
|
const i18n = require('../../lib/i18n/i18n.js');
|
|
12
|
+
const {getRequestForScript} = require('../../lib/script-helpers.js');
|
|
12
13
|
|
|
13
14
|
const UIStrings = {
|
|
14
15
|
/** Imperative title of a Lighthouse audit that tells the user to reduce JavaScript that is never evaluated during page load. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -67,7 +68,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
|
|
|
67
68
|
title: str_(UIStrings.title),
|
|
68
69
|
description: str_(UIStrings.description),
|
|
69
70
|
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
|
|
70
|
-
requiredArtifacts: ['JsUsage', '
|
|
71
|
+
requiredArtifacts: ['JsUsage', 'Scripts', 'SourceMaps', 'GatherContext',
|
|
71
72
|
'devtoolsLogs', 'traces'],
|
|
72
73
|
};
|
|
73
74
|
}
|
|
@@ -86,12 +87,16 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
|
|
|
86
87
|
} = context.options || {};
|
|
87
88
|
|
|
88
89
|
const items = [];
|
|
89
|
-
for (const [
|
|
90
|
-
const
|
|
90
|
+
for (const [scriptId, scriptCoverage] of Object.entries(artifacts.JsUsage)) {
|
|
91
|
+
const script = artifacts.Scripts.find(s => s.scriptId === scriptId);
|
|
92
|
+
if (!script) continue; // This should never happen.
|
|
93
|
+
|
|
94
|
+
const networkRecord = getRequestForScript(networkRecords, script);
|
|
91
95
|
if (!networkRecord) continue;
|
|
92
|
-
|
|
96
|
+
|
|
97
|
+
const bundle = bundles.find(b => b.script.scriptId === scriptId);
|
|
93
98
|
const unusedJsSummary =
|
|
94
|
-
await UnusedJavaScriptSummary.request({
|
|
99
|
+
await UnusedJavaScriptSummary.request({scriptId, scriptCoverage, bundle}, context);
|
|
95
100
|
if (unusedJsSummary.wastedBytes === 0 || unusedJsSummary.totalBytes === 0) continue;
|
|
96
101
|
|
|
97
102
|
const transfer = ByteEfficiencyAudit
|
|
@@ -99,7 +104,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
|
|
|
99
104
|
const transferRatio = transfer / unusedJsSummary.totalBytes;
|
|
100
105
|
/** @type {LH.Audit.ByteEfficiencyItem} */
|
|
101
106
|
const item = {
|
|
102
|
-
url:
|
|
107
|
+
url: script.url,
|
|
103
108
|
totalBytes: Math.round(transferRatio * unusedJsSummary.totalBytes),
|
|
104
109
|
wastedBytes: Math.round(transferRatio * unusedJsSummary.wastedBytes),
|
|
105
110
|
wastedPercent: unusedJsSummary.wastedPercent,
|
|
@@ -48,7 +48,7 @@ class Deprecations extends Audit {
|
|
|
48
48
|
title: str_(UIStrings.title),
|
|
49
49
|
failureTitle: str_(UIStrings.failureTitle),
|
|
50
50
|
description: str_(UIStrings.description),
|
|
51
|
-
requiredArtifacts: ['ConsoleMessages', 'InspectorIssues', 'SourceMaps', '
|
|
51
|
+
requiredArtifacts: ['ConsoleMessages', 'InspectorIssues', 'SourceMaps', 'Scripts'],
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -65,8 +65,8 @@ class Deprecations extends Audit {
|
|
|
65
65
|
if (artifacts.InspectorIssues.deprecationIssue.length) {
|
|
66
66
|
deprecations = artifacts.InspectorIssues.deprecationIssue
|
|
67
67
|
.map(deprecation => {
|
|
68
|
-
const {url, lineNumber, columnNumber} = deprecation.sourceCodeLocation;
|
|
69
|
-
const bundle = bundles.find(bundle => bundle.script.
|
|
68
|
+
const {scriptId, url, lineNumber, columnNumber} = deprecation.sourceCodeLocation;
|
|
69
|
+
const bundle = bundles.find(bundle => bundle.script.scriptId === scriptId);
|
|
70
70
|
return {
|
|
71
71
|
value: deprecation.message || '',
|
|
72
72
|
// Protocol.Audits.SourceCodeLocation.columnNumber is 1-indexed, but we use 0-indexed.
|
|
@@ -38,7 +38,7 @@ class GeolocationOnStart extends ViolationAudit {
|
|
|
38
38
|
failureTitle: str_(UIStrings.failureTitle),
|
|
39
39
|
description: str_(UIStrings.description),
|
|
40
40
|
supportedModes: ['navigation'],
|
|
41
|
-
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', '
|
|
41
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -54,7 +54,7 @@ class NoDocWriteAudit extends ViolationAudit {
|
|
|
54
54
|
title: str_(UIStrings.title),
|
|
55
55
|
failureTitle: str_(UIStrings.failureTitle),
|
|
56
56
|
description: str_(UIStrings.description),
|
|
57
|
-
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', '
|
|
57
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -38,7 +38,7 @@ class NotificationOnStart extends ViolationAudit {
|
|
|
38
38
|
failureTitle: str_(UIStrings.failureTitle),
|
|
39
39
|
description: str_(UIStrings.description),
|
|
40
40
|
supportedModes: ['navigation'],
|
|
41
|
-
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', '
|
|
41
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -37,7 +37,7 @@ class PassiveEventsAudit extends ViolationAudit {
|
|
|
37
37
|
title: str_(UIStrings.title),
|
|
38
38
|
failureTitle: str_(UIStrings.failureTitle),
|
|
39
39
|
description: str_(UIStrings.description),
|
|
40
|
-
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', '
|
|
40
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -40,7 +40,7 @@ class ErrorLogs extends Audit {
|
|
|
40
40
|
title: str_(UIStrings.title),
|
|
41
41
|
failureTitle: str_(UIStrings.failureTitle),
|
|
42
42
|
description: str_(UIStrings.description),
|
|
43
|
-
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', '
|
|
43
|
+
requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -87,7 +87,7 @@ class ErrorLogs extends Audit {
|
|
|
87
87
|
const consoleRows = artifacts.ConsoleMessages
|
|
88
88
|
.filter(item => item.level === 'error')
|
|
89
89
|
.map(item => {
|
|
90
|
-
const bundle = bundles.find(bundle => bundle.script.
|
|
90
|
+
const bundle = bundles.find(bundle => bundle.script.scriptId === item.scriptId);
|
|
91
91
|
return {
|
|
92
92
|
source: item.source,
|
|
93
93
|
description: item.text,
|
|
@@ -30,7 +30,7 @@ class NoUnloadListeners extends Audit {
|
|
|
30
30
|
title: str_(UIStrings.title),
|
|
31
31
|
failureTitle: str_(UIStrings.failureTitle),
|
|
32
32
|
description: str_(UIStrings.description),
|
|
33
|
-
requiredArtifacts: ['GlobalListeners', '
|
|
33
|
+
requiredArtifacts: ['GlobalListeners', 'SourceMaps', 'Scripts'],
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -54,33 +54,25 @@ class NoUnloadListeners extends Audit {
|
|
|
54
54
|
{key: 'source', itemType: 'source-location', text: str_(i18n.UIStrings.columnSource)},
|
|
55
55
|
];
|
|
56
56
|
|
|
57
|
-
// Look up scriptId to script URL via the JsUsage artifact.
|
|
58
|
-
/** @type {Map<string, string>} */
|
|
59
|
-
const scriptIdToUrl = new Map();
|
|
60
|
-
for (const [url, usages] of Object.entries(artifacts.JsUsage)) {
|
|
61
|
-
for (const usage of usages) {
|
|
62
|
-
scriptIdToUrl.set(usage.scriptId, url);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
57
|
/** @type {Array<{source: LH.Audit.Details.ItemValue}>} */
|
|
67
58
|
const tableItems = unloadListeners.map(listener => {
|
|
68
|
-
const
|
|
59
|
+
const {lineNumber, columnNumber} = listener;
|
|
60
|
+
const script = artifacts.Scripts.find(s => s.scriptId === listener.scriptId);
|
|
69
61
|
|
|
70
62
|
// If we can't find a url, still show something so the user can manually
|
|
71
63
|
// look for where an `unload` handler is being created.
|
|
72
|
-
if (!
|
|
64
|
+
if (!script) {
|
|
73
65
|
return {
|
|
74
66
|
source: {
|
|
75
67
|
type: 'url',
|
|
76
|
-
value:
|
|
68
|
+
value: `(unknown):${lineNumber}:${columnNumber}`,
|
|
77
69
|
},
|
|
78
70
|
};
|
|
79
71
|
}
|
|
80
72
|
|
|
81
|
-
const bundle = bundles.find(bundle => bundle.script.
|
|
73
|
+
const bundle = bundles.find(bundle => bundle.script.scriptId === script.scriptId);
|
|
82
74
|
return {
|
|
83
|
-
source: Audit.makeSourceLocation(url,
|
|
75
|
+
source: Audit.makeSourceLocation(script.url, lineNumber, columnNumber, bundle),
|
|
84
76
|
};
|
|
85
77
|
});
|
|
86
78
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2022 The Lighthouse Authors. All Rights Reserved.
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
4
|
+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @fileoverview This is a fake audit used exclusively in smoke tests to force inclusion of ScriptElements artifact.
|
|
10
|
+
* It is included here for complex reasons in the way the bundled smoketests work.
|
|
11
|
+
*
|
|
12
|
+
* The smokehouse configs are evaluated first in the node CLI side (which requires an absolute path using LH_ROOT).
|
|
13
|
+
* The smokehouse configs are then *re-evaluated* in the bundled context for execution by Lighthouse (which *cannot* use an absolute path using LH_ROOT).
|
|
14
|
+
*
|
|
15
|
+
* This mismatch in environment demands that the audit path in the config must be context-aware,
|
|
16
|
+
* yet the require-graph for the config is included before even the CLI knows that it will be using
|
|
17
|
+
* a bundled runner. Rather than force a massive smoketest architecture change, we include a harmless,
|
|
18
|
+
* test-only audit in our core list instead.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
meta: {
|
|
23
|
+
id: 'script-elements-test-audit',
|
|
24
|
+
title: 'ScriptElements',
|
|
25
|
+
failureTitle: 'ScriptElements',
|
|
26
|
+
description: 'Audit to force the inclusion of ScriptElements artifact',
|
|
27
|
+
requiredArtifacts: ['ScriptElements'],
|
|
28
|
+
},
|
|
29
|
+
audit: () => ({score: 1}),
|
|
30
|
+
};
|
|
@@ -31,7 +31,7 @@ class ScriptTreemapDataAudit extends Audit {
|
|
|
31
31
|
title: 'Script Treemap Data',
|
|
32
32
|
description: 'Used for treemap app',
|
|
33
33
|
requiredArtifacts:
|
|
34
|
-
['traces', 'devtoolsLogs', 'SourceMaps', '
|
|
34
|
+
['traces', 'devtoolsLogs', 'SourceMaps', 'Scripts', 'JsUsage', 'URL'],
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -167,11 +167,10 @@ class ScriptTreemapDataAudit extends Audit {
|
|
|
167
167
|
const nodes = [];
|
|
168
168
|
|
|
169
169
|
let inlineScriptLength = 0;
|
|
170
|
-
for (const
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
inlineScriptLength += (scriptElement.content || '').length;
|
|
170
|
+
for (const script of artifacts.Scripts) {
|
|
171
|
+
// Combine so that inline scripts show up as a single root node.
|
|
172
|
+
if (script.url === artifacts.URL.finalUrl) {
|
|
173
|
+
inlineScriptLength += (script.content || '').length;
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
176
|
if (inlineScriptLength) {
|
|
@@ -185,25 +184,28 @@ class ScriptTreemapDataAudit extends Audit {
|
|
|
185
184
|
const bundles = await JsBundles.request(artifacts, context);
|
|
186
185
|
const duplicationByPath = await ModuleDuplication.request(artifacts, context);
|
|
187
186
|
|
|
188
|
-
for (const
|
|
189
|
-
if (
|
|
187
|
+
for (const script of artifacts.Scripts) {
|
|
188
|
+
if (script.url === artifacts.URL.finalUrl) continue; // Handled above.
|
|
190
189
|
|
|
191
|
-
const name =
|
|
192
|
-
const bundle = bundles.find(bundle =>
|
|
193
|
-
const
|
|
194
|
-
|
|
190
|
+
const name = script.url;
|
|
191
|
+
const bundle = bundles.find(bundle => script.scriptId === bundle.script.scriptId);
|
|
192
|
+
const scriptCoverage = /** @type {Omit<LH.Crdp.Profiler.ScriptCoverage, "url"> | undefined} */
|
|
193
|
+
(artifacts.JsUsage[script.scriptId]);
|
|
194
|
+
if (!bundle && !scriptCoverage) {
|
|
195
195
|
// No bundle and no coverage information, so simply make a single node
|
|
196
196
|
// detailing how big the script is.
|
|
197
197
|
|
|
198
198
|
nodes.push({
|
|
199
199
|
name,
|
|
200
|
-
resourceBytes:
|
|
200
|
+
resourceBytes: script.length || 0,
|
|
201
201
|
});
|
|
202
202
|
continue;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
const unusedJavascriptSummary =
|
|
206
|
-
|
|
205
|
+
const unusedJavascriptSummary = scriptCoverage ?
|
|
206
|
+
await UnusedJavaScriptSummary.request(
|
|
207
|
+
{scriptId: script.scriptId, scriptCoverage, bundle}, context) :
|
|
208
|
+
undefined;
|
|
207
209
|
|
|
208
210
|
/** @type {LH.Treemap.Node} */
|
|
209
211
|
let node;
|
|
@@ -218,7 +220,7 @@ class ScriptTreemapDataAudit extends Audit {
|
|
|
218
220
|
resourceBytes: bundle.sizes.files[source],
|
|
219
221
|
};
|
|
220
222
|
|
|
221
|
-
if (unusedJavascriptSummary
|
|
223
|
+
if (unusedJavascriptSummary?.sourcesWastedBytes) {
|
|
222
224
|
sourceData.unusedBytes = unusedJavascriptSummary.sourcesWastedBytes[source];
|
|
223
225
|
}
|
|
224
226
|
|
|
@@ -241,20 +243,19 @@ class ScriptTreemapDataAudit extends Audit {
|
|
|
241
243
|
const sourceData = {
|
|
242
244
|
resourceBytes: bundle.sizes.unmappedBytes,
|
|
243
245
|
};
|
|
244
|
-
if (unusedJavascriptSummary
|
|
246
|
+
if (unusedJavascriptSummary?.sourcesWastedBytes) {
|
|
245
247
|
sourceData.unusedBytes = unusedJavascriptSummary.sourcesWastedBytes['(unmapped)'];
|
|
246
248
|
}
|
|
247
249
|
sourcesData['(unmapped)'] = sourceData;
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
node = this.makeScriptNode(
|
|
252
|
+
node = this.makeScriptNode(script.url, bundle.rawMap.sourceRoot || '', sourcesData);
|
|
251
253
|
} else {
|
|
252
254
|
// No valid source map for this script, so we can only produce a single node.
|
|
253
|
-
|
|
254
255
|
node = {
|
|
255
256
|
name,
|
|
256
|
-
resourceBytes: unusedJavascriptSummary.
|
|
257
|
-
unusedBytes: unusedJavascriptSummary
|
|
257
|
+
resourceBytes: unusedJavascriptSummary?.totalBytes ?? script.length ?? 0,
|
|
258
|
+
unusedBytes: unusedJavascriptSummary?.wastedBytes,
|
|
258
259
|
};
|
|
259
260
|
}
|
|
260
261
|
|
|
@@ -44,22 +44,22 @@ class ValidSourceMaps extends Audit {
|
|
|
44
44
|
title: str_(UIStrings.title),
|
|
45
45
|
failureTitle: str_(UIStrings.failureTitle),
|
|
46
46
|
description: str_(UIStrings.description),
|
|
47
|
-
requiredArtifacts: ['
|
|
47
|
+
requiredArtifacts: ['Scripts', 'SourceMaps', 'URL'],
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Returns true if the size of the script exceeds a static threshold.
|
|
53
|
-
* @param {LH.Artifacts.
|
|
53
|
+
* @param {LH.Artifacts.Script} script
|
|
54
54
|
* @param {string} finalURL
|
|
55
55
|
* @return {boolean}
|
|
56
56
|
*/
|
|
57
|
-
static isLargeFirstPartyJS(
|
|
58
|
-
if (
|
|
57
|
+
static isLargeFirstPartyJS(script, finalURL) {
|
|
58
|
+
if (!script.length) return false;
|
|
59
59
|
|
|
60
|
-
const isLargeJS =
|
|
61
|
-
const isFirstPartyJS =
|
|
62
|
-
thirdPartyWeb.isFirstParty(
|
|
60
|
+
const isLargeJS = script.length >= LARGE_JS_BYTE_THRESHOLD;
|
|
61
|
+
const isFirstPartyJS = script.url ?
|
|
62
|
+
thirdPartyWeb.isFirstParty(script.url, thirdPartyWeb.getEntity(finalURL)) : false;
|
|
63
63
|
|
|
64
64
|
return isLargeJS && isFirstPartyJS;
|
|
65
65
|
}
|
|
@@ -75,16 +75,14 @@ class ValidSourceMaps extends Audit {
|
|
|
75
75
|
|
|
76
76
|
let missingMapsForLargeFirstPartyFile = false;
|
|
77
77
|
const results = [];
|
|
78
|
-
for (const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const sourceMap = SourceMaps.find(m => m.scriptUrl === scriptElement.src);
|
|
78
|
+
for (const script of artifacts.Scripts) {
|
|
79
|
+
const sourceMap = SourceMaps.find(m => m.scriptId === script.scriptId);
|
|
82
80
|
const errors = [];
|
|
83
|
-
const isLargeFirstParty = this.isLargeFirstPartyJS(
|
|
81
|
+
const isLargeFirstParty = this.isLargeFirstPartyJS(script, artifacts.URL.finalUrl);
|
|
84
82
|
|
|
85
83
|
if (isLargeFirstParty && (!sourceMap || !sourceMap.map)) {
|
|
86
84
|
missingMapsForLargeFirstPartyFile = true;
|
|
87
|
-
isMissingMapForLargeFirstPartyScriptUrl.add(
|
|
85
|
+
isMissingMapForLargeFirstPartyScriptUrl.add(script.url);
|
|
88
86
|
errors.push({error: str_(UIStrings.missingSourceMapErrorMessage)});
|
|
89
87
|
}
|
|
90
88
|
|
|
@@ -107,7 +105,7 @@ class ValidSourceMaps extends Audit {
|
|
|
107
105
|
|
|
108
106
|
if (sourceMap || errors.length) {
|
|
109
107
|
results.push({
|
|
110
|
-
scriptUrl:
|
|
108
|
+
scriptUrl: script.url,
|
|
111
109
|
sourceMapUrl: sourceMap?.sourceMapUrl,
|
|
112
110
|
subItems: {
|
|
113
111
|
type: /** @type {const} */ ('subitems'),
|
|
@@ -31,7 +31,7 @@ class ViolationAudit extends Audit {
|
|
|
31
31
|
return artifacts.ConsoleMessages
|
|
32
32
|
.filter(entry => entry.url && entry.source === 'violation' && pattern.test(entry.text))
|
|
33
33
|
.map(entry => {
|
|
34
|
-
const bundle = bundles.find(bundle => bundle.script.
|
|
34
|
+
const bundle = bundles.find(bundle => bundle.script.scriptId === entry.scriptId);
|
|
35
35
|
return Audit.makeSourceLocationFromConsoleMessage(entry, bundle);
|
|
36
36
|
})
|
|
37
37
|
.filter(filterUndefined)
|